diff --git a/.circleci/config.yml b/.circleci/config.yml index 59ad88eeaa..44293d2ba3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,17 +9,13 @@ references: ipfs_image: &ipfs_image image: requestnetwork/request-ipfs ganache_image: &ganache_image - image: trufflesuite/ganache-cli:v6.3.0 + image: trufflesuite/ganache-cli:v6.8.2 command: - '-l' - '90000000' - '-m' - 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' -orbs: - gcp-gcr: circleci/gcp-gcr@0.3.0 - gcp: circleci/gcp-cli@1.3.0 - jobs: build: docker: @@ -29,7 +25,7 @@ jobs: - checkout - run: name: 'Yarn install' - command: 'yarn install' + command: 'yarn install --frozen-lockfile' - persist_to_workspace: root: *working_directory paths: . @@ -58,8 +54,8 @@ jobs: name: 'Lint request-client.js' command: 'yarn workspace @requestnetwork/request-client.js run lint ' - run: - name: 'Deploy test contract from ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run deploy' + name: 'Deploy test contracts from smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' - run: name: 'Test request-client.js' command: 'yarn workspace @requestnetwork/request-client.js run test' @@ -138,29 +134,46 @@ jobs: at: *working_directory - run: name: 'Build ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run build:lib' + command: 'yarn workspace @requestnetwork/ethereum-storage run build' - run: name: 'Lint ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run lint:lib' + command: 'yarn workspace @requestnetwork/ethereum-storage run lint' - run: - name: 'Deploy contracs ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run deploy' + name: 'Deploy contracs smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' - run: name: 'Test ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run test:lib' - - run: - name: 'Build smart contracts ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run build:sol' - - run: - name: 'Lint smart contracts ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run lint:sol' - - run: - name: 'Test smart contracts ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run test:sol' + command: 'yarn workspace @requestnetwork/ethereum-storage run test' - persist_to_workspace: root: *working_directory paths: - packages/ethereum-storage/coverage/ + test-smart-contracts: + docker: + - *node_image + - *ganache_image + working_directory: *working_directory + steps: + - attach_workspace: + at: *working_directory + - run: + name: 'Build smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run build:lib' + - run: + name: 'Lint smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run lint:lib' + - run: + name: 'Deploy contracs smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' + - run: + name: 'Build smart contracts smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run build:sol' + - run: + name: 'Lint smart contracts smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run lint:sol' + - run: + name: 'Test smart contracts smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run test' test-request-logic: docker: - *node_image @@ -217,8 +230,8 @@ jobs: name: 'Lint request-node' command: 'yarn workspace @requestnetwork/request-node run lint' - run: - name: 'Deploy test contract from ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run deploy' + name: 'Deploy test contract from smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' - run: name: 'Test request-node' command: 'yarn workspace @requestnetwork/request-node run test' @@ -335,8 +348,8 @@ jobs: name: 'Lint integration-test' command: 'yarn workspace @requestnetwork/integration-test run lint' - run: - name: 'Deploy test contract from ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run deploy' + name: 'Deploy test contract from smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' - run: name: 'Start request-node' command: 'yarn workspace @requestnetwork/request-node run start' @@ -403,6 +416,54 @@ jobs: root: *working_directory paths: - packages/multi-format/coverage/ + test-payment-detection: + docker: + - *node_image + - *ganache_image + working_directory: *working_directory + steps: + - attach_workspace: + at: *working_directory + - run: + name: 'Build payment-detection' + command: 'yarn workspace @requestnetwork/payment-detection run build' + - run: + name: 'Lint payment-detection' + command: 'yarn workspace @requestnetwork/payment-detection run lint' + - run: + name: 'Deploy test contracts from smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' + - run: + name: 'Test payment-detection' + command: 'yarn workspace @requestnetwork/payment-detection run test' + - persist_to_workspace: + root: *working_directory + paths: + - packages/payment-detection/coverage/ + test-payment-processor: + docker: + - *node_image + - *ganache_image + working_directory: *working_directory + steps: + - attach_workspace: + at: *working_directory + - run: + name: 'Build payment-processor' + command: 'yarn workspace @requestnetwork/payment-processor run build' + - run: + name: 'Lint payment-processor' + command: 'yarn workspace @requestnetwork/payment-processor run lint' + - run: + name: 'Deploy test contracts from smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' + - run: + name: 'Test payment-processor' + command: 'yarn workspace @requestnetwork/payment-processor run test' + - persist_to_workspace: + root: *working_directory + paths: + - packages/payment-processor/coverage/ publish-coverage: docker: - *node_image @@ -413,32 +474,6 @@ jobs: - run: name: 'Publish Coverage' command: 'yarn publish-coverage' - push-request-network-image: - executor: gcp/default - steps: - - checkout - - setup_remote_docker - - gcp/install - - gcp/initialize - - run: - name: 'gcloud docker auth' - command: gcloud auth configure-docker --project $GOOGLE_PROJECT_ID --quiet - - gcp-gcr/build-image: - image: request-network - registry-url: eu.gcr.io - tag: ${CIRCLE_SHA1:0:7} - - gcp-gcr/push-image: - image: request-network - registry-url: eu.gcr.io - tag: ${CIRCLE_SHA1:0:7} - - run: - name: Add Tag to image - command: | - export BRANCH_TAG_NAME=$(echo ${CIRCLE_BRANCH} | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]//g') - gcloud container images add-tag \ - eu.gcr.io/${GOOGLE_PROJECT_ID}/request-network:${CIRCLE_SHA1:0:7} \ - eu.gcr.io/${GOOGLE_PROJECT_ID}/request-network:${BRANCH_TAG_NAME} \ - --quiet test-prototype-estimator: docker: - *node_image @@ -450,7 +485,6 @@ jobs: name: 'Build prototype-estimator' command: 'yarn workspace @requestnetwork/prototype-estimator run build' test-nightly: - test-integration-test: docker: - *node_image - *ipfs_image @@ -466,8 +500,8 @@ jobs: name: 'Lint integration-test' command: 'yarn workspace @requestnetwork/integration-test run lint' - run: - name: 'Deploy test contract from ethereum-storage' - command: 'yarn workspace @requestnetwork/ethereum-storage run deploy' + name: 'Deploy test contract from smart-contracts' + command: 'yarn workspace @requestnetwork/smart-contracts run deploy' - run: name: 'Start request-node' command: 'yarn workspace @requestnetwork/request-node run start' @@ -486,11 +520,26 @@ jobs: name: 'Test integration-test' command: 'yarn workspace @requestnetwork/integration-test run test:scheduled' + # Release a next version package everytime we merge to master + next-release: + docker: + - *node_image + working_directory: *working_directory + steps: + - attach_workspace: + at: *working_directory + - run: + name: 'Build all' + command: 'yarn clean && yarn build' + - run: + name: 'Authenticate with registry' + command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc + - run: + name: 'publish' + command: '$(yarn bin)/lerna publish --conventional-commits --conventional-prerelease --exact --canary --no-git-tag-version --no-push --preid next --dist-tag next --yes' + workflows: version: 2 - push-request-network-image: - jobs: - - push-request-network-image build-and-test: jobs: - build @@ -513,6 +562,9 @@ workflows: - test-ethereum-storage: requires: - build + - test-smart-contracts: + requires: + - build - test-request-logic: requires: - build @@ -552,6 +604,12 @@ workflows: - test-prototype-estimator: requires: - build + - test-payment-detection: + requires: + - build + - test-payment-processor: + requires: + - build # Publish the code coverage - publish-coverage: @@ -569,6 +627,35 @@ workflows: - test-request-node - test-utils - test-multi-format + - test-payment-detection + - test-payment-processor + + # Release a next version package everytime we merge to master + - next-release: + requires: + - test-advanced-logic + - test-data-access + - test-data-format + - test-epk-decryption + - test-epk-signature + - test-ethereum-storage + - test-integration-test + - test-smart-contracts + - test-multi-format + - test-request-client + - test-request-logic + - test-request-node + - test-transaction-manager + - test-types + - test-utils + - test-web3-signature + - test-payment-detection + - test-payment-processor + context: protocol-release + filters: + branches: + only: + - master nightly: triggers: @@ -579,7 +666,6 @@ workflows: branches: only: - master - - development jobs: - build - lint-package-json: diff --git a/.dockerignore b/.dockerignore index 2ecb1223e2..acb2190a8e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,8 @@ Dockerfile node_modules -packages/**/node_modules \ No newline at end of file +packages/**/node_modules +.git +.github +.vscode +*.md +.circleci \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index d6ebd857d4..7dd03e9612 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,11 +18,14 @@ "abi", "aes", "aes256cbc", + "aes256gcm", "api", + "arrayish", "arg", "args", "argv", "async", + "auth", "axios", "blockchain", "blockcypher", @@ -34,6 +37,7 @@ "checksum", "cipheriv", "cors", + "const", "cryptocurrency", "cryptocurrencies", "cryptographically", @@ -57,11 +61,15 @@ "ethers", "etherchain", "ethereum", + "etherscan", "ethgasstation", "fau", "fetchable", "filename", + "gcm", + "gnosis", "gwei", + "hexlify", "http", "https", "infura", @@ -79,7 +87,11 @@ "mainnet", "memberof", "metadata", + "metamask", "mul", + "multisig", + "numberify", + "numberish", "namespace", "parallelize", "param", @@ -92,10 +104,12 @@ "satoshi", "semver", "serializable", + "sinon", "src", "stackoverflow", "timestamp", "timestamps", + "timestamped", "testnet", "tslint", "tx", diff --git a/.github/workflows/auto-approve-dependabot.yml b/.github/workflows/auto-approve-dependabot.yml new file mode 100644 index 0000000000..7b04a8e12a --- /dev/null +++ b/.github/workflows/auto-approve-dependabot.yml @@ -0,0 +1,20 @@ +name: Auto approve dependabot +on: pull_request + +jobs: + approve: + runs-on: ubuntu-latest + steps: + - name: "Wait for status checks" + id: waitforstatuschecks + uses: "WyriHaximus/github-action-wait-for-status@0.1.0" + if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' + with: + ignoreActions: approve + checkInterval: 60 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + - uses: hmarr/auto-approve-action@v2.0.0 + if: steps.waitforstatuschecks.outputs.status == 'success' && (github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]') + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/docs-deploy-staging.yml b/.github/workflows/docs-deploy-staging.yml new file mode 100644 index 0000000000..dd171e073a --- /dev/null +++ b/.github/workflows/docs-deploy-staging.yml @@ -0,0 +1,27 @@ +name: Deploy docs to staging + +on: + push: + branches: + - master + +jobs: + build-deploy-staging: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Use Node.js 10.x + uses: actions/setup-node@v1 + with: + node-version: 10.15.3 + - name: yarn install & build + run: | + yarn + yarn build + - uses: benjlevesque/s3-sync-action@master + env: + SOURCE_DIR: './packages/docs/build' + AWS_REGION: ${{ secrets.AWS_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_S3_BUCKET: ${{ secrets. AWS_S3_BUCKET_DOCS_STAGING }} diff --git a/.gitignore b/.gitignore index 82a9098656..ef92ee3c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,12 +11,6 @@ coverage .nyc_output coverage.lcov -# IDE - VSCode -.vscode/* -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - # logs lerna-debug.log yarn-error.log diff --git a/.vscode/launch.json b/.vscode/launch.json index 5ccf563269..64099ab9d1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,7 +23,7 @@ "--no-timeouts", "--require", "ts-node/register", - "${workspaceFolder}/packages/request-client.js/test/*.test.ts", + "${workspaceFolder}/packages/request-client.js/test/index.test.ts", "--colors" ], "cwd": "${workspaceFolder}/packages/request-client.js/", diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..0e7c85fa6e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.files": "**/test/**/*.test.ts", + "mochaExplorer.cwd": "./" +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4d17e368c3..93bc904954 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,15 @@ FROM node:10-alpine WORKDIR /app +RUN apk add --virtual .build-deps git python g++ bash make + +COPY package.json . +COPY yarn.lock . + +RUN yarn + COPY . . +RUN yarn +RUN yarn build -RUN apk add --no-cache --virtual .build-deps git python g++ bash make && \ - yarn && \ - yarn build && \ - apk del .build-deps +RUN apk del .build-deps diff --git a/README.md b/README.md index cf0d67f643..b465c40015 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ --- -[![CircleCI](https://img.shields.io/circleci/project/github/RequestNetwork/requestNetwork/development.svg)](https://circleci.com/gh/RequestNetwork/requestNetwork) -[![Codecov](https://codecov.io/gh/RequestNetwork/requestNetwork/branch/master/graph/badge.svg)](https://codecov.io/gh/RequestNetwork/requestNetwork) +[![CircleCI](https://img.shields.io/circleci/project/github/RequestNetwork/requestNetwork/master.svg)](https://circleci.com/gh/RequestNetwork/requestNetwork) +[![Coverage Status](https://coveralls.io/repos/github/RequestNetwork/requestNetwork/badge.svg?branch=master)](https://coveralls.io/github/RequestNetwork/requestNetwork?branch=master) [![Commit Activity](https://img.shields.io/github.amrom.workers.devmit-activity/m/RequestNetwork/requestNetwork.svg?color=green)](https://github.com/RequestNetwork/requestNetwork/pulse/monthly) -[![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge) [Request][website-url] is a decentralized network built on top of Ethereum, which allows anyone, anywhere to request a payment. A full description of the protocol may be found in our [whitepaper][whitepaper-url]. @@ -19,21 +18,25 @@ Join the [Request Hub][requesthub-slack-url] to get in touch with us. ### Published Packages -| Package | Version | Description | -| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| [`@requestnetwork/advanced-logic`](/packages/advanced-logic) | [![npm](https://img.shields.io/npm/v/@requestnetwork/advanced-logic.svg)](https://www.npmjs.com/package/@requestnetwork/advanced-logic) | Extensions to the protocol | -| [`@requestnetwork/request-client.js`](/packages/request-client.js) | [![npm](https://img.shields.io/npm/v/@requestnetwork/request-client.js.svg)](https://www.npmjs.com/package/@requestnetwork/request-client.js) | Library to use Request nodes as servers | -| [`@requestnetwork/data-access`](/packages/data-access) | [![npm](https://img.shields.io/npm/v/@requestnetwork/data-access.svg)](https://www.npmjs.com/package/@requestnetwork/data-access) | Indexing an batching of transactions | -| [`@requestnetwork/data-format`](/packages/data-format) | [![npm](https://img.shields.io/npm/v/@requestnetwork/data-format.svg)](https://www.npmjs.com/package/@requestnetwork/data-format) | Standards for data stored on Request, like invoices format | -| [`@requestnetwork/epk-signature`](/packages/epk-signature) | [![npm](https://img.shields.io/npm/v/@requestnetwork/epk-signature.svg)](https://www.npmjs.com/package/@requestnetwork/epk-signature) | Sign requests using Ethereum private keys | -| [`@requestnetwork/ethereum-storage`](/packages/ethereum-storage) | [![npm](https://img.shields.io/npm/v/@requestnetwork/ethereum-storage.svg)](https://www.npmjs.com/package/@requestnetwork/ethereum-storage) | Storage of Request data on Ethereum and IPFS | -| [`@requestnetwork/epk-decryption`](/packages/epk-decryption) | [![npm](https://img.shields.io/npm/v/@requestnetwork/epk-decryption.svg)](https://www.npmjs.com/package/@requestnetwork/epk-decryption) | Decrypt encrypted requests using Ethereum private keys | -| [`@requestnetwork/request-logic`](/packages/request-logic) | [![npm](https://img.shields.io/npm/v/@requestnetwork/request-logic.svg)](https://www.npmjs.com/package/@requestnetwork/request-logic) | The Request business logic: properties and actions of requests | -| [`@requestnetwork/request-node`](/packages/request-node) | [![npm](https://img.shields.io/npm/v/@requestnetwork/request-node.svg)](https://www.npmjs.com/package/@requestnetwork/request-node) | Web server that allows easy access to Request system | -| [`@requestnetwork/transaction-manager`](/packages/transaction-manager) | [![npm](https://img.shields.io/npm/v/@requestnetwork/transaction-manager.svg)](https://www.npmjs.com/package/@requestnetwork/transaction-manager) | Creates transactions to be sent to Data Access | -| [`@requestnetwork/types`](/packages/types) | [![npm](https://img.shields.io/npm/v/@requestnetwork/types.svg)](https://www.npmjs.com/package/@requestnetwork/types) | Typescript types shared across @requestnetwork packages | -| [`@requestnetwork/utils`](/packages/utils) | [![npm](https://img.shields.io/npm/v/@requestnetwork/utils.svg)](https://www.npmjs.com/package/@requestnetwork/utils) | Collection of tools shared between the @requestnetwork packages | -| [`@requestnetwork/web3-signature`](/packages/web3-signature) | [![npm](https://img.shields.io/npm/v/@requestnetwork/web3-signature.svg)](https://www.npmjs.com/package/@requestnetwork/web3-signature) | Sign requests using web3 tools (like Metamask) | +| Package | Version | Description | +| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [`@requestnetwork/advanced-logic`](/packages/advanced-logic) | [![npm](https://img.shields.io/npm/v/@requestnetwork/advanced-logic.svg)](https://www.npmjs.com/package/@requestnetwork/advanced-logic) | Extensions to the protocol | +| [`@requestnetwork/request-client.js`](/packages/request-client.js) | [![npm](https://img.shields.io/npm/v/@requestnetwork/request-client.js.svg)](https://www.npmjs.com/package/@requestnetwork/request-client.js) | Library to use Request nodes as servers | +| [`@requestnetwork/data-access`](/packages/data-access) | [![npm](https://img.shields.io/npm/v/@requestnetwork/data-access.svg)](https://www.npmjs.com/package/@requestnetwork/data-access) | Indexing an batching of transactions | +| [`@requestnetwork/data-format`](/packages/data-format) | [![npm](https://img.shields.io/npm/v/@requestnetwork/data-format.svg)](https://www.npmjs.com/package/@requestnetwork/data-format) | Standards for data stored on Request, like invoices format | +| [`@requestnetwork/epk-signature`](/packages/epk-signature) | [![npm](https://img.shields.io/npm/v/@requestnetwork/epk-signature.svg)](https://www.npmjs.com/package/@requestnetwork/epk-signature) | Sign requests using Ethereum private keys | +| [`@requestnetwork/ethereum-storage`](/packages/ethereum-storage) | [![npm](https://img.shields.io/npm/v/@requestnetwork/ethereum-storage.svg)](https://www.npmjs.com/package/@requestnetwork/ethereum-storage) | Storage of Request data on Ethereum and IPFS | +| [`@requestnetwork/epk-decryption`](/packages/epk-decryption) | [![npm](https://img.shields.io/npm/v/@requestnetwork/epk-decryption.svg)](https://www.npmjs.com/package/@requestnetwork/epk-decryption) | Decrypt encrypted requests using Ethereum private keys | +| [`@requestnetwork/payment-detection`](/packages/payment-detection) | [![npm](https://img.shields.io/npm/v/@requestnetwork/payment-detection.svg)](https://www.npmjs.com/package/@requestnetwork/payment-detection) | Client-side payment detection, to compute the balance. | +| [`@requestnetwork/payment-processor`](/packages/payment-processor) | [![npm](https://img.shields.io/npm/v/@requestnetwork/payment-processor.svg)](https://www.npmjs.com/package/@requestnetwork/payment-processor) | Pay a request using a web3 wallet | +| [`@requestnetwork/request-logic`](/packages/request-logic) | [![npm](https://img.shields.io/npm/v/@requestnetwork/request-logic.svg)](https://www.npmjs.com/package/@requestnetwork/request-logic) | The Request business logic: properties and actions of requests | +| [`@requestnetwork/request-node`](/packages/request-node) | [![npm](https://img.shields.io/npm/v/@requestnetwork/request-node.svg)](https://www.npmjs.com/package/@requestnetwork/request-node) | Web server that allows easy access to Request system | +| [`@requestnetwork/transaction-manager`](/packages/transaction-manager) | [![npm](https://img.shields.io/npm/v/@requestnetwork/transaction-manager.svg)](https://www.npmjs.com/package/@requestnetwork/transaction-manager) | Creates transactions to be sent to Data Access, managing encryption | +| [`@requestnetwork/types`](/packages/types) | [![npm](https://img.shields.io/npm/v/@requestnetwork/types.svg)](https://www.npmjs.com/package/@requestnetwork/types) | Typescript types shared across @requestnetwork packages | +| [`@requestnetwork/utils`](/packages/utils) | [![npm](https://img.shields.io/npm/v/@requestnetwork/utils.svg)](https://www.npmjs.com/package/@requestnetwork/utils) | Collection of tools shared between the @requestnetwork packages | +| [`@requestnetwork/web3-signature`](/packages/web3-signature) | [![npm](https://img.shields.io/npm/v/@requestnetwork/web3-signature.svg)](https://www.npmjs.com/package/@requestnetwork/web3-signature) | Sign requests using web3 tools (like Metamask) | +| [`@requestnetwork/multi-format`](/packages/multi-format) | [![npm](https://img.shields.io/npm/v/@requestnetwork/multi-format.svg)](https://www.npmjs.com/package/@requestnetwork/multi-format) | Serialize and deserialize object in the Request Network protocol | +| [`@requestnetwork/smart-contracts`](/packages/smart-contracts) | [![npm](https://img.shields.io/npm/v/@requestnetwork/smart-contracts.svg)](https://www.npmjs.com/package/@requestnetwork/smart-contracts) | Sources and artifacts of the smart contracts | ### Private Packages diff --git a/package.json b/package.json index ec6a308559..6e9f1b7117 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "build": "lerna run build", "clean": "lerna run clean", - "build:tsc": "tsc -b packages/advanced-logic packages/data-access packages/data-format packages/epk-decryption packages/epk-signature packages/ethereum-storage packages/integration-test packages/prototype-estimator packages/request-client.js packages/request-logic packages/request-node packages/toolbox packages/transaction-manager packages/types packages/usage-examples packages/utils packages/web3-signature packages/multi-format", + "build:tsc": "tsc -b packages/advanced-logic packages/data-access packages/data-format packages/epk-decryption packages/epk-signature packages/ethereum-storage packages/integration-test packages/multi-format packages/payment-detection packages/prototype-estimator packages/request-client.js packages/request-logic packages/request-node packages/smart-contracts packages/toolbox packages/transaction-manager packages/types packages/usage-examples packages/utils packages/web3-signature", "lint": "lerna run lint", "lint-staged": "lerna run lint-staged", "lerna": "lerna", @@ -31,8 +31,8 @@ "coveralls": "3.0.3", "husky": "3.0.5", "lerna": "3.16.4", - "npm-package-json-lint": "4.0.3", - "nyc": "14.1.1", + "npm-package-json-lint": "4.5.0", + "nyc": "15.0.0", "remap-istanbul": "0.13.0", "typescript": "3.7.2" } diff --git a/packages/advanced-logic/.vscode/settings.json b/packages/advanced-logic/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/advanced-logic/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/advanced-logic/CHANGELOG.md b/packages/advanced-logic/CHANGELOG.md index c48598bdff..739478fdf7 100644 --- a/packages/advanced-logic/CHANGELOG.md +++ b/packages/advanced-logic/CHANGELOG.md @@ -3,6 +3,362 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.15.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.15.0) (2020-06-29) + + +### Features + +* amount are only number or string ([#223](https://github.com/RequestNetwork/requestNetwork/issues/223)) ([7a35bde](https://github.com/RequestNetwork/requestNetwork/commit/7a35bde63f78b9305819a80e97022fca7e9494d2)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.14.0) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.13.0) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.12.0) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.11.0) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.10.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.9.0) (2020-01-16) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.8.0) (2019-12-18) + + +### Bug Fixes + +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + +# [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.6.0...@requestnetwork/advanced-logic@0.7.0) (2019-12-04) + + +### Features + +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) + + + + + # [0.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/advanced-logic@0.5.0...@requestnetwork/advanced-logic@0.6.0) (2019-11-20) diff --git a/packages/advanced-logic/README.md b/packages/advanced-logic/README.md index 5963182ad3..7fb3f3290a 100644 --- a/packages/advanced-logic/README.md +++ b/packages/advanced-logic/README.md @@ -11,7 +11,7 @@ npm install @requestnetwork/advanced-logic ## Specifications -Specifications of Advanced Logic can be found [here](/packages/advanced-logic/specs/advanced-logic-specs-0.1.0-DRAFT.md) +Specifications of Advanced Logic can be found [here](/packages/advanced-logic/specs/advanced-logic-specs-0.1.0.md) ## Implemented Extensions @@ -19,7 +19,7 @@ Specifications of Advanced Logic can be found [here](/packages/advanced-logic/sp This extension allows linking content data to the request. The content data can be used to give extra information about the request. You can find examples of content data format [here](/packages/data-format). -Specifications of Content Data can be found [here](/packages/advanced-logic/specs/content-data-0.1.0-DRAFT.md) +Specifications of Content Data can be found [here](/packages/advanced-logic/specs/content-data-0.1.0.md) ### Declarative payments @@ -28,7 +28,7 @@ The payments and refunds are documented by the payer and the payee of the reques This extension do not ensure payment detection, only a consensus is made between the payer and the payee. -Specifications of Declarative payments can be found [here](/packages/advanced-logic/specs/payment-network-any-declarative-0.1.0-DRAFT.md) +Specifications of Declarative payments can be found [here](/packages/advanced-logic/specs/payment-network-any-declarative-0.1.0.md) ### Address based bitcoin payments @@ -36,7 +36,7 @@ This extension allows the payments and the refunds to be made on the Bitcoin blo Note: this extension can be used with the bitcoin mainnet and testnet. -Specifications of Address based bitcoin payments can be found [here](/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0-DRAFT.md) +Specifications of Address based bitcoin payments can be found [here](/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0.md) ## Contributing diff --git a/packages/advanced-logic/package.json b/packages/advanced-logic/package.json index 9556b72413..e5122bf2b2 100644 --- a/packages/advanced-logic/package.json +++ b/packages/advanced-logic/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/advanced-logic", - "version": "0.6.0", + "version": "0.15.0", "publishConfig": { "access": "public" }, @@ -32,41 +32,42 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "stryker": "stryker run", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "@types/node": "11.9.0", "lodash": "4.17.13", "wallet-address-validator": "0.2.4" }, "devDependencies": { - "@stryker-mutator/core": "2.1.0", + "@stryker-mutator/core": "2.4.0", "@stryker-mutator/html-reporter": "2.1.0", "@stryker-mutator/mocha-framework": "2.1.0", "@stryker-mutator/mocha-runner": "2.1.0", - "@stryker-mutator/typescript": "2.1.0", + "@stryker-mutator/typescript": "2.3.0", "@types/chai": "4.1.7", "@types/chai-spies": "1.0.0", "@types/lodash": "4.14.120", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" }, diff --git a/packages/advanced-logic/specs/advanced-logic-specs-0.1.0-DRAFT.md b/packages/advanced-logic/specs/advanced-logic-specs-0.1.0.md similarity index 100% rename from packages/advanced-logic/specs/advanced-logic-specs-0.1.0-DRAFT.md rename to packages/advanced-logic/specs/advanced-logic-specs-0.1.0.md diff --git a/packages/advanced-logic/specs/content-data-0.1.0-DRAFT.md b/packages/advanced-logic/specs/content-data-0.1.0.md similarity index 98% rename from packages/advanced-logic/specs/content-data-0.1.0-DRAFT.md rename to packages/advanced-logic/specs/content-data-0.1.0.md index 6ceaa796c1..33914cedeb 100644 --- a/packages/advanced-logic/specs/content-data-0.1.0-DRAFT.md +++ b/packages/advanced-logic/specs/content-data-0.1.0.md @@ -5,7 +5,7 @@ You can be interested in this document if: - you want to create your own implementation of the Request protocol - you are curious enough to dive and see what is under the hood of the Request protocol -Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0-DRAFT.md)) +Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0.md)) ## Description diff --git a/packages/advanced-logic/specs/payment-network-any-declarative-0.1.0-DRAFT.md b/packages/advanced-logic/specs/payment-network-any-declarative-0.1.0.md similarity index 100% rename from packages/advanced-logic/specs/payment-network-any-declarative-0.1.0-DRAFT.md rename to packages/advanced-logic/specs/payment-network-any-declarative-0.1.0.md diff --git a/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0-DRAFT.md b/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0.md similarity index 99% rename from packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0-DRAFT.md rename to packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0.md index ed6b6e7883..7f2b6c6b49 100644 --- a/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0-DRAFT.md +++ b/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0.md @@ -5,7 +5,7 @@ You can be interested in this document if: - you want to create your own implementation of the Request protocol - you are curious enough to dive and see what is under the hood of the Request protocol -Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0-DRAFT.md)). +Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0.md)). ## Description @@ -50,7 +50,7 @@ Note: to use the bitcoin testnet just replace the id by "pn-testnet-bitcoin-addr This action is valid, if: -- The request `currency` must be "BTC" +- The request `currency.type` must be "BTC" #### Warnings diff --git a/packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0-DRAFT.md b/packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0.md similarity index 99% rename from packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0-DRAFT.md rename to packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0.md index b229ab8b7a..7d474fb375 100644 --- a/packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0-DRAFT.md +++ b/packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0.md @@ -5,7 +5,7 @@ You may be interested in this document if: - you want to create your own implementation of the Request protocol - you are curious enough to dive and see what is under the hood of the Request protocol -Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0-DRAFT.md)). +Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0.md)). ## Description diff --git a/packages/advanced-logic/specs/payment-network-erc20-proxy-contract-0.1.0.md b/packages/advanced-logic/specs/payment-network-erc20-proxy-contract-0.1.0.md new file mode 100644 index 0000000000..8b269c2eac --- /dev/null +++ b/packages/advanced-logic/specs/payment-network-erc20-proxy-contract-0.1.0.md @@ -0,0 +1,223 @@ +# Payment Network - ERC20 - proxy contract + +You may be interested in this document if: + +- you want to create your own implementation of the Request protocol +- you are curious enough to dive and see what is under the hood of the Request protocol + +Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0.md)). + +## Description + +This extension allows the payments and the refunds to be made in ERC20 tokens on the Ethereum blockchain. +The payment is made through a proxy contract. This proxy contract does the ERC20 token transfer on behalf of +the user. The contract ensures a link between an ERC20 transfer and a request through a `paymentReference`. +This `paymentReference` consists of the last 8 bytes of a salted hash of the requestId: `last8Bytes(hash(lowercase(requestId + salt + address)))`: + +- `requestId` is the id of the request +- `salt` is a random number with at least 8 bytes of randomness. It must be unique to each request +- `address` is the payment address for payments, the refund address for refunds +- `lowercase()` transforms all characters to lowercase +- `hash()` is a keccak256 hash function +- `last8Bytes()` take the last 8 bytes + +As a payment network, this extension allows to deduce a payment `balance` for the request. (see +[Interpretation](#Interpretation)) + +## Contract + +The contract contains one function called `transferFromWithReference` which takes 4 arguments: + +- `tokenAddress` is the address of the ERC20 contract +- `to` is the destination address for the tokens +- `amount` is the amount of tokens +- `paymentReference` is the reference data used to track the transfer (see `paymentReference`) + +The `TransferWithReference` event is emitted when the tokens are transfered. This event contains the same 4 arguments as the `transferFromWithReference` function. + +[See smart contract source](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/smart-contracts/src/contracts/ERC20Proxy.sol) + +| Network | Contract Address | +|---------|--------------------------------------------| +| Mainnet | 0x5f821c20947ff9be22e823edc5b3c709b33121b3 | +| Rinkeby | 0x162edb802fae75b9ee4288345735008ba51a4ec9 | + +## Properties + +| Property | Type | Description | Requirement | +| ------------------------- | ------ | ---------------------------------------------- | ------------- | +| **id** | String | constant value: "pn-erc20-proxy-contract" | **Mandatory** | +| **type** | String | constant value: "paymentNetwork" | **Mandatory** | +| **version** | String | constant value: "0.1.0" | **Mandatory** | +| **events** | Array | List of the actions performed by the extension | **Mandatory** | +| **values** | Object | | | +| **values.salt** | String | Salt for the request | **Mandatory** | +| **values.paymentAddress** | String | Ethereum address for the payment | Optional | +| **values.refundAddress** | String | Ethereum address for the refund | Optional | + + +Note: to use the Rinkeby testnet, create a request with `currency.network` as `rinkeby`. + +--- + +## Actions + +### Creation + +#### Parameters + +| | Type | Description | Requirement | +| ----------------------------- | ------ | -------------------------------- | ------------- | +| **id** | String | Constant value: "pn-erc20-proxy-contract" | **Mandatory** | +| **type** | String | Constant value: "paymentNetwork" | **Mandatory** | +| **version** | String | Constant value: "0.1.0" | **Mandatory** | +| **parameters** | Object | | | +| **parameters.salt** | String | Salt for the request | **Mandatory** | +| **parameters.paymentAddress** | String | Ethereum address for the payment | Optional | +| **parameters.refundAddress** | String | Ethereum address for the refund | Optional | + + +#### Conditions + +This action is valid if: + +- The `salt` is not empty and long enough (8 bytes of randomness minimum). +- The `currency.type` is ERC20. + +#### Warnings + +This action must trigger the warnings: + +| Warning | Condition | +| --------------------------------------- | ----------------------------------------------------------- | +| "paymentAddress is given by the payer"  | If `signer` is the payer **and** `paymentAddress` is given  | +| "refundAddress is given by the payee"  | If `signer` is the payee **and** `refundAddress` is given | + +Note: These warnings are necessary to highlight to avoid attempts of fake payments and refunds. For example, a payer could create a request using as the payment address one of his own addresses. A system could interpret a transaction to this address as a payment while the payee did not receive the funds. + +#### Results + +An extension state is created with the following properties: + +|  Property |  Value | +| ------------------------- | -------------------------------------------------------------- | +| **id** | "pn-erc20-proxy-contract" | +| **type** | "paymentNetwork" | +| **version** | "0.1.0" | +| **values** | | +| **values.paymentAddress** | `paymentAddress` from parameters if given, undefined otherwise | +| **values.refundAddress** | `refundAddress` from parameters if given, undefined otherwise | +| **values.salt** | Salt for the request | +| **events** | Array with one 'create' event (see below) | + +the 'create' event: + +|  Property |  Value | +| ----------------------------- | -------------------------------------------------------------- | +| **name** | 'create' | +| **parameters** | | +| **parameters.paymentAddress** | `paymentAddress` from parameters if given, undefined otherwise | +| **parameters.refundAddress** | `refundAddress` from parameters if given, undefined otherwise | +| **parameters.salt** | Salt for the request | + +--- + +### Updates + +#### addPaymentAddress + +##### Parameters + +| | Type | Description | Requirement | +| ----------------------------- | ------ | ----------------------------------- | ------------- | +| **id** | String | Constant value: "pn-erc20-proxy-contract" | **Mandatory** | +| **action** | String | Constant value: "addPaymentAddress" | **Mandatory** | +| **parameters** | Object | | | +| **parameters.paymentAddress** | String | Ethereum address for the payment | **Mandatory** | + +##### Conditions + +This action is valid, if: + +- The extension state with the id "pn-erc20-proxy-contract" exists +- The signer is the `payee` +- The extension property `paymentAddress` is undefined + +##### Warnings + +None. + +##### Results + +An extension state is updated with the following properties: + +|  Property |  Value | +| -------------------------- | ---------------------------------------------------- | +| **values.paymentAddress** | `paymentAddress` from parameters | +| **events** | Add an 'paymentAddress' event (see below) at its end | + +the 'addPaymentAddress' event: + +|  Property |  Value | +| ----------------------------- | ----------------------------------- | +| **name** | Constant value: "addPaymentAddress" | +| **parameters** | | +| **parameters.paymentAddress** | `paymentAddress` from parameters | + +#### addRefundAddress + +##### Parameters + +| | Type | Description | Requirement | +| ---------------------------- | ------ | ---------------------------------- | ------------- | +| **id** | String | Constant value: "pn-erc20-proxy-contract" | **Mandatory** | +| **action** | String | Constant value: "addRefundAddress" | **Mandatory** | +| **parameters** | Object | | | +| **parameters.refundAddress** | String | Ethereum address for the refund | **Mandatory** | + +##### Conditions + +This action is valid if: + +- The extension state with the id "pn-erc20-proxy-contract" exists +- The signer is the `payer` +- The extension property `refundAddress` is undefined + +##### Warnings + +None. + +##### Results + +An extension state is updated with the following properties: + +|  Property |  Value | +| ------------------------ | ------------------------------------------------------ | +| **values.refundAddress** | `refundAddress` from parameters | +| **events** | Add an 'addRefundAddress' event (see below) at its end | + +The 'addRefundAddress' event: + +|  Property |  Value | +| ---------------------------- | ------------------------------- | +| **name** | 'addRefundAddress' | +| **parameters** | | +| **parameters.refundAddress** | `refundAddress` from parameters | + +--- + +## Interpretation + +The proxy contract address is determined by the `request.currency.network` (see (table)[#Contract] with proxy contract addresses). + +Any `TransferWithReference` events emitted from the proxy contract with the following arguments are considered as a payment: +- `tokenAddress` `===` `request.currency.value` +- `to` `===` `paymentAddress` +- `paymentReference` `===` `last8Bytes(hash(lowercase(requestId + salt + payment address)))` + +Any `TransferWithReference` events emitted from the proxy contract with the following arguments are considered as a refund: +- `tokenAddress` `===` `request.currency.value` +- `to` `===` `refundAddress` +- `paymentReference` `===` `last8Bytes(hash(lowercase(requestId + salt + refund address)))` + +The sum of payment amounts minus the sum of refund amounts is considered the balance. diff --git a/packages/advanced-logic/specs/payment-network-eth-input-data-0.1.0-DRAFT.md b/packages/advanced-logic/specs/payment-network-eth-input-data-0.2.0.md similarity index 72% rename from packages/advanced-logic/specs/payment-network-eth-input-data-0.1.0-DRAFT.md rename to packages/advanced-logic/specs/payment-network-eth-input-data-0.2.0.md index 092dd7c9d3..103abf29a5 100644 --- a/packages/advanced-logic/specs/payment-network-eth-input-data-0.1.0-DRAFT.md +++ b/packages/advanced-logic/specs/payment-network-eth-input-data-0.2.0.md @@ -5,13 +5,19 @@ You may be interested in this document if: - you want to create your own implementation of the Request protocol - you are curious enough to dive and see what is under the hood of the Request protocol -Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0-DRAFT.md)). +Prerequisite: Having read the advanced logic specification (see [here](./advanced-logic-specs-0.1.0.md)). ## Description This extension allows the payments and the refunds to be made in Ether on the Ethereum blockchain. -A specific value has to be given in input data when making the transfer to link the payment to the request. -The value is the last 8 bytes of a salted hash of the requestId: `last8Bytes(hash(requestId + salt + address))`: +A payment reference has to be given when making the transfer to link the payment to the request. + +There are two ways to add a payment reference to a transfer: + +1. add the reference to the input data of the transfer +2. call the ethereum proxy smart contract (see [Contract](#Contract)) + +The payment reference is the last 8 bytes of a salted hash of the requestId: `last8Bytes(hash(lowercase(requestId + salt + address)))`: - `requestId` is the id of the request - `salt` is a random number with at least 8 bytes of randomness. It must be unique to each request @@ -22,20 +28,36 @@ The value is the last 8 bytes of a salted hash of the requestId: `last8Bytes(has As a payment network, this extension allows to deduce a payment `balance` for the request. (see [Interpretation](#Interpretation)) +## Contract + +The contract contains one function called `transferWithReference` which takes 2 arguments: + +- `to` is the destination address +- `paymentReference` is the reference data used to track the transfer (see `paymentReference`) + +The `TransferWithReference` event is emitted when the Ether is transfered. This event contains the same 2 arguments as the `transferWithReference` function plus the `amount` of ethereum sent. + +[See smart contract source](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/smart-contracts/src/contracts/EthereumProxy.sol) + +| Network | Contract Address | +| ------- | ------------------------------------------ | +| Mainnet | 0x37a8f5f64f2a84f2377481537f04d2a59c9f59b6 | +| Rinkeby | 0x9c6c7817e3679c4b3f9ef9486001eae5aaed25ff | + ## Properties | Property | Type | Description | Requirement | | ------------------------- | ------ | ---------------------------------------------- | ------------- | -| **id** | String | constant value: "pn-eth-tx-data" | **Mandatory** | +| **id** | String | constant value: "pn-eth-input-data" | **Mandatory** | | **type** | String | constant value: "paymentNetwork" | **Mandatory** | -| **version** | String | constant value: "0.1.0" | **Mandatory** | +| **version** | String | constant value: "0.2.0" | **Mandatory** | | **events** | Array | List of the actions performed by the extension | **Mandatory** | | **values** | Object | | | | **values.paymentAddress** | String | Ethereum address for the payment | Optional | | **values.refundAddress** | String | Ethereum address for the refund | Optional | -| **values.salt** | String | Salt for the request | Optional | +| **values.salt** | String | Salt for the request | **Mandatory** | -Note: to use the Rinkeby testnet just replace the id by "pn-rinkeby-eth-tx-data" +Note: to use the Rinkeby testnet just set the `currency.network` to "rinkeby" --- @@ -45,20 +67,22 @@ Note: to use the Rinkeby testnet just replace the id by "pn-rinkeby-eth-tx-data" #### Parameters -| | Type | Description | Requirement | -| ----------------------------- | ------ | -------------------------------- | ------------- | -| **id** | String | Constant value: "pn-eth-tx-data" | **Mandatory** | -| **type** | String | Constant value: "paymentNetwork" | **Mandatory** | -| **version** | String | Constant value: "0.1.0" | **Mandatory** | -| **parameters** | Object | | | -| **parameters.paymentAddress** | String | Ethereum address for the payment | Optional | -| **parameters.refundAddress** | String | Ethereum address for the refund | Optional | -| **parameters.salt** | String | Salt for the request | **Mandatory** | +| | Type | Description | Requirement | +| ----------------------------- | ------ | ----------------------------------- | ------------- | +| **id** | String | Constant value: "pn-eth-input-data" | **Mandatory** | +| **type** | String | Constant value: "paymentNetwork" | **Mandatory** | +| **version** | String | Constant value: "0.2.0" | **Mandatory** | +| **parameters** | Object | | | +| **parameters.paymentAddress** | String | Ethereum address for the payment | Optional | +| **parameters.refundAddress** | String | Ethereum address for the refund | Optional | +| **parameters.salt** | String | Salt for the request | **Mandatory** | #### Conditions This action is valid if: +- The request `currency.type` must be "ETH" +- The request `currency.network` must be "mainnet" or 'rinkeby" - The `salt` is not empty and long enough (8 bytes of randomness minimum). #### Warnings @@ -78,9 +102,9 @@ A extension state is created with the following properties: |  Property |  Value | | ------------------------- | -------------------------------------------------------------- | -| **id** | "pn-eth-tx-data" | +| **id** | "pn-eth-input-data" | | **type** | "paymentNetwork" | -| **version** | "0.1.0" | +| **version** | "0.2.0" | | **values** | | | **values.paymentAddress** | `paymentAddress` from parameters if given, undefined otherwise | | **values.refundAddress** | `refundAddress` from parameters if given, undefined otherwise | @@ -107,7 +131,7 @@ the 'create' event: | | Type | Description | Requirement | | ----------------------------- | ------ | ----------------------------------- | ------------- | -| **id** | String | Constant value: "pn-eth-tx-data" | **Mandatory** | +| **id** | String | Constant value: "pn-eth-input-data" | **Mandatory** | | **action** | String | Constant value: "addPaymentAddress" | **Mandatory** | | **parameters** | Object | | | | **parameters.paymentAddress** | String | Ethereum address for the payment | **Mandatory** | @@ -116,7 +140,7 @@ the 'create' event: This action is valid, if: -- The extension state with the id "pn-eth-tx-data" exists +- The extension state with the id "pn-eth-input-data" exists - The signer is the `payee` - The extension property `paymentAddress` is undefined @@ -146,18 +170,18 @@ the 'addPaymentAddress' event: ##### Parameters -| | Type | Description | Requirement | -| ---------------------------- | ------ | ---------------------------------- | ------------- | -| **id** | String | Constant value: "pn-eth-tx-data" | **Mandatory** | -| **action** | String | Constant value: "addRefundAddress" | **Mandatory** | -| **parameters** | Object | | | -| **parameters.refundAddress** | String | Ethereum address for the refund | **Mandatory** | +| | Type | Description | Requirement | +| ---------------------------- | ------ | ----------------------------------- | ------------- | +| **id** | String | Constant value: "pn-eth-input-data" | **Mandatory** | +| **action** | String | Constant value: "addRefundAddress" | **Mandatory** | +| **parameters** | Object | | | +| **parameters.refundAddress** | String | Ethereum address for the refund | **Mandatory** | ##### Conditions This action is valid if: -- The extension state with the id "pn-eth-tx-data" exists +- The extension state with the id "pn-eth-input-data" exists - The signer is the `payer` - The extension property `refundAddress` is undefined @@ -187,6 +211,17 @@ The 'addRefundAddress' event: ## Interpretation +The proxy contract address is determined by the `request.currency.network` (see (table)[#Contract] with proxy contract addresses). + The `balance` starts from `0`. Any ETH transaction to `paymentAddress` with exactly `last8Bytes(hash(requestId + salt + payment address))` in input data is considered as a payment. The `balance` is increased by the sum of the amounts of the transactions. +Any `TransferWithReference` events emitted from the proxy contract with the following arguments are considered as a payment: + +- `to` `===` `paymentAddress` +- `paymentReference` `===` `last8Bytes(hash(lowercase(requestId + salt + payment address)))` + Any ETH transaction to `refundAddress` with exactly `last8Bytes(hash(requestId + salt + refund address))` in input data is considered as a refund. The `balance` is reduced by the sum of the amounts of the transactions. +Any `TransferWithReference` events emitted from the proxy contract with the following arguments are considered as a refund: + +- `to` `===` `refundAddress` +- `paymentReference` `===` `last8Bytes(hash(lowercase(requestId + salt + refund address)))` diff --git a/packages/advanced-logic/src/advanced-logic.ts b/packages/advanced-logic/src/advanced-logic.ts index 2115a7d4a5..eebf032bb2 100644 --- a/packages/advanced-logic/src/advanced-logic.ts +++ b/packages/advanced-logic/src/advanced-logic.ts @@ -10,6 +10,7 @@ import addressBasedBtc from './extensions/payment-network/bitcoin/mainnet-addres import addressBasedTestnetBtc from './extensions/payment-network/bitcoin/testnet-address-based'; import declarative from './extensions/payment-network/declarative'; import addressBasedErc20 from './extensions/payment-network/erc20/address-based'; +import proxyContractErc20 from './extensions/payment-network/erc20/proxy-contract'; import ethereumInputData from './extensions/payment-network/ethereum/input-data'; /** @@ -25,6 +26,7 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic contentData, declarative, ethereumInputData, + proxyContractErc20, }; /** @@ -92,6 +94,15 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic timestamp, ); } + if (id === ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT) { + return proxyContractErc20.applyActionToExtension( + extensionsState, + extensionAction, + requestState, + actionSigner, + timestamp, + ); + } if (id === ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA) { return ethereumInputData.applyActionToExtension( extensionsState, diff --git a/packages/advanced-logic/src/extensions/payment-network/declarative.ts b/packages/advanced-logic/src/extensions/payment-network/declarative.ts index 985d56dda0..8bf447afc2 100644 --- a/packages/advanced-logic/src/extensions/payment-network/declarative.ts +++ b/packages/advanced-logic/src/extensions/payment-network/declarative.ts @@ -53,7 +53,7 @@ function createDeclareSentPaymentAction( action: ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_SENT_PAYMENT, id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, parameters: { - amount: parameters.amount, + amount: parameters.amount.toString(), note: parameters.note, }, }; @@ -73,7 +73,7 @@ function createDeclareSentRefundAction( action: ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_SENT_REFUND, id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, parameters: { - amount: parameters.amount, + amount: parameters.amount.toString(), note: parameters.note, }, }; @@ -93,7 +93,7 @@ function createDeclareReceivedPaymentAction( action: ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_RECEIVED_PAYMENT, id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, parameters: { - amount: parameters.amount, + amount: parameters.amount.toString(), note: parameters.note, }, }; @@ -113,7 +113,7 @@ function createDeclareReceivedRefundAction( action: ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_RECEIVED_REFUND, id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, parameters: { - amount: parameters.amount, + amount: parameters.amount.toString(), note: parameters.note, }, }; diff --git a/packages/advanced-logic/src/extensions/payment-network/erc20/proxy-contract.ts b/packages/advanced-logic/src/extensions/payment-network/erc20/proxy-contract.ts new file mode 100644 index 0000000000..82ede684ec --- /dev/null +++ b/packages/advanced-logic/src/extensions/payment-network/erc20/proxy-contract.ts @@ -0,0 +1,147 @@ +import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; + +import ReferenceBased from '../reference-based'; + +const CURRENT_VERSION = '0.1.0'; + +const walletAddressValidator = require('wallet-address-validator'); + +/** + * Implementation of the payment network to pay in ERC20 based on a reference provided to a proxy contract. + * With this extension, one request can have two Ethereum addresses (one for payment and one for refund) and a specific value to give as input data + * Every ERC20 ethereum transaction that reaches these addresses through the proxy contract and has the correct reference will be interpreted as a payment or a refund. + * The value to give as input data is the last 8 bytes of a salted hash of the requestId and the address: `last8Bytes(hash(requestId + salt + address))`: + * The salt should have at least 8 bytes of randomness. A way to generate it is: + * `Math.floor(Math.random() * Math.pow(2, 4 * 8)).toString(16) + Math.floor(Math.random() * Math.pow(2, 4 * 8)).toString(16)` + */ +const erc20ProxyContract: ExtensionTypes.PnReferenceBased.IReferenceBased = { + applyActionToExtension, + createAddPaymentAddressAction, + createAddRefundAddressAction, + createCreationAction, + isValidAddress, +}; + +const supportedNetworks = ['mainnet', 'rinkeby', 'private']; + +/** + * Creates the extensionsData to create the extension ERC20 proxy contract payment detection + * + * @param creationParameters extensions parameters to create + * + * @returns IExtensionCreationAction the extensionsData to be stored in the request + */ +function createCreationAction( + creationParameters: ExtensionTypes.PnReferenceBased.ICreationParameters, +): ExtensionTypes.IAction { + if (creationParameters.paymentAddress && !isValidAddress(creationParameters.paymentAddress)) { + throw Error('paymentAddress is not a valid ethereum address'); + } + + if (creationParameters.refundAddress && !isValidAddress(creationParameters.refundAddress)) { + throw Error('refundAddress is not a valid ethereum address'); + } + + return ReferenceBased.createCreationAction( + ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + creationParameters, + CURRENT_VERSION, + ); +} + +/** + * Creates the extensionsData to add a payment address + * + * @param addPaymentAddressParameters extensions parameters to create + * + * @returns IAction the extensionsData to be stored in the request + */ +function createAddPaymentAddressAction( + addPaymentAddressParameters: ExtensionTypes.PnReferenceBased.IAddPaymentAddressParameters, +): ExtensionTypes.IAction { + if ( + addPaymentAddressParameters.paymentAddress && + !isValidAddress(addPaymentAddressParameters.paymentAddress) + ) { + throw Error('paymentAddress is not a valid ethereum address'); + } + + return ReferenceBased.createAddPaymentAddressAction( + ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + addPaymentAddressParameters, + ); +} + +/** + * Creates the extensionsData to add a refund address + * + * @param addRefundAddressParameters extensions parameters to create + * + * @returns IAction the extensionsData to be stored in the request + */ +function createAddRefundAddressAction( + addRefundAddressParameters: ExtensionTypes.PnReferenceBased.IAddRefundAddressParameters, +): ExtensionTypes.IAction { + if ( + addRefundAddressParameters.refundAddress && + !isValidAddress(addRefundAddressParameters.refundAddress) + ) { + throw Error('refundAddress is not a valid ethereum address'); + } + + return ReferenceBased.createAddRefundAddressAction( + ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + addRefundAddressParameters, + ); +} + +/** + * Applies the extension action to the request + * Is called to interpret the extensions data when applying the transaction + * + * @param extensionsState previous state of the extensions + * @param extensionAction action to apply + * @param requestState request state read-only + * @param actionSigner identity of the signer + * + * @returns state of the request updated + */ +function applyActionToExtension( + extensionsState: RequestLogicTypes.IExtensionStates, + extensionAction: ExtensionTypes.IAction, + requestState: RequestLogicTypes.IRequest, + actionSigner: IdentityTypes.IIdentity, + timestamp: number, +): RequestLogicTypes.IExtensionStates { + if ( + requestState.currency.type !== RequestLogicTypes.CURRENCY.ERC20 || + (requestState.currency.network && !supportedNetworks.includes(requestState.currency.network)) + ) { + throw Error( + `This extension can be used only on ERC20 requests and on supported networks ${supportedNetworks.join( + ', ', + )}`, + ); + } + + return ReferenceBased.applyActionToExtension( + isValidAddress, + extensionsState, + extensionAction, + requestState, + actionSigner, + timestamp, + ); +} + +/** + * Check if an ethereum address is valid + * + * @param {string} address address to check + * @returns {boolean} true if address is valid + */ +function isValidAddress(address: string): boolean { + return walletAddressValidator.validate(address, 'ethereum'); +} + +export default erc20ProxyContract; diff --git a/packages/advanced-logic/src/extensions/payment-network/ethereum/input-data.ts b/packages/advanced-logic/src/extensions/payment-network/ethereum/input-data.ts index 86c6a956b3..6e9f05759d 100644 --- a/packages/advanced-logic/src/extensions/payment-network/ethereum/input-data.ts +++ b/packages/advanced-logic/src/extensions/payment-network/ethereum/input-data.ts @@ -1,8 +1,11 @@ import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; -import Utils from '@requestnetwork/utils'; + +import ReferenceBased from '../reference-based'; const walletAddressValidator = require('wallet-address-validator'); +const CURRENT_VERSION = '0.2.0'; + /** * Implementation of the payment network to pay in ETH based on input data. * With this extension, one request can have two Ethereum addresses (one for payment and one for refund) and a specific value to give as input data @@ -11,7 +14,7 @@ const walletAddressValidator = require('wallet-address-validator'); * The salt should have at least 8 bytes of randomness. A way to generate it is: * `Math.floor(Math.random() * Math.pow(2, 4 * 8)).toString(16) + Math.floor(Math.random() * Math.pow(2, 4 * 8)).toString(16)` */ -const ethInputData: ExtensionTypes.PnEthInputData.IEthInputData = { +const ethInputData: ExtensionTypes.PnReferenceBased.IReferenceBased = { applyActionToExtension, createAddPaymentAddressAction, createAddRefundAddressAction, @@ -21,11 +24,6 @@ const ethInputData: ExtensionTypes.PnEthInputData.IEthInputData = { const supportedNetworks = ['mainnet', 'rinkeby']; -// Regex for "at least 16 hexadecimal numbers". Used to validate the salt -const eightHexRegex = /[0-9a-f]{16,}/; - -const CURRENT_VERSION = '0.1.0'; - /** * Creates the extensionsData to create the ETH payment detection extension * @@ -34,7 +32,7 @@ const CURRENT_VERSION = '0.1.0'; * @returns IExtensionCreationAction the extensionsData to be stored in the request */ function createCreationAction( - creationParameters: ExtensionTypes.PnEthInputData.ICreationParameters, + creationParameters: ExtensionTypes.PnReferenceBased.ICreationParameters, ): ExtensionTypes.IAction { if (creationParameters.paymentAddress && !isValidAddress(creationParameters.paymentAddress)) { throw Error('paymentAddress is not a valid ethereum address'); @@ -44,23 +42,11 @@ function createCreationAction( throw Error('refundAddress is not a valid ethereum address'); } - if (!creationParameters.salt) { - throw Error('salt should not be empty'); - } - - if (!eightHexRegex.test(creationParameters.salt)) { - /* eslint-disable spellcheck/spell-checker */ - throw Error( - `salt be a string of minimum 16 hexadecimal characters. Example: 'ea3bc7caf64110ca'`, - ); - } - - return { - action: ExtensionTypes.PnAddressBased.ACTION.CREATE, - id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, - parameters: creationParameters, - version: CURRENT_VERSION, - }; + return ReferenceBased.createCreationAction( + ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + creationParameters, + CURRENT_VERSION, + ); } /** @@ -71,7 +57,7 @@ function createCreationAction( * @returns IAction the extensionsData to be stored in the request */ function createAddPaymentAddressAction( - addPaymentAddressParameters: ExtensionTypes.PnEthInputData.IAddPaymentAddressParameters, + addPaymentAddressParameters: ExtensionTypes.PnReferenceBased.IAddPaymentAddressParameters, ): ExtensionTypes.IAction { if ( addPaymentAddressParameters.paymentAddress && @@ -80,11 +66,10 @@ function createAddPaymentAddressAction( throw Error('paymentAddress is not a valid ethereum address'); } - return { - action: ExtensionTypes.PnAddressBased.ACTION.ADD_PAYMENT_ADDRESS, - id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, - parameters: addPaymentAddressParameters, - }; + return ReferenceBased.createAddPaymentAddressAction( + ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + addPaymentAddressParameters, + ); } /** @@ -95,7 +80,7 @@ function createAddPaymentAddressAction( * @returns IAction the extensionsData to be stored in the request */ function createAddRefundAddressAction( - addRefundAddressParameters: ExtensionTypes.PnEthInputData.IAddRefundAddressParameters, + addRefundAddressParameters: ExtensionTypes.PnReferenceBased.IAddRefundAddressParameters, ): ExtensionTypes.IAction { if ( addRefundAddressParameters.refundAddress && @@ -104,11 +89,10 @@ function createAddRefundAddressAction( throw Error('refundAddress is not a valid ethereum address'); } - return { - action: ExtensionTypes.PnAddressBased.ACTION.ADD_REFUND_ADDRESS, - id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, - parameters: addRefundAddressParameters, - }; + return ReferenceBased.createAddRefundAddressAction( + ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + addRefundAddressParameters, + ); } /** @@ -140,191 +124,14 @@ function applyActionToExtension( ); } - const copiedExtensionState: RequestLogicTypes.IExtensionStates = Utils.deepCopy(extensionsState); - - if (extensionAction.action === ExtensionTypes.PnEthInputData.ACTION.CREATE) { - if (requestState.extensions[extensionAction.id]) { - throw Error(`This extension has already been created`); - } - - copiedExtensionState[extensionAction.id] = applyCreation(extensionAction, timestamp); - - return copiedExtensionState; - } - - // if the action is not "create", the state must have been created before - if (!requestState.extensions[extensionAction.id]) { - throw Error(`The extension should be created before receiving any other action`); - } - - if (extensionAction.action === ExtensionTypes.PnEthInputData.ACTION.ADD_PAYMENT_ADDRESS) { - copiedExtensionState[extensionAction.id] = applyAddPaymentAddress( - copiedExtensionState[extensionAction.id], - extensionAction, - requestState, - actionSigner, - timestamp, - ); - - return copiedExtensionState; - } - - if (extensionAction.action === ExtensionTypes.PnEthInputData.ACTION.ADD_REFUND_ADDRESS) { - copiedExtensionState[extensionAction.id] = applyAddRefundAddress( - copiedExtensionState[extensionAction.id], - extensionAction, - requestState, - actionSigner, - timestamp, - ); - - return copiedExtensionState; - } - - throw Error(`Unknown action: ${extensionAction.action}`); -} - -/** - * Applies a creation - * - * @param isValidAddress address validator function - * @param extensionAction action to apply - * - * @returns state of the extension created - */ -function applyCreation( - extensionAction: ExtensionTypes.IAction, - timestamp: number, -): ExtensionTypes.IState { - if ( - extensionAction.parameters.paymentAddress && - !isValidAddress(extensionAction.parameters.paymentAddress) - ) { - throw Error('paymentAddress is not a valid address'); - } - if ( - extensionAction.parameters.refundAddress && - !isValidAddress(extensionAction.parameters.refundAddress) - ) { - throw Error('refundAddress is not a valid address'); - } - return { - events: [ - { - name: 'create', - parameters: { - paymentAddress: extensionAction.parameters.paymentAddress, - refundAddress: extensionAction.parameters.refundAddress, - salt: extensionAction.parameters.salt, - }, - timestamp, - }, - ], - id: extensionAction.id, - type: ExtensionTypes.TYPE.PAYMENT_NETWORK, - values: { - paymentAddress: extensionAction.parameters.paymentAddress, - refundAddress: extensionAction.parameters.refundAddress, - salt: extensionAction.parameters.salt, - }, - version: CURRENT_VERSION, - }; -} - -/** - * Applies add payment address - * - * @param isValidAddress address validator function - * @param extensionState previous state of the extension - * @param extensionAction action to apply - * @param requestState request state read-only - * @param actionSigner identity of the signer - * - * @returns state of the extension updated - */ -function applyAddPaymentAddress( - extensionState: ExtensionTypes.IState, - extensionAction: ExtensionTypes.IAction, - requestState: RequestLogicTypes.IRequest, - actionSigner: IdentityTypes.IIdentity, - timestamp: number, -): ExtensionTypes.IState { - if ( - extensionAction.parameters.paymentAddress && - !isValidAddress(extensionAction.parameters.paymentAddress) - ) { - throw Error('paymentAddress is not a valid address'); - } - if (extensionState.values.paymentAddress) { - throw Error(`Payment address already given`); - } - if (!requestState.payee) { - throw Error(`The request must have a payee`); - } - if (!Utils.identity.areEqual(actionSigner, requestState.payee)) { - throw Error(`The signer must be the payee`); - } - - const copiedExtensionState: ExtensionTypes.IState = Utils.deepCopy(extensionState); - - // update payment address - copiedExtensionState.values.paymentAddress = extensionAction.parameters.paymentAddress; - // update events - copiedExtensionState.events.push({ - name: ExtensionTypes.PnAddressBased.ACTION.ADD_PAYMENT_ADDRESS, - parameters: { paymentAddress: extensionAction.parameters.paymentAddress }, - timestamp, - }); - - return copiedExtensionState; -} - -/** - * Applies add refund address - * - * @param isValidAddress address validator function - * @param extensionState previous state of the extension - * @param extensionAction action to apply - * @param requestState request state read-only - * @param actionSigner identity of the signer - * - * @returns state of the extension updated - */ -function applyAddRefundAddress( - extensionState: ExtensionTypes.IState, - extensionAction: ExtensionTypes.IAction, - requestState: RequestLogicTypes.IRequest, - actionSigner: IdentityTypes.IIdentity, - timestamp: number, -): ExtensionTypes.IState { - if ( - extensionAction.parameters.refundAddress && - !isValidAddress(extensionAction.parameters.refundAddress) - ) { - throw Error('refundAddress is not a valid address'); - } - if (extensionState.values.refundAddress) { - throw Error(`Refund address already given`); - } - if (!requestState.payer) { - throw Error(`The request must have a payer`); - } - if (!Utils.identity.areEqual(actionSigner, requestState.payer)) { - throw Error(`The signer must be the payer`); - } - - const copiedExtensionState: ExtensionTypes.IState = Utils.deepCopy(extensionState); - - // update refund address - copiedExtensionState.values.refundAddress = extensionAction.parameters.refundAddress; - // update events - copiedExtensionState.events.push({ - name: ExtensionTypes.PnAddressBased.ACTION.ADD_REFUND_ADDRESS, - parameters: { refundAddress: extensionAction.parameters.refundAddress }, + return ReferenceBased.applyActionToExtension( + isValidAddress, + extensionsState, + extensionAction, + requestState, + actionSigner, timestamp, - }); - - return copiedExtensionState; + ); } /** diff --git a/packages/advanced-logic/src/extensions/payment-network/reference-based.ts b/packages/advanced-logic/src/extensions/payment-network/reference-based.ts new file mode 100644 index 0000000000..1f0c7bd5c9 --- /dev/null +++ b/packages/advanced-logic/src/extensions/payment-network/reference-based.ts @@ -0,0 +1,301 @@ +import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; + +/** + * Core of the reference based payment networks + * This module is called by the reference based payment networks to avoid code redundancy + */ +export default { + applyActionToExtension, + createAddPaymentAddressAction, + createAddRefundAddressAction, + createCreationAction, +}; + +// Regex for "at least 16 hexadecimal numbers". Used to validate the salt +const eightHexRegex = /[0-9a-f]{16,}/; + +/** + * Creates the extensionsData to create the ETH payment detection extension + * + * @param creationParameters extensions parameters to create + * + * @returns IExtensionCreationAction the extensionsData to be stored in the request + */ +function createCreationAction( + extensionId: ExtensionTypes.ID, + creationParameters: ExtensionTypes.PnReferenceBased.ICreationParameters, + version: string, +): ExtensionTypes.IAction { + if (!creationParameters.salt) { + throw Error('salt should not be empty'); + } + + if (!eightHexRegex.test(creationParameters.salt)) { + /* eslint-disable spellcheck/spell-checker */ + throw Error( + `The salt must be a string of minimum 16 hexadecimal characters. Example: 'ea3bc7caf64110ca'`, + ); + } + + return { + action: ExtensionTypes.PnReferenceBased.ACTION.CREATE, + id: extensionId, + parameters: creationParameters, + version, + }; +} + +/** + * Creates the extensionsData to add a payment address + * + * @param addPaymentAddressParameters extensions parameters to create + * + * @returns IAction the extensionsData to be stored in the request + */ +function createAddPaymentAddressAction( + extensionId: ExtensionTypes.ID, + addPaymentAddressParameters: ExtensionTypes.PnReferenceBased.IAddPaymentAddressParameters, +): ExtensionTypes.IAction { + return { + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + id: extensionId, + parameters: addPaymentAddressParameters, + }; +} + +/** + * Creates the extensionsData to add a refund address + * + * @param addRefundAddressParameters extensions parameters to create + * + * @returns IAction the extensionsData to be stored in the request + */ +function createAddRefundAddressAction( + extensionId: ExtensionTypes.ID, + addRefundAddressParameters: ExtensionTypes.PnReferenceBased.IAddRefundAddressParameters, +): ExtensionTypes.IAction { + return { + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + id: extensionId, + parameters: addRefundAddressParameters, + }; +} + +/** + * Applies the extension action to the request + * Is called to interpret the extensions data when applying the transaction + * + * @param extensionsState previous state of the extensions + * @param extensionAction action to apply + * @param requestState request state read-only + * @param actionSigner identity of the signer + * + * @returns state of the request updated + */ +function applyActionToExtension( + isValidAddress: (address: string) => boolean, + extensionsState: RequestLogicTypes.IExtensionStates, + extensionAction: ExtensionTypes.IAction, + requestState: RequestLogicTypes.IRequest, + actionSigner: IdentityTypes.IIdentity, + timestamp: number, +): RequestLogicTypes.IExtensionStates { + const copiedExtensionState: RequestLogicTypes.IExtensionStates = Utils.deepCopy(extensionsState); + + if (extensionAction.action === ExtensionTypes.PnReferenceBased.ACTION.CREATE) { + if (requestState.extensions[extensionAction.id]) { + throw Error(`This extension has already been created`); + } + + copiedExtensionState[extensionAction.id] = applyCreation( + isValidAddress, + extensionAction, + timestamp, + ); + + return copiedExtensionState; + } + + // if the action is not "create", the state must have been created before + if (!requestState.extensions[extensionAction.id]) { + throw Error(`The extension should be created before receiving any other action`); + } + + if (extensionAction.action === ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS) { + copiedExtensionState[extensionAction.id] = applyAddPaymentAddress( + isValidAddress, + copiedExtensionState[extensionAction.id], + extensionAction, + requestState, + actionSigner, + timestamp, + ); + + return copiedExtensionState; + } + + if (extensionAction.action === ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS) { + copiedExtensionState[extensionAction.id] = applyAddRefundAddress( + isValidAddress, + copiedExtensionState[extensionAction.id], + extensionAction, + requestState, + actionSigner, + timestamp, + ); + + return copiedExtensionState; + } + + throw Error(`Unknown action: ${extensionAction.action}`); +} + +/** + * Applies a creation extension action + * + * @param isValidAddress address validator function + * @param extensionAction action to apply + * + * @returns state of the extension created + */ +function applyCreation( + isValidAddress: (address: string) => boolean, + extensionAction: ExtensionTypes.IAction, + timestamp: number, +): ExtensionTypes.IState { + if (!extensionAction.version) { + throw Error('version is missing'); + } + if ( + extensionAction.parameters.paymentAddress && + !isValidAddress(extensionAction.parameters.paymentAddress) + ) { + throw Error('paymentAddress is not a valid address'); + } + if ( + extensionAction.parameters.refundAddress && + !isValidAddress(extensionAction.parameters.refundAddress) + ) { + throw Error('refundAddress is not a valid address'); + } + return { + events: [ + { + name: 'create', + parameters: { + paymentAddress: extensionAction.parameters.paymentAddress, + refundAddress: extensionAction.parameters.refundAddress, + salt: extensionAction.parameters.salt, + }, + timestamp, + }, + ], + id: extensionAction.id, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress: extensionAction.parameters.paymentAddress, + refundAddress: extensionAction.parameters.refundAddress, + salt: extensionAction.parameters.salt, + }, + version: extensionAction.version, + }; +} + +/** + * Applies an add payment address extension action + * + * @param isValidAddress address validator function + * @param extensionState previous state of the extension + * @param extensionAction action to apply + * @param requestState request state read-only + * @param actionSigner identity of the signer + * + * @returns state of the extension updated + */ +function applyAddPaymentAddress( + isValidAddress: (address: string) => boolean, + extensionState: ExtensionTypes.IState, + extensionAction: ExtensionTypes.IAction, + requestState: RequestLogicTypes.IRequest, + actionSigner: IdentityTypes.IIdentity, + timestamp: number, +): ExtensionTypes.IState { + if ( + extensionAction.parameters.paymentAddress && + !isValidAddress(extensionAction.parameters.paymentAddress) + ) { + throw Error('paymentAddress is not a valid address'); + } + if (extensionState.values.paymentAddress) { + throw Error(`Payment address already given`); + } + if (!requestState.payee) { + throw Error(`The request must have a payee`); + } + if (!Utils.identity.areEqual(actionSigner, requestState.payee)) { + throw Error(`The signer must be the payee`); + } + + const copiedExtensionState: ExtensionTypes.IState = Utils.deepCopy(extensionState); + + // update payment address + copiedExtensionState.values.paymentAddress = extensionAction.parameters.paymentAddress; + // update events + copiedExtensionState.events.push({ + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + parameters: { paymentAddress: extensionAction.parameters.paymentAddress }, + timestamp, + }); + + return copiedExtensionState; +} + +/** + * Applies an add refund address extension action + * + * @param isValidAddress address validator function + * @param extensionState previous state of the extension + * @param extensionAction action to apply + * @param requestState request state read-only + * @param actionSigner identity of the signer + * + * @returns state of the extension updated + */ +function applyAddRefundAddress( + isValidAddress: (address: string) => boolean, + extensionState: ExtensionTypes.IState, + extensionAction: ExtensionTypes.IAction, + requestState: RequestLogicTypes.IRequest, + actionSigner: IdentityTypes.IIdentity, + timestamp: number, +): ExtensionTypes.IState { + if ( + extensionAction.parameters.refundAddress && + !isValidAddress(extensionAction.parameters.refundAddress) + ) { + throw Error('refundAddress is not a valid address'); + } + if (extensionState.values.refundAddress) { + throw Error(`Refund address already given`); + } + if (!requestState.payer) { + throw Error(`The request must have a payer`); + } + if (!Utils.identity.areEqual(actionSigner, requestState.payer)) { + throw Error(`The signer must be the payer`); + } + + const copiedExtensionState: ExtensionTypes.IState = Utils.deepCopy(extensionState); + + // update refund address + copiedExtensionState.values.refundAddress = extensionAction.parameters.refundAddress; + // update events + copiedExtensionState.events.push({ + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + parameters: { refundAddress: extensionAction.parameters.refundAddress }, + timestamp, + }); + + return copiedExtensionState; +} diff --git a/packages/advanced-logic/test/extensions/payment-network/erc20/mainnet-address-based.test.ts b/packages/advanced-logic/test/extensions/payment-network/erc20/address-based.test.ts similarity index 99% rename from packages/advanced-logic/test/extensions/payment-network/erc20/mainnet-address-based.test.ts rename to packages/advanced-logic/test/extensions/payment-network/erc20/address-based.test.ts index 81d359f50e..b5a998f538 100644 --- a/packages/advanced-logic/test/extensions/payment-network/erc20/mainnet-address-based.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/erc20/address-based.test.ts @@ -6,8 +6,8 @@ import Utils from '@requestnetwork/utils'; import { expect } from 'chai'; -import * as DataERC20AddPaymentAddress from '../../../utils/payment-network/erc20/add-payment-address-data-generator'; -import * as DataERC20Create from '../../../utils/payment-network/erc20/create-data-generator'; +import * as DataERC20AddPaymentAddress from '../../../utils/payment-network/erc20/address-based-add-payment-address-data-generator'; +import * as DataERC20Create from '../../../utils/payment-network/erc20/address-based-create-data-generator'; import * as TestData from '../../../utils/test-data-generator'; /* tslint:disable:no-unused-expression */ diff --git a/packages/advanced-logic/test/extensions/payment-network/erc20/proxy-contract.test.ts b/packages/advanced-logic/test/extensions/payment-network/erc20/proxy-contract.test.ts new file mode 100644 index 0000000000..485dfae372 --- /dev/null +++ b/packages/advanced-logic/test/extensions/payment-network/erc20/proxy-contract.test.ts @@ -0,0 +1,393 @@ +import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; +import 'mocha'; + +import erc20ProxyContract from '../../../../src/extensions/payment-network/erc20/proxy-contract'; + +import { expect } from 'chai'; + +import * as DataERC20AddPaymentAddress from '../../../utils/payment-network/erc20/proxy-contract-add-payment-address-data-generator'; +import * as DataERC20Create from '../../../utils/payment-network/erc20/proxy-contract-create-data-generator'; +import * as TestData from '../../../utils/test-data-generator'; + +/* tslint:disable:no-unused-expression */ +describe('extensions/payment-network/erc20/proxy-contract', () => { + describe('createCreationAction', () => { + it('can create a create action', () => { + expect( + erc20ProxyContract.createCreationAction({ + paymentAddress: '0x0000000000000000000000000000000000000001', + refundAddress: '0x0000000000000000000000000000000000000002', + salt: 'ea3bc7caf64110ca', + }), + 'extensionsdata is wrong', + ).to.deep.equal({ + action: 'create', + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress: '0x0000000000000000000000000000000000000001', + refundAddress: '0x0000000000000000000000000000000000000002', + salt: 'ea3bc7caf64110ca', + }, + version: '0.1.0', + }); + }); + + it('can create a create action with only salt', () => { + expect( + erc20ProxyContract.createCreationAction({ + salt: 'ea3bc7caf64110ca', + }), + 'extensionsdata is wrong', + ).to.deep.equal({ + action: 'create', + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + salt: 'ea3bc7caf64110ca', + }, + version: '0.1.0', + }); + }); + + it('cannot createCreationAction with payment address not an ethereum address', () => { + expect(() => { + erc20ProxyContract.createCreationAction({ + paymentAddress: 'not an ethereum address', + refundAddress: '0x0000000000000000000000000000000000000002', + salt: 'ea3bc7caf64110ca', + }); + }, 'must throw').to.throw('paymentAddress is not a valid ethereum address'); + }); + + it('cannot createCreationAction with refund address not an ethereum address', () => { + expect(() => { + erc20ProxyContract.createCreationAction({ + paymentAddress: '0x0000000000000000000000000000000000000001', + refundAddress: 'not an ethereum address', + salt: 'ea3bc7caf64110ca', + }); + }, 'must throw').to.throw('refundAddress is not a valid ethereum address'); + }); + }); + + describe('createAddPaymentAddressAction', () => { + it('can createAddPaymentAddressAction', () => { + expect( + erc20ProxyContract.createAddPaymentAddressAction({ + paymentAddress: '0x0000000000000000000000000000000000000001', + }), + 'extensionsdata is wrong', + ).to.deep.equal({ + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress: '0x0000000000000000000000000000000000000001', + }, + }); + }); + + it('cannot createAddPaymentAddressAction with payment address not an ethereum address', () => { + expect(() => { + erc20ProxyContract.createAddPaymentAddressAction({ + paymentAddress: 'not an ethereum address', + }); + }, 'must throw').to.throw('paymentAddress is not a valid ethereum address'); + }); + }); + + describe('createAddRefundAddressAction', () => { + it('can createAddRefundAddressAction', () => { + expect( + erc20ProxyContract.createAddRefundAddressAction({ + refundAddress: '0x0000000000000000000000000000000000000002', + }), + 'extensionsdata is wrong', + ).to.deep.equal({ + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + refundAddress: '0x0000000000000000000000000000000000000002', + }, + }); + }); + it('cannot createAddRefundAddressAction with payment address not an ethereum address', () => { + expect(() => { + erc20ProxyContract.createAddRefundAddressAction({ + refundAddress: 'not an ethereum address', + }); + }, 'must throw').to.throw('refundAddress is not a valid ethereum address'); + }); + }); + + describe('applyActionToExtension', () => { + describe('applyActionToExtension/unknown action', () => { + it('cannot applyActionToExtensions of unknown action', () => { + const unknownAction = Utils.deepCopy(DataERC20AddPaymentAddress.actionAddPaymentAddress); + unknownAction.action = 'unknown action'; + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedEmpty.extensions, + unknownAction, + DataERC20Create.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('Unknown action: unknown action'); + }); + + it('cannot applyActionToExtensions of unknown id', () => { + const unknownAction = Utils.deepCopy(DataERC20AddPaymentAddress.actionAddPaymentAddress); + unknownAction.id = 'unknown id'; + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedEmpty.extensions, + unknownAction, + DataERC20Create.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw( + 'The extension should be created before receiving any other action', + ); + }); + }); + + describe('applyActionToExtension/create', () => { + it('can applyActionToExtensions of creation', () => { + expect( + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateNoExtensions.extensions, + DataERC20Create.actionCreationWithPaymentAndRefund, + DataERC20Create.requestStateNoExtensions, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ), + 'new extension state wrong', + ).to.deep.equal(DataERC20Create.extensionStateWithPaymentAndRefund); + }); + + it('cannot applyActionToExtensions of creation with a previous state', () => { + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedWithPaymentAndRefund.extensions, + DataERC20Create.actionCreationWithPaymentAndRefund, + DataERC20Create.requestStateCreatedWithPaymentAndRefund, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('This extension has already been created'); + }); + + it('cannot applyActionToExtensions of creation on a not Eth request', () => { + const requestCreatedNoExtension: RequestLogicTypes.IRequest = Utils.deepCopy( + TestData.requestCreatedNoExtension, + ); + requestCreatedNoExtension.currency = { + type: RequestLogicTypes.CURRENCY.BTC, + value: 'BTC', + }; + expect(() => { + erc20ProxyContract.applyActionToExtension( + TestData.requestCreatedNoExtension.extensions, + DataERC20Create.actionCreationWithPaymentAndRefund, + requestCreatedNoExtension, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw( + 'This extension can be used only on ERC20 requests and on supported networks mainnet, rinkeby, private', + ); + }); + + it('cannot applyActionToExtensions of creation with payment address not valid', () => { + const testnetPaymentAddress = Utils.deepCopy( + DataERC20Create.actionCreationWithPaymentAndRefund, + ); + testnetPaymentAddress.parameters.paymentAddress = DataERC20AddPaymentAddress.invalidAddress; + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateNoExtensions.extensions, + testnetPaymentAddress, + DataERC20Create.requestStateNoExtensions, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('paymentAddress is not a valid address'); + }); + + it('cannot applyActionToExtensions of creation with refund address not valid', () => { + const testnetRefundAddress = Utils.deepCopy( + DataERC20Create.actionCreationWithPaymentAndRefund, + ); + testnetRefundAddress.parameters.refundAddress = DataERC20AddPaymentAddress.invalidAddress; + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateNoExtensions.extensions, + testnetRefundAddress, + DataERC20Create.requestStateNoExtensions, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('refundAddress is not a valid address'); + }); + }); + + describe('applyActionToExtension/addPaymentAddress', () => { + it('can applyActionToExtensions of addPaymentAddress', () => { + expect( + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedEmpty.extensions, + DataERC20AddPaymentAddress.actionAddPaymentAddress, + DataERC20Create.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ), + 'new extension state wrong', + ).to.deep.equal(DataERC20AddPaymentAddress.extensionStateWithPaymentAfterCreation); + }); + it('cannot applyActionToExtensions of addPaymentAddress without a previous state', () => { + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateNoExtensions.extensions, + DataERC20AddPaymentAddress.actionAddPaymentAddress, + DataERC20Create.requestStateNoExtensions, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw( + `The extension should be created before receiving any other action`, + ); + }); + it('cannot applyActionToExtensions of addPaymentAddress without a payee', () => { + const previousState = Utils.deepCopy(DataERC20Create.requestStateCreatedEmpty); + previousState.payee = undefined; + expect(() => { + erc20ProxyContract.applyActionToExtension( + previousState.extensions, + DataERC20AddPaymentAddress.actionAddPaymentAddress, + previousState, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The request must have a payee`); + }); + it('cannot applyActionToExtensions of addPaymentAddress signed by someone else than the payee', () => { + const previousState = Utils.deepCopy(DataERC20Create.requestStateCreatedEmpty); + expect(() => { + erc20ProxyContract.applyActionToExtension( + previousState.extensions, + DataERC20AddPaymentAddress.actionAddPaymentAddress, + previousState, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The signer must be the payee`); + }); + it('cannot applyActionToExtensions of addPaymentAddress with payment address already given', () => { + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedWithPaymentAndRefund.extensions, + DataERC20AddPaymentAddress.actionAddPaymentAddress, + DataERC20Create.requestStateCreatedWithPaymentAndRefund, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`Payment address already given`); + }); + it('cannot applyActionToExtensions of addPaymentAddress with payment address not valid', () => { + const testnetPaymentAddress = Utils.deepCopy( + DataERC20AddPaymentAddress.actionAddPaymentAddress, + ); + testnetPaymentAddress.parameters.paymentAddress = DataERC20AddPaymentAddress.invalidAddress; + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedEmpty.extensions, + testnetPaymentAddress, + DataERC20Create.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('paymentAddress is not a valid address'); + }); + }); + + describe('applyActionToExtension/addRefundAddress', () => { + it('can applyActionToExtensions of addRefundAddress', () => { + expect( + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedEmpty.extensions, + DataERC20AddPaymentAddress.actionAddRefundAddress, + DataERC20Create.requestStateCreatedEmpty, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ), + 'new extension state wrong', + ).to.deep.equal(DataERC20AddPaymentAddress.extensionStateWithRefundAfterCreation); + }); + it('cannot applyActionToExtensions of addRefundAddress without a previous state', () => { + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateNoExtensions.extensions, + DataERC20AddPaymentAddress.actionAddRefundAddress, + DataERC20Create.requestStateNoExtensions, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw( + `The extension should be created before receiving any other action`, + ); + }); + it('cannot applyActionToExtensions of addRefundAddress without a payer', () => { + const previousState = Utils.deepCopy(DataERC20Create.requestStateCreatedEmpty); + previousState.payer = undefined; + expect(() => { + erc20ProxyContract.applyActionToExtension( + previousState.extensions, + DataERC20AddPaymentAddress.actionAddRefundAddress, + previousState, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The request must have a payer`); + }); + it('cannot applyActionToExtensions of addRefundAddress signed by someone else than the payer', () => { + const previousState = Utils.deepCopy(DataERC20Create.requestStateCreatedEmpty); + expect(() => { + erc20ProxyContract.applyActionToExtension( + previousState.extensions, + DataERC20AddPaymentAddress.actionAddRefundAddress, + previousState, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The signer must be the payer`); + }); + it('cannot applyActionToExtensions of addRefundAddress with payment address already given', () => { + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedWithPaymentAndRefund.extensions, + DataERC20AddPaymentAddress.actionAddRefundAddress, + DataERC20Create.requestStateCreatedWithPaymentAndRefund, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`Refund address already given`); + }); + it('cannot applyActionToExtensions of addRefundAddress with refund address not valid', () => { + const testnetPaymentAddress = Utils.deepCopy( + DataERC20AddPaymentAddress.actionAddRefundAddress, + ); + testnetPaymentAddress.parameters.refundAddress = DataERC20AddPaymentAddress.invalidAddress; + expect(() => { + erc20ProxyContract.applyActionToExtension( + DataERC20Create.requestStateCreatedEmpty.extensions, + testnetPaymentAddress, + DataERC20Create.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('refundAddress is not a valid address'); + }); + }); + }); +}); diff --git a/packages/advanced-logic/test/extensions/payment-network/ethereum/input-data.test.ts b/packages/advanced-logic/test/extensions/payment-network/ethereum/input-data.test.ts index 12f8fb5219..ae7164b558 100644 --- a/packages/advanced-logic/test/extensions/payment-network/ethereum/input-data.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/ethereum/input-data.test.ts @@ -29,7 +29,7 @@ describe('extensions/payment-network/ethereum/input-data', () => { refundAddress: '0x0000000000000000000000000000000000000002', salt: 'ea3bc7caf64110ca', }, - version: '0.1.0', + version: '0.2.0', }); }); @@ -45,7 +45,7 @@ describe('extensions/payment-network/ethereum/input-data', () => { parameters: { salt: 'ea3bc7caf64110ca', }, - version: '0.1.0', + version: '0.2.0', }); }); @@ -78,7 +78,7 @@ describe('extensions/payment-network/ethereum/input-data', () => { }), 'extensionsdata is wrong', ).to.deep.equal({ - action: ExtensionTypes.PnAddressBased.ACTION.ADD_PAYMENT_ADDRESS, + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, parameters: { paymentAddress: '0x0000000000000000000000000000000000000001', @@ -103,7 +103,7 @@ describe('extensions/payment-network/ethereum/input-data', () => { }), 'extensionsdata is wrong', ).to.deep.equal({ - action: ExtensionTypes.PnAddressBased.ACTION.ADD_REFUND_ADDRESS, + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, parameters: { refundAddress: '0x0000000000000000000000000000000000000002', diff --git a/packages/advanced-logic/test/extensions/payment-network/reference-based.test.ts b/packages/advanced-logic/test/extensions/payment-network/reference-based.test.ts new file mode 100644 index 0000000000..283a2a2657 --- /dev/null +++ b/packages/advanced-logic/test/extensions/payment-network/reference-based.test.ts @@ -0,0 +1,321 @@ +import 'mocha'; + +import referenceBasedManager from '../../../src/extensions/payment-network/reference-based'; + +import { ExtensionTypes } from '@requestnetwork/types'; + +import Utils from '@requestnetwork/utils'; + +import { expect } from 'chai'; + +import * as DataAddPaymentAddress from '../../utils/payment-network/reference-based-add-payment-address-data-generator'; +import * as DataCreate from '../../utils/payment-network/reference-based-data-generator'; +import * as TestData from '../../utils/test-data-generator'; + +const isValidAddressMock = (valid = true): (() => boolean) => (): boolean => valid; + +const PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED = 'do-not-use!-pn-test-reference-based' as ExtensionTypes.ID; + +/* tslint:disable:no-unused-expression */ +describe('extensions/payment-network/reference-based', () => { + describe('createCreationAction', () => { + it('can createCreationAction with payment and refund', () => { + expect( + referenceBasedManager.createCreationAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + paymentAddress: DataCreate.paymentAddress, + refundAddress: DataCreate.refundAddress, + salt: DataCreate.salt, + }, + '0.1.0', + ), + 'extensionsdata is wrong', + ).to.deep.equal(DataCreate.actionCreationWithPaymentAndRefund); + }); + + it('can createCreationAction with paymentAddress', () => { + // deep copy to remove the undefined properties to comply deep.equal() + expect( + Utils.deepCopy( + referenceBasedManager.createCreationAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + paymentAddress: DataCreate.paymentAddress, + salt: DataCreate.salt, + }, + '0.1.0', + ), + ), + 'extensionsdata is wrong', + ).to.deep.equal(DataCreate.actionCreationOnlyPayment); + }); + it('can createCreationAction with refundAddress', () => { + // deep copy to remove the undefined properties to comply deep.equal() + expect( + Utils.deepCopy( + referenceBasedManager.createCreationAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + refundAddress: DataCreate.refundAddress, + salt: DataCreate.salt, + }, + '0.1.0', + ), + ), + 'extensionsdata is wrong', + ).to.deep.equal(DataCreate.actionCreationOnlyRefund); + }); + it('can createCreationAction with only salt', () => { + // deep copy to remove the undefined properties to comply deep.equal() + expect( + Utils.deepCopy( + referenceBasedManager.createCreationAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + salt: DataCreate.salt, + }, + '0.1.0', + ), + ), + 'extensionsdata is wrong', + ).to.deep.equal(DataCreate.actionCreationEmpty); + }); + it('prevent createCreationAction with no salt', () => { + expect(() => { + referenceBasedManager.createCreationAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + {} as ExtensionTypes.PnReferenceBased.ICreationParameters, + '0.1.0', + ); + }, 'must throw').to.throw('salt should not be empty'); + }); + it('prevent createCreationAction with invalid salt', () => { + expect(() => { + referenceBasedManager.createCreationAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + salt: DataCreate.invalidSalt, + }, + '0.1.0', + ); + }, 'must throw').to.throw( + `The salt must be a string of minimum 16 hexadecimal characters. Example: 'ea3bc7caf64110ca'`, + ); + }); + }); + + describe('createAddPaymentAddressAction', () => { + it('can createAddPaymentAddressAction', () => { + expect( + referenceBasedManager.createAddPaymentAddressAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + paymentAddress: DataAddPaymentAddress.paymentAddress, + }, + ), + 'extensionsdata is wrong', + ).to.deep.equal(DataAddPaymentAddress.actionAddPaymentAddress); + }); + }); + + describe('createAddRefundAddressAction', () => { + it('can createAddRefundAddressAction', () => { + expect( + referenceBasedManager.createAddRefundAddressAction( + PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + { + refundAddress: DataAddPaymentAddress.refundAddress, + }, + ), + 'extensionsdata is wrong', + ).to.deep.equal(DataAddPaymentAddress.actionAddRefundAddress); + }); + }); + + describe('applyActionToExtension', () => { + describe('applyActionToExtension/unknown action', () => { + it('cannot applyActionToExtensions of unknown action', () => { + const unknownAction = Utils.deepCopy(DataAddPaymentAddress.actionAddPaymentAddress); + unknownAction.action = 'unknown action'; + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateCreatedEmpty.extensions, + unknownAction, + DataCreate.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('Unknown action: unknown action'); + }); + }); + + describe('applyActionToExtension/create', () => { + it('can applyActionToExtensions of creation', () => { + expect( + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateNoExtensions.extensions, + DataCreate.actionCreationWithPaymentAndRefund, + DataCreate.requestStateNoExtensions, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ), + 'new extension state wrong', + ).to.deep.equal(DataCreate.extensionStateWithPaymentAndRefund); + }); + + it('cannot applyActionToExtensions of creation with a previous state', () => { + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateCreatedWithPaymentAndRefund.extensions, + DataCreate.actionCreationWithPaymentAndRefund, + DataCreate.requestStateCreatedWithPaymentAndRefund, + TestData.otherIdRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw('This extension has already been created'); + }); + }); + + describe('applyActionToExtension/addPaymentAddress', () => { + it('can applyActionToExtensions of addPaymentAddress', () => { + expect( + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateCreatedEmpty.extensions, + DataAddPaymentAddress.actionAddPaymentAddress, + DataCreate.requestStateCreatedEmpty, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ), + 'new extension state wrong', + ).to.deep.equal(DataAddPaymentAddress.extensionStateWithPaymentAfterCreation); + }); + it('cannot applyActionToExtensions of addPaymentAddress without a previous state', () => { + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateNoExtensions.extensions, + DataAddPaymentAddress.actionAddPaymentAddress, + DataCreate.requestStateNoExtensions, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw( + `The extension should be created before receiving any other action`, + ); + }); + it('cannot applyActionToExtensions of addPaymentAddress without a payee', () => { + const previousState = Utils.deepCopy(DataCreate.requestStateCreatedEmpty); + previousState.payee = undefined; + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + previousState.extensions, + DataAddPaymentAddress.actionAddPaymentAddress, + previousState, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The request must have a payee`); + }); + it('cannot applyActionToExtensions of addPaymentAddress signed by someone else than the payee', () => { + const previousState = Utils.deepCopy(DataCreate.requestStateCreatedEmpty); + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + previousState.extensions, + DataAddPaymentAddress.actionAddPaymentAddress, + previousState, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The signer must be the payee`); + }); + it('cannot applyActionToExtensions of addPaymentAddress with payment address already given', () => { + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateCreatedWithPaymentAndRefund.extensions, + DataAddPaymentAddress.actionAddPaymentAddress, + DataCreate.requestStateCreatedWithPaymentAndRefund, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`Payment address already given`); + }); + }); + + describe('applyActionToExtension/addRefundAddress', () => { + it('can applyActionToExtensions of addRefundAddress', () => { + expect( + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateCreatedEmpty.extensions, + DataAddPaymentAddress.actionAddRefundAddress, + DataCreate.requestStateCreatedEmpty, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ), + 'new extension state wrong', + ).to.deep.equal(DataAddPaymentAddress.extensionStateWithRefundAfterCreation); + }); + it('cannot applyActionToExtensions of addRefundAddress without a previous state', () => { + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateNoExtensions.extensions, + DataAddPaymentAddress.actionAddRefundAddress, + DataCreate.requestStateNoExtensions, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw( + `The extension should be created before receiving any other action`, + ); + }); + it('cannot applyActionToExtensions of addRefundAddress without a payer', () => { + const previousState = Utils.deepCopy(DataCreate.requestStateCreatedEmpty); + previousState.payer = undefined; + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + previousState.extensions, + DataAddPaymentAddress.actionAddRefundAddress, + previousState, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The request must have a payer`); + }); + it('cannot applyActionToExtensions of addRefundAddress signed by someone else than the payer', () => { + const previousState = Utils.deepCopy(DataCreate.requestStateCreatedEmpty); + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + previousState.extensions, + DataAddPaymentAddress.actionAddRefundAddress, + previousState, + TestData.payeeRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`The signer must be the payer`); + }); + it('cannot applyActionToExtensions of addRefundAddress with payment address already given', () => { + expect(() => { + referenceBasedManager.applyActionToExtension( + isValidAddressMock(), + DataCreate.requestStateCreatedWithPaymentAndRefund.extensions, + DataAddPaymentAddress.actionAddRefundAddress, + DataCreate.requestStateCreatedWithPaymentAndRefund, + TestData.payerRaw.identity, + TestData.arbitraryTimestamp, + ); + }, 'must throw').to.throw(`Refund address already given`); + }); + }); + }); +}); diff --git a/packages/advanced-logic/test/utils/payment-network/erc20/add-payment-address-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/erc20/address-based-add-payment-address-data-generator.ts similarity index 98% rename from packages/advanced-logic/test/utils/payment-network/erc20/add-payment-address-data-generator.ts rename to packages/advanced-logic/test/utils/payment-network/erc20/address-based-add-payment-address-data-generator.ts index b6fa7cb523..61a702e7b4 100644 --- a/packages/advanced-logic/test/utils/payment-network/erc20/add-payment-address-data-generator.ts +++ b/packages/advanced-logic/test/utils/payment-network/erc20/address-based-add-payment-address-data-generator.ts @@ -1,4 +1,4 @@ -import * as TestDataCreate from './create-data-generator'; +import * as TestDataCreate from './address-based-create-data-generator'; import * as TestData from '../../test-data-generator'; diff --git a/packages/advanced-logic/test/utils/payment-network/erc20/create-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/erc20/address-based-create-data-generator.ts similarity index 95% rename from packages/advanced-logic/test/utils/payment-network/erc20/create-data-generator.ts rename to packages/advanced-logic/test/utils/payment-network/erc20/address-based-create-data-generator.ts index 0fa424e62f..5797317272 100644 --- a/packages/advanced-logic/test/utils/payment-network/erc20/create-data-generator.ts +++ b/packages/advanced-logic/test/utils/payment-network/erc20/address-based-create-data-generator.ts @@ -15,8 +15,8 @@ export const actionCreationWithPaymentAndRefund = { action: 'create', id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED, parameters: { - paymentAddress: paymentAddress, - refundAddress: refundAddress, + paymentAddress, + refundAddress, }, version: '0.1.0', }; @@ -24,7 +24,7 @@ export const actionCreationOnlyPayment = { action: 'create', id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED, parameters: { - paymentAddress: paymentAddress, + paymentAddress, }, version: '0.1.0', }; @@ -32,7 +32,7 @@ export const actionCreationOnlyRefund = { action: 'create', id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED, parameters: { - refundAddress: refundAddress, + refundAddress, }, version: '0.1.0', }; @@ -51,8 +51,8 @@ export const extensionStateWithPaymentAndRefund = { { name: 'create', parameters: { - paymentAddress: paymentAddress, - refundAddress: refundAddress, + paymentAddress, + refundAddress, }, timestamp: arbitraryTimestamp, }, @@ -60,8 +60,8 @@ export const extensionStateWithPaymentAndRefund = { id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED, type: ExtensionTypes.TYPE.PAYMENT_NETWORK, values: { - paymentAddress: paymentAddress, - refundAddress: refundAddress, + paymentAddress, + refundAddress, }, version: '0.1.0', }, diff --git a/packages/advanced-logic/test/utils/payment-network/erc20/proxy-contract-add-payment-address-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/erc20/proxy-contract-add-payment-address-data-generator.ts new file mode 100644 index 0000000000..7d22ebf98b --- /dev/null +++ b/packages/advanced-logic/test/utils/payment-network/erc20/proxy-contract-add-payment-address-data-generator.ts @@ -0,0 +1,168 @@ +import * as TestDataCreate from './proxy-contract-create-data-generator'; + +import * as TestData from '../../test-data-generator'; + +import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; + +export const arbitraryTimestamp = 1544426030; + +// --------------------------------------------------------------------- +// Mock addresses for testing generic address based payment networks +export const paymentAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; +export const refundAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; +export const invalidAddress = '0xnotandaddress'; +// --------------------------------------------------------------------- +export const salt = 'ea3bc7caf64110ca'; +// actions +export const actionAddPaymentAddress = { + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress, + }, +}; +export const actionAddRefundAddress = { + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + refundAddress, + }, +}; + +// --------------------------------------------------------------------- +// extensions states +export const extensionStateWithPaymentAfterCreation = { + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT as string]: { + events: [ + { + name: ExtensionTypes.PnReferenceBased.ACTION.CREATE, + parameters: {}, + timestamp: arbitraryTimestamp, + }, + { + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + parameters: { + paymentAddress, + }, + timestamp: arbitraryTimestamp, + }, + ], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + }, + version: '0.1.0', + }, +}; + +export const extensionStateWithRefundAfterCreation = { + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT as string]: { + events: [ + { + name: ExtensionTypes.PnReferenceBased.ACTION.CREATE, + parameters: {}, + timestamp: arbitraryTimestamp, + }, + { + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + parameters: { + refundAddress, + }, + timestamp: arbitraryTimestamp, + }, + ], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + refundAddress, + }, + version: '0.1.0', + }, +}; + +// --------------------------------------------------------------------- +// request states +export const requestStateCreatedEmptyThenAddPayment: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', // SAI + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 2, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateWithPaymentAfterCreation, + extensionsData: [TestDataCreate.actionCreationEmpty, actionAddPaymentAddress], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; + +export const requestStateCreatedEmptyThenAddRefund: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', // SAI + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 2, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateWithRefundAfterCreation, + extensionsData: [TestDataCreate.actionCreationEmpty, actionAddRefundAddress], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; diff --git a/packages/advanced-logic/test/utils/payment-network/erc20/proxy-contract-create-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/erc20/proxy-contract-create-data-generator.ts new file mode 100644 index 0000000000..e7e304071f --- /dev/null +++ b/packages/advanced-logic/test/utils/payment-network/erc20/proxy-contract-create-data-generator.ts @@ -0,0 +1,215 @@ +import * as TestData from '../../test-data-generator'; + +import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; + +export const arbitraryTimestamp = 1544426030; + +// --------------------------------------------------------------------- +// Mock addresses for testing ETH payment networks +export const paymentAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; +export const refundAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; +export const invalidAddress = '0xnotandaddress'; +// --------------------------------------------------------------------- +export const salt = 'ea3bc7caf64110ca'; +// actions +export const actionCreationWithPaymentAndRefund = { + action: 'create', + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress, + refundAddress, + salt, + }, + version: '0.1.0', +}; +export const actionCreationOnlyPayment = { + action: 'create', + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress, + }, + version: '0.1.0', +}; +export const actionCreationOnlyRefund = { + action: 'create', + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: { + refundAddress, + }, + version: '0.1.0', +}; +export const actionCreationEmpty = { + action: 'create', + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + parameters: {}, + version: '0.1.0', +}; + +// --------------------------------------------------------------------- +// extensions states +export const extensionStateWithPaymentAndRefund = { + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT as string]: { + events: [ + { + name: 'create', + parameters: { + paymentAddress, + refundAddress, + salt, + }, + timestamp: arbitraryTimestamp, + }, + ], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + refundAddress, + salt, + }, + version: '0.1.0', + }, +}; +export const extensionStateCreatedEmpty = { + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT as string]: { + events: [ + { + name: 'create', + parameters: {}, + timestamp: arbitraryTimestamp, + }, + ], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '0.1.0', + }, +}; + +// --------------------------------------------------------------------- +// request states +export const requestStateNoExtensions: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', // SAI + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 0, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: {}, + extensionsData: [], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; + +export const requestStateCreatedWithPaymentAndRefund: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', // SAI + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 1, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateWithPaymentAndRefund, + extensionsData: [actionCreationWithPaymentAndRefund], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; + +export const requestStateCreatedEmpty: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', // SAI + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 1, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateCreatedEmpty, + extensionsData: [actionCreationEmpty], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; diff --git a/packages/advanced-logic/test/utils/payment-network/ethereum/add-payment-address-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/ethereum/add-payment-address-data-generator.ts index 98082ef7d2..f36ff2c677 100644 --- a/packages/advanced-logic/test/utils/payment-network/ethereum/add-payment-address-data-generator.ts +++ b/packages/advanced-logic/test/utils/payment-network/ethereum/add-payment-address-data-generator.ts @@ -15,14 +15,14 @@ export const invalidAddress = '0xnotandaddress'; export const salt = 'ea3bc7caf64110ca'; // actions export const actionAddPaymentAddress = { - action: ExtensionTypes.PnAddressBased.ACTION.ADD_PAYMENT_ADDRESS, + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, parameters: { paymentAddress, }, }; export const actionAddRefundAddress = { - action: ExtensionTypes.PnAddressBased.ACTION.ADD_REFUND_ADDRESS, + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, parameters: { refundAddress, @@ -35,12 +35,12 @@ export const extensionStateWithPaymentAfterCreation = { [ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA as string]: { events: [ { - name: ExtensionTypes.PnAddressBased.ACTION.CREATE, + name: ExtensionTypes.PnReferenceBased.ACTION.CREATE, parameters: {}, timestamp: arbitraryTimestamp, }, { - name: ExtensionTypes.PnAddressBased.ACTION.ADD_PAYMENT_ADDRESS, + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, parameters: { paymentAddress, }, @@ -60,12 +60,12 @@ export const extensionStateWithRefundAfterCreation = { [ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA as string]: { events: [ { - name: ExtensionTypes.PnAddressBased.ACTION.CREATE, + name: ExtensionTypes.PnReferenceBased.ACTION.CREATE, parameters: {}, timestamp: arbitraryTimestamp, }, { - name: ExtensionTypes.PnAddressBased.ACTION.ADD_REFUND_ADDRESS, + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, parameters: { refundAddress, }, diff --git a/packages/advanced-logic/test/utils/payment-network/reference-based-add-payment-address-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/reference-based-add-payment-address-data-generator.ts new file mode 100644 index 0000000000..f8fef9e36f --- /dev/null +++ b/packages/advanced-logic/test/utils/payment-network/reference-based-add-payment-address-data-generator.ts @@ -0,0 +1,170 @@ +import * as TestDataCreate from './reference-based-data-generator'; + +import * as TestData from '../test-data-generator'; + +import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; + +export const arbitraryTimestamp = 1544426030; + +const PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED = 'do-not-use!-pn-test-reference-based' as ExtensionTypes.ID; +// --------------------------------------------------------------------- +// Mock addresses for testing generic address based payment networks +export const paymentAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; +export const refundAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; + +export const salt = 'ea3bc7caf64110ca'; + +// --------------------------------------------------------------------- +// actions +export const actionAddPaymentAddress = { + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + parameters: { + paymentAddress, + }, +}; +export const actionAddRefundAddress = { + action: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + parameters: { + refundAddress, + }, +}; + +// --------------------------------------------------------------------- +// extensions states +export const extensionStateWithPaymentAfterCreation = { + [PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED as string]: { + events: [ + { + name: ExtensionTypes.PnReferenceBased.ACTION.CREATE, + parameters: { salt }, + timestamp: arbitraryTimestamp, + }, + { + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_PAYMENT_ADDRESS, + parameters: { + paymentAddress, + }, + timestamp: arbitraryTimestamp, + }, + ], + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + salt, + }, + version: '0.1.0', + }, +}; + +export const extensionStateWithRefundAfterCreation = { + [PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED as string]: { + events: [ + { + name: ExtensionTypes.PnReferenceBased.ACTION.CREATE, + parameters: { salt }, + timestamp: arbitraryTimestamp, + }, + { + name: ExtensionTypes.PnReferenceBased.ACTION.ADD_REFUND_ADDRESS, + parameters: { + refundAddress, + }, + timestamp: arbitraryTimestamp, + }, + ], + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + refundAddress, + salt, + }, + version: '0.1.0', + }, +}; + +// --------------------------------------------------------------------- +// request states +export const requestStateCreatedEmptyThenAddPayment: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'EUR', + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 2, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateWithPaymentAfterCreation, + extensionsData: [TestDataCreate.actionCreationEmpty, actionAddPaymentAddress], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; + +export const requestStateCreatedEmptyThenAddRefund: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'EUR', + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 2, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateWithRefundAfterCreation, + extensionsData: [TestDataCreate.actionCreationEmpty, actionAddRefundAddress], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; diff --git a/packages/advanced-logic/test/utils/payment-network/reference-based-data-generator.ts b/packages/advanced-logic/test/utils/payment-network/reference-based-data-generator.ts new file mode 100644 index 0000000000..24de03ee10 --- /dev/null +++ b/packages/advanced-logic/test/utils/payment-network/reference-based-data-generator.ts @@ -0,0 +1,219 @@ +import * as TestData from '../test-data-generator'; + +import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; + +export const arbitraryTimestamp = 1544426030; + +const PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED = 'do-not-use!-pn-test-reference-based' as ExtensionTypes.ID; +// --------------------------------------------------------------------- +// Mock addresses for testing generic address based payment networks +export const paymentAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; +export const refundAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; + +export const salt = 'ea3bc7caf64110ca'; + +// This salt is not valid, it contains a 'z' +export const invalidSalt = 'ea3bc7czf64110ca'; + +// --------------------------------------------------------------------- +// actions +export const actionCreationWithPaymentAndRefund = { + action: 'create', + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + parameters: { + paymentAddress, + refundAddress, + salt, + }, + version: '0.1.0', +}; +export const actionCreationOnlyPayment = { + action: 'create', + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + parameters: { + paymentAddress, + salt, + }, + version: '0.1.0', +}; +export const actionCreationOnlyRefund = { + action: 'create', + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + parameters: { + refundAddress, + salt, + }, + version: '0.1.0', +}; +export const actionCreationEmpty = { + action: 'create', + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + parameters: { salt }, + version: '0.1.0', +}; + +// --------------------------------------------------------------------- +// extensions states +export const extensionStateWithPaymentAndRefund = { + [PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED as string]: { + events: [ + { + name: 'create', + parameters: { + paymentAddress, + refundAddress, + salt, + }, + timestamp: arbitraryTimestamp, + }, + ], + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + refundAddress, + salt, + }, + version: '0.1.0', + }, +}; +export const extensionStateCreatedEmpty = { + [PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED as string]: { + events: [ + { + name: 'create', + parameters: { salt }, + timestamp: arbitraryTimestamp, + }, + ], + id: PAYMENT_NETWORK_TEST_GENERIC_REFERENCE_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { salt }, + version: '0.1.0', + }, +}; + +// --------------------------------------------------------------------- +// request states +export const requestStateNoExtensions: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'EUR', + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 0, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: {}, + extensionsData: [], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; + +export const requestStateCreatedWithPaymentAndRefund: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'EUR', + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 1, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateWithPaymentAndRefund, + extensionsData: [actionCreationWithPaymentAndRefund], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; + +export const requestStateCreatedEmpty: RequestLogicTypes.IRequest = { + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'EUR', + }, + events: [ + { + actionSigner: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 1, + isSignedRequest: false, + }, + timestamp: arbitraryTimestamp, + }, + ], + expectedAmount: TestData.arbitraryExpectedAmount, + extensions: extensionStateCreatedEmpty, + extensionsData: [actionCreationEmpty], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payerRaw.address, + }, + requestId: TestData.requestIdMock, + state: RequestLogicTypes.STATE.CREATED, + timestamp: TestData.arbitraryTimestamp, + version: '0.1.0', +}; diff --git a/packages/data-access/.vscode/settings.json b/packages/data-access/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/data-access/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/data-access/CHANGELOG.md b/packages/data-access/CHANGELOG.md index f4a4411d2f..de48bf78d9 100644 --- a/packages/data-access/CHANGELOG.md +++ b/packages/data-access/CHANGELOG.md @@ -3,6 +3,352 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.11.0) (2020-06-29) + + +### Bug Fixes + +* don't remove failed transactions from data-access ([#236](https://github.com/RequestNetwork/requestNetwork/issues/236)) ([74835f0](https://github.com/RequestNetwork/requestNetwork/commit/74835f0890de5816d0d29c43c1c253ecd756bd6e)) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) +* add the synchronization of ignored data in data access ([#214](https://github.com/RequestNetwork/requestNetwork/issues/214)) ([7f8e668](https://github.com/RequestNetwork/requestNetwork/commit/7f8e6685a20b6d9057d2224213fb2ed75bae168f)) +* remove hash in encrypted transaction ([#232](https://github.com/RequestNetwork/requestNetwork/issues/232)) ([d58f101](https://github.com/RequestNetwork/requestNetwork/commit/d58f101f9f76e408671dd1edb0d67863d1c8abd5)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.10.0) (2020-05-04) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.9.0) (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.8.0) (2020-04-06) + + +### Features + +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.7.0) (2020-03-23) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.6.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.5.5) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/data-access + + + + + +## [0.5.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.5.4) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/data-access + + + + + +## [0.5.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.2...@requestnetwork/data-access@0.5.3) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/data-access + + + + + ## [0.5.2](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-access@0.5.1...@requestnetwork/data-access@0.5.2) (2019-11-20) **Note:** Version bump only for package @requestnetwork/data-access diff --git a/packages/data-access/package.json b/packages/data-access/package.json index b2ca1a2604..cf6a74eb3f 100644 --- a/packages/data-access/package.json +++ b/packages/data-access/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/data-access", - "version": "0.5.2", + "version": "0.11.0", "publishConfig": { "access": "public" }, @@ -32,48 +32,48 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "stryker": "stryker run", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "bluebird": "3.5.5", "keyv": "3.1.0" }, "devDependencies": { - "@stryker-mutator/core": "2.1.0", + "@stryker-mutator/core": "2.4.0", "@stryker-mutator/html-reporter": "2.1.0", "@stryker-mutator/mocha-framework": "2.1.0", "@stryker-mutator/mocha-runner": "2.1.0", - "@stryker-mutator/typescript": "2.1.0", - "@types/bluebird": "3.5.27", + "@stryker-mutator/typescript": "2.3.0", + "@types/bluebird": "3.5.29", "@types/chai": "4.1.7", "@types/chai-spies": "1.0.0", "@types/keyv": "3.1.0", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@types/node": "12.0.3", - "@types/sinon": "7.0.5", + "@types/sinon": "7.5.0", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", - "sinon": "7.2.3", + "sinon": "7.5.0", "source-map-support": "0.5.13", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" }, diff --git a/packages/data-access/src/block.ts b/packages/data-access/src/block.ts index 7bef9e326d..4b4036953d 100644 --- a/packages/data-access/src/block.ts +++ b/packages/data-access/src/block.ts @@ -59,10 +59,9 @@ function parseBlock(data: string): DataAccessTypes.IBlock { } // check the transactions format - if (maybeBlock.transactions.some((tx: any) => !tx.data)) { + if (!maybeBlock.transactions.every((tx: any) => tx.data || tx.encryptedData)) { throw new Error(`Transactions do not follow the block standard`); } - // check if channelIds are well formatted // check that all the channel ids are hashes if ( @@ -122,10 +121,8 @@ function pushTransaction( channelId: string, topics: string[] = [], ): DataAccessTypes.IBlock { - if (transaction.data === undefined && !(transaction.encryptedData && transaction.hash)) { - throw new Error( - 'The transaction is missing the data property or encryptedData and hash property', - ); + if (transaction.data === undefined && !transaction.encryptedData) { + throw new Error('The transaction is missing the data property or encryptedData property'); } // we don't want to modify the original block state const copiedBlock: DataAccessTypes.IBlock = Utils.deepCopy(block); diff --git a/packages/data-access/src/data-access.ts b/packages/data-access/src/data-access.ts index d3d157794e..4127bc330e 100644 --- a/packages/data-access/src/data-access.ts +++ b/packages/data-access/src/data-access.ts @@ -3,8 +3,10 @@ import { DataAccessTypes, LogTypes, StorageTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import * as Bluebird from 'bluebird'; +import { EventEmitter } from 'events'; import Block from './block'; +import IgnoredLocationIndex from './ignored-location'; import IntervalTimer from './interval-timer'; import TransactionIndex from './transaction-index'; @@ -30,6 +32,11 @@ export interface IDataAccessOptions { * Defaults to DEFAULT_INTERVAL_TIME. */ synchronizationIntervalTime?: number; + + /** + * Index of the ignored location with the reason + */ + ignoredLocationIndex?: IgnoredLocationIndex; } /** @@ -39,11 +46,14 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { // Transaction index, that allows storing and retrieving transactions by channel or topic, with time boundaries. // public for test purpose public transactionIndex: DataAccessTypes.ITransactionIndex; + // boolean to store the initialization state protected isInitialized: boolean = false; // Storage layer private storage: StorageTypes.IStorage; + private ignoredLocationIndex: IgnoredLocationIndex; + // The function used to synchronize with the storage should be called periodically // This object allows to handle the periodical call of the function private synchronizationTimer: IntervalTimer; @@ -69,6 +79,7 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { */ public constructor(storage: StorageTypes.IStorage, options?: IDataAccessOptions) { const defaultOptions: IDataAccessOptions = { + ignoredLocationIndex: new IgnoredLocationIndex(), logger: new Utils.SimpleLogger(), synchronizationIntervalTime: DEFAULT_INTERVAL_TIME, transactionIndex: new TransactionIndex(), @@ -86,6 +97,7 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { 5, ); this.transactionIndex = options.transactionIndex!; + this.ignoredLocationIndex = options.ignoredLocationIndex!; this.logger = options.logger!; } @@ -161,9 +173,43 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { channelId, topics, ); + // get the topic of the data in storage const resultAppend = await this.storage.append(JSON.stringify(updatedBlock)); + const result: DataAccessTypes.IReturnPersistTransaction = Object.assign(new EventEmitter(), { + meta: { + storageMeta: resultAppend.meta, + topics, + transactionStorageLocation: resultAppend.id, + }, + result: {}, + }); + + // Store the data to the real storage + resultAppend + .on('confirmed', async (resultAppendConfirmed: StorageTypes.IAppendResult) => { + // update the timestamp with the confirmed one + await this.transactionIndex.updateTimestamp( + resultAppendConfirmed.id, + resultAppendConfirmed.meta.timestamp, + ); + + const resultAfterConfirmation = { + meta: { + storageMeta: resultAppendConfirmed.meta, + topics, + transactionStorageLocation: resultAppendConfirmed.id, + }, + result: {}, + }; + + result.emit('confirmed', resultAfterConfirmation); + }) + .on('error', async error => { + result.emit('error', error); + }); + // adds this transaction to the index, to enable retrieving it later. await this.transactionIndex.addTransaction( resultAppend.id, @@ -171,14 +217,7 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { resultAppend.meta.timestamp, ); - return { - meta: { - storageMeta: resultAppend.meta, - topics, - transactionStorageLocation: resultAppend.id, - }, - result: {}, - }; + return result; } /** @@ -202,12 +241,13 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { channelId, timestampBoundaries, ); + // Gets the block and meta from the storage location const blockWithMetaList = await this.getBlockAndMetaFromStorageLocation(storageLocationList); // Get the transactions (and the meta) indexed by channelIds in the blocks found const transactionsAndMetaPerBlocks: Array<{ - transactions: DataAccessTypes.IConfirmedTransaction[]; + transactions: DataAccessTypes.ITimestampedTransaction[]; transactionsStorageLocation: string[]; storageMeta: string[]; }> = @@ -386,9 +426,12 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { to: synchronizationTo, }); + // Try to get some data previously ignored + const oldEntriesWithMeta = await this.storage.getIgnoredData(); + // check if the data returned by getNewDataId are correct // if yes, the dataIds are indexed with LocationByTopic - await this.pushLocationsWithTopics(newDataWithMeta.entries); + await this.pushLocationsWithTopics(newDataWithMeta.entries.concat(oldEntriesWithMeta)); // The last synced timestamp is the latest one returned by storage this.lastSyncStorageTimestamp = newDataWithMeta.lastTimestamp; @@ -410,6 +453,37 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { this.synchronizationTimer.stop(); } + /** + * Gets information of the data indexed + * + * @param detailed if true get the list of the files hashes + */ + public async _getStatus(detailed: boolean = false): Promise { + this.checkInitialized(); + + // last transaction timestamp retrieved + const lastLocationTimestamp = await this.transactionIndex.getLastTransactionTimestamp(); + const listIndexedLocation = await this.transactionIndex.getIndexedLocations(); + const listIgnoredLocationIndex = await this.ignoredLocationIndex.getIgnoredLocations(); + + const synchronizationConfig = this.synchronizationTimer.getConfig(); + + return { + filesIgnored: { + count: Object.keys(listIgnoredLocationIndex).length, + list: detailed ? listIgnoredLocationIndex : undefined, + }, + filesRetrieved: { + count: listIndexedLocation.length, + lastTimestamp: lastLocationTimestamp, + list: detailed ? listIndexedLocation : undefined, + }, + lastSynchronizationTimestamp: this.lastSyncStorageTimestamp, + storage: await this.storage._getStatus(detailed), + synchronizationConfig, + }; + } + /** * Check the format of the data, extract the topics from it and push location indexed with the topics * @@ -438,6 +512,8 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { await this.transactionIndex.addTransaction(entry.id, block.header, entry.meta.timestamp); } catch (e) { parsingErrorCount++; + // Index ignored Location + await this.ignoredLocationIndex.pushReasonByLocation(entry.id, e.message); this.logger.debug(`Error: can't parse content of the dataId (${entry.id}): ${e}`, [ 'synchronization', ]); @@ -467,7 +543,7 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { const resultRead = await this.storage.read(location); return { - block: JSON.parse(resultRead.content), + block: Block.parseBlock(resultRead.content), location, meta: resultRead.meta, }; @@ -490,16 +566,20 @@ export default class DataAccess implements DataAccessTypes.IDataAccess { location: string, meta: StorageTypes.IEntryMetadata, ): { - transactions: DataAccessTypes.IConfirmedTransaction[]; + transactions: DataAccessTypes.ITimestampedTransaction[]; transactionsStorageLocation: string[]; storageMeta: string[]; } { // Gets the transaction from the positions - const transactions: DataAccessTypes.IConfirmedTransaction[] = + const transactions: DataAccessTypes.ITimestampedTransaction[] = // first remove de duplicates Utils.unique(transactionPositions).uniqueItems.map( // Get the transaction from their position and add the timestamp (position: number) => ({ + state: + meta.state === StorageTypes.ContentState.CONFIRMED + ? DataAccessTypes.TransactionState.CONFIRMED + : DataAccessTypes.TransactionState.PENDING, timestamp: meta.timestamp, transaction: block.transactions[position], }), diff --git a/packages/data-access/src/ignored-location.ts b/packages/data-access/src/ignored-location.ts new file mode 100644 index 0000000000..416f6d5c18 --- /dev/null +++ b/packages/data-access/src/ignored-location.ts @@ -0,0 +1,104 @@ +import * as Keyv from 'keyv'; + +/** + * Interface for reason from location + */ +export interface IReasonByIgnoredLocation { + [location: string]: string; +} +/** + * Class used to store the block's reason indexed by location of blocks + */ +export default class ReasonsByIgnoredLocationIndex { + /** + * reason by location + * maps dataId => reason + */ + private reasonsByIgnoredLocation: Keyv; + + private listIgnoredLocation: Keyv; + + /** + * reasonByLocationTransactionIndex constructor + * @param store a Keyv store to persist the index to + */ + public constructor(store?: Keyv.Store) { + this.reasonsByIgnoredLocation = new Keyv({ + namespace: 'reasonsByIgnoredLocation', + store, + }); + + this.listIgnoredLocation = new Keyv({ + namespace: 'listIgnoredLocation', + store, + }); + } + + /** + * Function to push reason indexed by location + * + * @param dataId dataId of the block + * @param reason reason to be ignored + */ + public async pushReasonByLocation(dataId: string, reason: string): Promise { + if (!(await this.reasonsByIgnoredLocation.get(dataId))) { + await this.reasonsByIgnoredLocation.set(dataId, reason); + await this.updateDataId(dataId); + } + } + + /** + * Function to update reason indexed by location + * + * @param dataId dataId of the block + * @param reason reason to be ignored + */ + public async removeReasonByLocation(dataId: string): Promise { + await this.reasonsByIgnoredLocation.delete(dataId); + } + + /** + * Function to get reason from location + * + * @param dataId location to get the reason from + * @returns reason of the location, null if not found + */ + public async getReasonFromLocation(dataId: string): Promise { + const reason: string | undefined = await this.reasonsByIgnoredLocation.get(dataId); + return reason ? reason : null; + } + + /** + * Get the list of data ids stored + * + * @returns the list of data ids stored + */ + public async getIgnoredLocations(): Promise { + const listDataId: string[] | undefined = await this.listIgnoredLocation.get('list'); + + if (!listDataId) { + return {}; + } + const result: any = {}; + for (const dataId of Array.from(listDataId)) { + result[dataId] = await this.reasonsByIgnoredLocation.get(dataId); + } + + return result; + } + + /** + * Update the list of data ids stored + * + * @param dataId data id to add to the list + * @returns + */ + private async updateDataId(dataId: string): Promise { + let listDataIds: string[] | undefined = await this.listIgnoredLocation.get('list'); + if (!listDataIds) { + listDataIds = []; + } + listDataIds.push(dataId); + await this.listIgnoredLocation.set('list', listDataIds); + } +} diff --git a/packages/data-access/src/index.ts b/packages/data-access/src/index.ts index 884da3c466..59a666cdaa 100644 --- a/packages/data-access/src/index.ts +++ b/packages/data-access/src/index.ts @@ -1,2 +1,3 @@ export { default as DataAccess } from './data-access'; -export {default as TransactionIndex } from './transaction-index'; +export { default as TransactionIndex } from './transaction-index'; +export { default as Block } from './block'; diff --git a/packages/data-access/src/interval-timer.ts b/packages/data-access/src/interval-timer.ts index e613776135..c73467ca10 100644 --- a/packages/data-access/src/interval-timer.ts +++ b/packages/data-access/src/interval-timer.ts @@ -89,4 +89,16 @@ export default class IntervalTimer { clearTimeout(this.timeoutObject); this.timeoutObject = null; } + + /** + * Gets current configuration + * + * @return the current configuration attributes + */ + public getConfig(): any { + return { + intervalTime: this.intervalTime, + successiveFailureThreshold: this.successiveFailureThreshold, + }; + } } diff --git a/packages/data-access/src/transaction-index/timestamp-by-location.ts b/packages/data-access/src/transaction-index/timestamp-by-location.ts index 9a039a0728..47a991cf8d 100644 --- a/packages/data-access/src/transaction-index/timestamp-by-location.ts +++ b/packages/data-access/src/transaction-index/timestamp-by-location.ts @@ -31,7 +31,7 @@ export default class TimestampByLocationTransactionIndex { */ public async pushTimestampByLocation(dataId: string, timestamp: number): Promise { if (!(await this.timestampByLocation.get(dataId))) { - await this.timestampByLocation!.set(dataId, timestamp); + await this.timestampByLocation.set(dataId, timestamp); } const lastTransactionTimestamp = await this.getLastTransactionTimestamp(); if (!lastTransactionTimestamp || timestamp > lastTransactionTimestamp) { @@ -39,6 +39,30 @@ export default class TimestampByLocationTransactionIndex { } } + /** + * Removes timestamp indexed by location + * + * @param dataId dataId of the block + */ + public async removeIndexedDataId(dataId: string): Promise { + await this.timestampByLocation.delete(dataId); + } + + /** + * Function to update timestamp indexed by location + * + * @param dataId dataId of the block + * @param timestamp timestamp of the block + */ + public async updateTimestampByLocation(dataId: string, timestamp: number): Promise { + await this.timestampByLocation.set(dataId, timestamp); + + const lastTransactionTimestamp = await this.getLastTransactionTimestamp(); + if (!lastTransactionTimestamp || timestamp > lastTransactionTimestamp) { + await this.setLastTransactionTimestamp(timestamp); + } + } + /** * Function to get timestamp from location * @@ -47,7 +71,7 @@ export default class TimestampByLocationTransactionIndex { */ public async getTimestampFromLocation(dataId: string): Promise { const timestamp = await this.timestampByLocation.get(dataId); - return timestamp ? timestamp : null; + return timestamp !== undefined ? timestamp : null; } /** diff --git a/packages/data-access/src/transaction-index/transaction-index.ts b/packages/data-access/src/transaction-index/transaction-index.ts index eee1f2452c..7a9be5c8a4 100644 --- a/packages/data-access/src/transaction-index/transaction-index.ts +++ b/packages/data-access/src/transaction-index/transaction-index.ts @@ -19,13 +19,20 @@ export default class TransactionIndex implements DataAccessTypes.ITransactionInd // Will be used to get the data from timestamp boundaries private timestampByLocation: TimestampByLocation; + private indexedLocation: Keyv; + /** * Constructor of TransactionIndex - * @param store a Keyv store to persist the index to + * @param store a Keyv store to persist the index */ constructor(store?: Keyv.Store) { this.timestampByLocation = new TimestampByLocation(store); this.locationByTopic = new LocationByTopic(store); + + this.indexedLocation = new Keyv({ + namespace: 'indexedLocation', + store, + }); } // tslint:disable-next-line: no-empty @@ -58,6 +65,36 @@ export default class TransactionIndex implements DataAccessTypes.ITransactionInd // add the timestamp in the index await this.timestampByLocation.pushTimestampByLocation(dataId, timestamp); + + await this.updateIndexedLocation(dataId); + } + + /** + * Removes a transaction from the index + * + * @param dataId the dataId to remove + */ + public async removeTransaction(dataId: string): Promise { + if (!this.locationByTopic) { + throw new Error('TransactionIndex must be initialized'); + } + + // remove the timestamp in the index + await this.timestampByLocation.removeIndexedDataId(dataId); + } + + /** + * Update timestamp for a dataId + * + * @param dataId the dataId to index + * @param timestamp the timestamp of the transaction + */ + public async updateTimestamp(dataId: string, timestamp: number): Promise { + if (!this.timestampByLocation) { + throw new Error('TransactionIndex must be initialized'); + } + // update the timestamp in the index + await this.timestampByLocation.updateTimestampByLocation(dataId, timestamp); } /** @@ -79,12 +116,16 @@ export default class TransactionIndex implements DataAccessTypes.ITransactionInd channelId, ); - // if boundaries are passed, only return locations of transaction within these boundaries - if (timestampBoundaries) { - storageLocationList = await Bluebird.filter(storageLocationList, (dataId: string) => - this.timestampByLocation.isDataInBoundaries(dataId, timestampBoundaries), - ); - } + storageLocationList = await Bluebird.filter(storageLocationList, async (dataId: string) => { + // if the dataId has not been suppressed + const exist: boolean = + (await this.timestampByLocation.getTimestampFromLocation(dataId)) !== null; + // if boundaries are passed, only return locations of transaction within these boundaries + const inTimeBoundaries: boolean = + !timestampBoundaries || + (await this.timestampByLocation.isDataInBoundaries(dataId, timestampBoundaries)); + return exist && inTimeBoundaries; + }); return storageLocationList; } @@ -168,4 +209,31 @@ export default class TransactionIndex implements DataAccessTypes.ITransactionInd return channelIds; } } + + /** + * the list of indexed locations + */ + public async getIndexedLocations(): Promise { + const listDataIds: string[] | undefined = await this.indexedLocation.get('list'); + return listDataIds || []; + } + + /** + * Update the list of data ids stored + * + * @param dataId data id to add to the list + * @returns + */ + private async updateIndexedLocation(dataId: string): Promise { + let listDataIds: string[] | undefined = await this.indexedLocation.get('list'); + if (!listDataIds) { + listDataIds = []; + } + + // push it if not already done + if (!listDataIds.includes(dataId)) { + listDataIds.push(dataId); + await this.indexedLocation.set('list', listDataIds); + } + } } diff --git a/packages/data-access/test/data-access.test.ts b/packages/data-access/test/data-access.test.ts index 25b2f7f7fe..e50f3fb5fa 100644 --- a/packages/data-access/test/data-access.test.ts +++ b/packages/data-access/test/data-access.test.ts @@ -1,6 +1,8 @@ import 'mocha'; import * as sinon from 'sinon'; +import { EventEmitter } from 'events'; + const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); const spies = require('chai-spies'); @@ -33,6 +35,10 @@ const transactionDataMock2String = JSON.stringify({ attribut1: 'foo', attribut2: 'bar', }); +const transactionDataMock3String = JSON.stringify({ + attribut1: 'jean', + attribut2: 'bon', +}); const transactionMock1: DataAccessTypes.ITransaction = { data: transactionDataMock1String, @@ -40,6 +46,9 @@ const transactionMock1: DataAccessTypes.ITransaction = { const transactionMock2: DataAccessTypes.ITransaction = { data: transactionDataMock2String, }; +const transactionMock3: DataAccessTypes.ITransaction = { + data: transactionDataMock3String, +}; const arbitraryId1 = '011111111111111111111111111111111111111111111111111111111111111111'; const arbitraryId2 = '012222222222222222222222222222222222222222222222222222222222222222'; @@ -61,6 +70,13 @@ const blockWith2tx = RequestDataAccessBlock.pushTransaction( [arbitraryTopic2], ); +const blockWith1txBis = RequestDataAccessBlock.pushTransaction( + emptyblock, + transactionMock3, + arbitraryId1, + [arbitraryTopic1], +); + const dataIdBlock2tx = 'dataIdBlock2tx'; const getDataResult: StorageTypes.IEntriesWithLastTimestamp = { @@ -68,18 +84,37 @@ const getDataResult: StorageTypes.IEntriesWithLastTimestamp = { { content: JSON.stringify(blockWith2tx), id: dataIdBlock2tx, - meta: { timestamp: 10 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 10 }, }, ], lastTimestamp: 0, }; -const appendResult: StorageTypes.IEntry = { +const dataIdBlock1txBis = 'dataIdBlock1txBis'; +const getIgnoredDataResult: StorageTypes.IEntry[] = [ + { + content: JSON.stringify(blockWith1txBis), + id: dataIdBlock1txBis, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 1 }, + }, +]; + +const appendResult: any = { + content: '', + id: dataIdBlock2tx, meta: { + state: StorageTypes.ContentState.PENDING, timestamp: 1, }, - id: dataIdBlock2tx, +}; + +const appendResultConfirmed = { content: '', + id: dataIdBlock2tx, + meta: { + state: StorageTypes.ContentState.CONFIRMED, + timestamp: 1, + }, }; const emptyDataResult: StorageTypes.IEntriesWithLastTimestamp = { @@ -92,8 +127,27 @@ const defaultTestData: Promise = Promise ); const defaultFakeStorage: StorageTypes.IStorage = { - append: chai.spy.returns(appendResult), + _getStatus: chai.spy( + (): any => ({ + fake: 'status', + }), + ), + _ipfsAdd: chai.spy(), + append: chai.spy( + (): any => { + const appendResultWithEvent = Object.assign(new EventEmitter(), appendResult); + setTimeout( + () => { + appendResultWithEvent.emit('confirmed', appendResultConfirmed); + }, + // tslint:disable-next-line:no-magic-numbers + 10, + ); + return appendResultWithEvent; + }, + ), getData: (): Promise => defaultTestData, + getIgnoredData: async (): Promise => [], initialize: chai.spy(), read: (param: string): any => { const dataIdBlock2txFake: any = { @@ -118,6 +172,10 @@ describe('data-access', () => { clock = sinon.useFakeTimers(); }); + // afterEach(async () => { + // sinon.restore(); + // }); + describe('constructor', () => { it('cannot initialize with getData without result', async () => { const customFakeStorage = { @@ -149,7 +207,7 @@ describe('data-access', () => { const dataIdBlock2txFake: StorageTypes.IEntry = { content: JSON.stringify(blockWith2tx), id: '1', - meta: { timestamp: 1 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 1 }, }; const result: any = { dataIdBlock2tx: dataIdBlock2txFake, @@ -176,7 +234,7 @@ describe('data-access', () => { const dataIdBlock2txFake: StorageTypes.IEntry = { content: JSON.stringify(blockWith2tx), id: '1', - meta: { timestamp: 10 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 10 }, }; const result: any = { dataIdBlock2tx: dataIdBlock2txFake, @@ -195,10 +253,23 @@ describe('data-access', () => { 'result with arbitraryId1 wrong', ).to.deep.equal({ meta: { - storageMeta: [{ timestamp: 10 }], + storageMeta: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + }, + ], transactionsStorageLocation: [dataIdBlock2tx], }, - result: { transactions: [{ transaction: transactionMock1, timestamp: 10 }] }, + result: { + transactions: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + transaction: transactionMock1, + }, + ], + }, }); }); @@ -226,7 +297,7 @@ describe('data-access', () => { const dataIdBlock2txFake: StorageTypes.IEntry = { content: JSON.stringify(blockWith2tx), id: '1', - meta: { timestamp: 10 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 10 }, }; const result: any = { dataIdBlock2tx: dataIdBlock2txFake, @@ -245,11 +316,26 @@ describe('data-access', () => { 'result with arbitraryTopic1 wrong', ).to.deep.equal({ meta: { - storageMeta: { [arbitraryId1]: [{ timestamp: 10 }] }, + storageMeta: { + [arbitraryId1]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + }, + ], + }, transactionsStorageLocation: { [arbitraryId1]: [dataIdBlock2tx] }, }, result: { - transactions: { [arbitraryId1]: [{ transaction: transactionMock1, timestamp: 10 }] }, + transactions: { + [arbitraryId1]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + transaction: transactionMock1, + }, + ], + }, }, }); }); @@ -278,7 +364,7 @@ describe('data-access', () => { const dataIdBlock2txFake: StorageTypes.IEntry = { content: JSON.stringify(blockWith2tx), id: '1', - meta: { timestamp: 10 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 10 }, }; const result: any = { dataIdBlock2tx: dataIdBlock2txFake, @@ -298,7 +384,20 @@ describe('data-access', () => { }); expect(ret.meta, 'meta wrong').to.deep.equal({ - storageMeta: { [arbitraryId1]: [{ timestamp: 10 }], [arbitraryId2]: [{ timestamp: 10 }] }, + storageMeta: { + [arbitraryId1]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + }, + ], + [arbitraryId2]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + }, + ], + }, transactionsStorageLocation: { [arbitraryId1]: [dataIdBlock2tx], [arbitraryId2]: [dataIdBlock2tx], @@ -306,8 +405,20 @@ describe('data-access', () => { }); expect(ret.result, 'result meta wrong').to.deep.equal({ transactions: { - [arbitraryId1]: [{ transaction: transactionMock1, timestamp: 10 }], - [arbitraryId2]: [{ transaction: transactionMock2, timestamp: 10 }], + [arbitraryId1]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + transaction: transactionMock1, + }, + ], + [arbitraryId2]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 10, + transaction: transactionMock2, + }, + ], }, }); }); @@ -334,10 +445,27 @@ describe('data-access', () => { const dataAccess = new DataAccess(defaultFakeStorage); await dataAccess.initialize(); + const errFunction = chai.spy(); const result = await dataAccess.persistTransaction(transactionMock1, arbitraryId1, [ arbitraryTopic1, ]); + result.on('error', errFunction).on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + storageMeta: { + state: DataAccessTypes.TransactionState.CONFIRMED, + timestamp: 1, + }, + topics: [arbitraryTopic1], + transactionStorageLocation: dataIdBlock2tx, + }, + result: {}, + }); + }); + clock.tick(11); + + expect(errFunction).to.not.be.called(); /* tslint:disable:object-literal-sort-keys */ /* tslint:disable:object-literal-key-quotes */ expect(defaultFakeStorage.append).to.have.been.called.with( @@ -358,13 +486,13 @@ describe('data-access', () => { ], }), ); - expect(result, 'result wrong').to.deep.equal({ - meta: { - storageMeta: { timestamp: 1 }, - topics: [arbitraryTopic1], - transactionStorageLocation: dataIdBlock2tx, + expect(result.meta, 'result wrong').to.deep.equal({ + storageMeta: { + state: DataAccessTypes.TransactionState.PENDING, + timestamp: 1, }, - result: {}, + topics: [arbitraryTopic1], + transactionStorageLocation: dataIdBlock2tx, }); }); @@ -390,6 +518,110 @@ describe('data-access', () => { `The following topics are not well formatted: ["This topic is not formatted"]`, ); }); + + it('cannot persistTransaction() and emit error if confirmation failed', async () => { + const mockStorageEmittingError: StorageTypes.IStorage = { + _getStatus: chai.spy(), + _ipfsAdd: chai.spy(), + append: chai.spy( + (): any => { + const appendResultWithEvent = Object.assign(new EventEmitter(), appendResult); + setTimeout( + () => { + appendResultWithEvent.emit('error', 'error for test purpose'); + }, + // tslint:disable-next-line:no-magic-numbers + 10, + ); + return appendResultWithEvent; + }, + ), + getData: (): Promise => defaultTestData, + getIgnoredData: async (): Promise => [], + initialize: chai.spy(), + read: (param: string): any => { + const dataIdBlock2txFake: any = { + meta: {}, + }; + const resultRead: any = { + dataIdBlock2tx: dataIdBlock2txFake, + }; + return resultRead[param]; + }, + readMany(params: string[]): Promise { + return Promise.all(params.map(this.read)); + }, + }; + + const dataAccess = new DataAccess(mockStorageEmittingError); + await dataAccess.initialize(); + + const result = await dataAccess.persistTransaction(transactionMock1, arbitraryId1, [ + arbitraryTopic1, + ]); + result.on('error', error => { + expect(error, 'result Confirmed wrong').to.equal('error for test purpose'); + }); + clock.tick(11); + + expect(result.meta, 'result wrong').to.deep.equal({ + storageMeta: { + state: DataAccessTypes.TransactionState.PENDING, + timestamp: 1, + }, + topics: [arbitraryTopic1], + transactionStorageLocation: dataIdBlock2tx, + }); + }); + }); + + describe('_getStatus', () => { + let dataAccess: any; + + beforeEach(async () => { + const fakeStorage = { + ...defaultFakeStorage, + read: (param: string): any => { + const dataIdBlock2txFake: StorageTypes.IEntry = { + content: JSON.stringify(blockWith2tx), + id: '1', + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 10 }, + }; + const result: any = { + dataIdBlock2tx: dataIdBlock2txFake, + }; + return result[param]; + }, + }; + + dataAccess = new DataAccess(fakeStorage); + await dataAccess.initialize(); + }); + + it('can _getStatus()', async () => { + expect(await dataAccess._getStatus(), 'result with arbitraryTopic1 wrong').to.deep.equal({ + filesIgnored: { count: 0, list: undefined }, + filesRetrieved: { count: 1, lastTimestamp: 10, list: undefined }, + lastSynchronizationTimestamp: 0, + storage: { fake: 'status' }, + synchronizationConfig: { + intervalTime: 10000, + successiveFailureThreshold: 5, + }, + }); + }); + it('can _getStatus() with details', async () => { + expect(await dataAccess._getStatus(true), 'result with arbitraryTopic1 wrong').to.deep.equal({ + filesIgnored: { count: 0, list: {} }, + filesRetrieved: { count: 1, lastTimestamp: 10, list: ['dataIdBlock2tx'] }, + lastSynchronizationTimestamp: 0, + storage: { fake: 'status' }, + synchronizationConfig: { + intervalTime: 10000, + successiveFailureThreshold: 5, + }, + }); + }); }); it('synchronizeNewDataId() should throw an error if not initialized', async () => { @@ -410,15 +642,18 @@ describe('data-access', () => { { content: JSON.stringify(blockWithoutHeader), id: 'whatever', - meta: { timestamp: 10 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 10 }, }, ], lastTimestamp: 0, }); const fakeStorageWithNotJsonData: StorageTypes.IStorage = { + _ipfsAdd: chai.spy(), append: chai.spy(), getData: (): Promise => testDataNotJsonData, + getIgnoredData: async (): Promise => [], + _getStatus: chai.spy(), initialize: chai.spy(), read: chai.spy(), readMany: chai.spy(), @@ -434,27 +669,25 @@ describe('data-access', () => { }); it('allows to get new transactions after synchronizeNewDataId() call', async () => { - const testData: Promise = Promise.resolve( - getDataResult, - ); - // We create a fakeStorage where getData() called at initialization returns empty structure const fakeStorage = { ...defaultFakeStorage, - getData: (options: any): Promise => { + getData: async (options: any): Promise => { if (!options) { return Promise.resolve(emptyDataResult); } - return testData; + return getDataResult; }, + getIgnoredData: async (): Promise => getIgnoredDataResult, read: (param: string): any => { const dataIdBlock2txFake: StorageTypes.IEntry = { content: JSON.stringify(blockWith2tx), id: '1', - meta: { timestamp: 1 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 1 }, }; const result: any = { dataIdBlock2tx: dataIdBlock2txFake, + dataIdBlock1txBis: getIgnoredDataResult[0], }; return result[param]; }, @@ -474,7 +707,7 @@ describe('data-access', () => { result: { transactions: {} }, }); - // Transactions should be available avec synchronization + // Transactions should be available after synchronization await expect(dataAccess.synchronizeNewDataIds()).to.be.fulfilled; expect( @@ -482,32 +715,69 @@ describe('data-access', () => { 'result with arbitraryTopic1 wrong', ).to.deep.equal({ meta: { - storageMeta: { [arbitraryId1]: [{ timestamp: 1 }] }, - transactionsStorageLocation: { [arbitraryId1]: [dataIdBlock2tx] }, + storageMeta: { + [arbitraryId1]: [ + { state: StorageTypes.ContentState.CONFIRMED, timestamp: 1 }, + { state: StorageTypes.ContentState.CONFIRMED, timestamp: 1 }, + ], + }, + transactionsStorageLocation: { [arbitraryId1]: [dataIdBlock2tx, dataIdBlock1txBis] }, }, result: { - transactions: { [arbitraryId1]: [{ transaction: transactionMock1, timestamp: 1 }] }, + transactions: { + [arbitraryId1]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + transaction: transactionMock1, + timestamp: 1, + }, + { + state: DataAccessTypes.TransactionState.CONFIRMED, + transaction: transactionMock3, + timestamp: 1, + }, + ], + }, }, }); - expect( await dataAccess.getChannelsByTopic(arbitraryTopic2), 'result with arbitraryTopic2 wrong', ).to.deep.equal({ meta: { storageMeta: { - [arbitraryId1]: [{ timestamp: 1 }], - [arbitraryId2]: [{ timestamp: 1 }], + [arbitraryId1]: [ + { state: DataAccessTypes.TransactionState.CONFIRMED, timestamp: 1 }, + { state: DataAccessTypes.TransactionState.CONFIRMED, timestamp: 1 }, + ], + [arbitraryId2]: [{ state: DataAccessTypes.TransactionState.CONFIRMED, timestamp: 1 }], }, transactionsStorageLocation: { - [arbitraryId1]: [dataIdBlock2tx], + [arbitraryId1]: [dataIdBlock2tx, dataIdBlock1txBis], [arbitraryId2]: [dataIdBlock2tx], }, }, result: { transactions: { - [arbitraryId1]: [{ transaction: transactionMock1, timestamp: 1 }], - [arbitraryId2]: [{ transaction: transactionMock2, timestamp: 1 }], + [arbitraryId1]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + transaction: transactionMock1, + timestamp: 1, + }, + { + state: DataAccessTypes.TransactionState.CONFIRMED, + transaction: transactionMock3, + timestamp: 1, + }, + ], + [arbitraryId2]: [ + { + state: DataAccessTypes.TransactionState.CONFIRMED, + transaction: transactionMock2, + timestamp: 1, + }, + ], }, }, }); @@ -515,8 +785,11 @@ describe('data-access', () => { it('startSynchronizationTimer() should throw an error if not initialized', async () => { const fakeStorageSpied: StorageTypes.IStorage = { + _ipfsAdd: chai.spy(), append: chai.spy.returns(appendResult), getData: (): Promise => chai.spy(), + getIgnoredData: async (): Promise => [], + _getStatus: chai.spy(), initialize: chai.spy(), read: chai.spy(), readMany: chai.spy(), @@ -533,7 +806,7 @@ describe('data-access', () => { const dataIdBlock2txFake: StorageTypes.IEntry = { content: JSON.stringify(blockWith2tx), id: '1', - meta: { timestamp: 1 }, + meta: { state: StorageTypes.ContentState.CONFIRMED, timestamp: 1 }, }; const result: any = { dataIdBlock2tx: dataIdBlock2txFake, diff --git a/packages/data-access/test/ignored-location.test.ts b/packages/data-access/test/ignored-location.test.ts new file mode 100644 index 0000000000..5b7101e6ef --- /dev/null +++ b/packages/data-access/test/ignored-location.test.ts @@ -0,0 +1,59 @@ +import 'mocha'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; +// tslint:disable: await-promise +// tslint:disable: no-magic-numbers + +chai.use(chaiAsPromised); +const expect = chai.expect; +chai.use(spies); + +import IgnoredLocation from '../src/ignored-location'; + +const arbitraryDataId1 = 'dataId1'; +const arbitraryDataId2 = 'dataId2'; +const arbitraryReason = 'reason1'; +const arbitraryReason2 = 'reason2'; + +/* tslint:disable:no-unused-expression */ +describe('IgnoredLocation', () => { + describe('pushReasonByLocation', () => { + it('can pushReasonByLocation()', async () => { + const ignoredLocation = new IgnoredLocation(); + await ignoredLocation.pushReasonByLocation(arbitraryDataId1, arbitraryReason); + + expect(await ignoredLocation.getReasonFromLocation(arbitraryDataId1)).to.equal( + arbitraryReason, + ); + }); + }); + describe('removeReasonByLocation', () => { + it('can removeReasonByLocation()', async () => { + const ignoredLocation = new IgnoredLocation(); + await ignoredLocation.pushReasonByLocation(arbitraryDataId1, arbitraryReason); + await ignoredLocation.removeReasonByLocation(arbitraryDataId1); + + expect(await ignoredLocation.getReasonFromLocation(arbitraryDataId1)).to.be.null; + }); + }); + + describe('getIgnoredLocations', () => { + it('can getIgnoredLocations()', async () => { + const ignoredLocation = new IgnoredLocation(); + await ignoredLocation.pushReasonByLocation(arbitraryDataId1, arbitraryReason); + await ignoredLocation.pushReasonByLocation(arbitraryDataId2, arbitraryReason2); + + expect(await ignoredLocation.getIgnoredLocations()).to.be.deep.equal({ + [arbitraryDataId1]: arbitraryReason, + [arbitraryDataId2]: arbitraryReason2, + }); + }); + it('can getIgnoredLocations() if empty', async () => { + const ignoredLocation = new IgnoredLocation(); + + expect(await ignoredLocation.getIgnoredLocations()).to.be.deep.equal({}); + }); + }); +}); diff --git a/packages/data-access/test/interval-timer.test.ts b/packages/data-access/test/interval-timer.test.ts index 1f58e813f9..4715a73761 100644 --- a/packages/data-access/test/interval-timer.test.ts +++ b/packages/data-access/test/interval-timer.test.ts @@ -27,9 +27,9 @@ const flushCallStack = (): Promise => { const emptyLogger = { debug: (_string: string): void => {}, - error: (_string: string): void => {}, - info: (_string: string): void => {}, - warn: (_string: string): void => {}, + error: (_string: string): void => {}, + info: (_string: string): void => {}, + warn: (_string: string): void => {}, } as LogTypes.ILogger; let intervalFunctionWithErrorCount: number; @@ -85,7 +85,7 @@ describe('interval-timer', () => { }); it('should periodically call the interval function provided when start() is called', async () => { - const callback = sinon.spy(async () => 0); + const callback = sinon.spy(async () => {}); intervalTimer = new IntervalTimer(callback, 1000, emptyLogger); intervalTimer.start(); @@ -168,7 +168,7 @@ describe('interval-timer', () => { hasBeenRejected = true; throw Error('makeReject set'); } - return 0; + return; }); intervalTimer = new IntervalTimer(callback, 1000, emptyLogger); diff --git a/packages/data-format/.vscode/settings.json b/packages/data-format/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/data-format/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/data-format/CHANGELOG.md b/packages/data-format/CHANGELOG.md index 66bd080165..c9fc3e0ac7 100644 --- a/packages/data-format/CHANGELOG.md +++ b/packages/data-format/CHANGELOG.md @@ -3,6 +3,197 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.14](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.14) (2020-06-29) + + +### Bug Fixes + +* company registration on rnf_invoice ([#228](https://github.com/RequestNetwork/requestNetwork/issues/228)) ([9bb3301](https://github.com/RequestNetwork/requestNetwork/commit/9bb3301a985a0b627a6c5eded3e3872af8628961)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.13](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.13) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.12](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.12) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.11](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.11) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.10](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.10) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.9](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.9) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.8) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/data-format + + + + + +## [0.4.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.7) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/data-format + + + + + +## [0.4.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.5...@requestnetwork/data-format@0.4.6) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/data-format + + + + + ## [0.4.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/data-format@0.4.4...@requestnetwork/data-format@0.4.5) (2019-11-20) **Note:** Version bump only for package @requestnetwork/data-format diff --git a/packages/data-format/package.json b/packages/data-format/package.json index 9cd3e446a9..7cfbae6671 100644 --- a/packages/data-format/package.json +++ b/packages/data-format/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/data-format", - "version": "0.4.5", + "version": "0.4.14", "publishConfig": { "access": "public" }, @@ -33,35 +33,35 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions json --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { "ajv": "6.8.1", - "tslib": "1.9.3", + "tslib": "1.10.0", "typescript": "3.7.2" }, "devDependencies": { "@types/chai": "4.1.7", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@types/node": "10.12.21", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-spies": "1.0.0", "dirty-chai": "2.0.1", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "rimraf": "2.6.3", "shx": "0.3.2", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1" }, "gitHead": "6155223cfce769e48ccae480c510b35b4f54b4d0" diff --git a/packages/data-format/src/format/rnf_invoice/rnf_invoice-0.0.2.json b/packages/data-format/src/format/rnf_invoice/rnf_invoice-0.0.2.json index c7c2000af8..3391fffd9f 100644 --- a/packages/data-format/src/format/rnf_invoice/rnf_invoice-0.0.2.json +++ b/packages/data-format/src/format/rnf_invoice/rnf_invoice-0.0.2.json @@ -97,6 +97,9 @@ "taxRegistration": { "type": "string" }, + "companyRegistration": { + "type": "string" + }, "miscellaneous": { "type": "object" } diff --git a/packages/data-format/test/data/example-valid-0.0.2.json b/packages/data-format/test/data/example-valid-0.0.2.json index 233ce0a490..9c1f9eebaa 100644 --- a/packages/data-format/test/data/example-valid-0.0.2.json +++ b/packages/data-format/test/data/example-valid-0.0.2.json @@ -21,6 +21,7 @@ "street-address": "38 avenue de l'Opera", "country-name": "France" }, + "companyRegistration": "187 579 453", "miscellaneous": { "aliases": ["Ultime Fauchelevent", "Urbain Fabre", "Prisoner 24601", "Prisoner 9430"] } @@ -37,6 +38,7 @@ "postal-code": "98052", "street-address": "20341 Whitworth Institute 405 N. Whitworth" }, + "companyRegistration": "147515605", "miscellaneous": { "Occupation": ["Prison guard", "Police inspector", "Detective"] } diff --git a/packages/docs/.gitignore b/packages/docs/.gitignore new file mode 100755 index 0000000000..a38f266d25 --- /dev/null +++ b/packages/docs/.gitignore @@ -0,0 +1,26 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader +docs/client/** +docs/guides/4-request-payment/1-multisig.md +docs/guides/5-request-client/2-erc20-payment-detection.md +docs/guides/5-request-client/3-eth-payment-detection.md +docs/guides/5-request-client/4-btc-payment-detection.md +docs/guides/5-request-client/5-declarative-payment-network.md + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md new file mode 100644 index 0000000000..acd5932ff8 --- /dev/null +++ b/packages/docs/CHANGELOG.md @@ -0,0 +1,64 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 0.1.4 (2020-06-29) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* fix docs dependencies ([#184](https://github.com/RequestNetwork/requestNetwork/issues/184)) ([b0f16be](https://github.com/RequestNetwork/requestNetwork/commit/b0f16be77d4bfa828b5e23648e9b161a4d2c80cf)) + + + + + +## 0.1.3 (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* fix docs dependencies ([#184](https://github.com/RequestNetwork/requestNetwork/issues/184)) ([b0f16be](https://github.com/RequestNetwork/requestNetwork/commit/b0f16be77d4bfa828b5e23648e9b161a4d2c80cf)) + + + + + +## 0.1.2 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* fix docs dependencies ([#184](https://github.com/RequestNetwork/requestNetwork/issues/184)) ([b0f16be](https://github.com/RequestNetwork/requestNetwork/commit/b0f16be77d4bfa828b5e23648e9b161a4d2c80cf)) + + + + + +## 0.1.1 (2020-04-06) + + +### Bug Fixes + +* fix docs dependencies ([#184](https://github.com/RequestNetwork/requestNetwork/issues/184)) ([b0f16be](https://github.com/RequestNetwork/requestNetwork/commit/b0f16be77d4bfa828b5e23648e9b161a4d2c80cf)) diff --git a/packages/docs/README.md b/packages/docs/README.md new file mode 100755 index 0000000000..ee0ccc9d18 --- /dev/null +++ b/packages/docs/README.md @@ -0,0 +1,33 @@ +# Website + +This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. + +### Installation + +``` +$ yarn +``` + +### Local Development + +``` +$ yarn start +``` + +This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. + +### Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +### Deployment + +``` +$ GIT_USER= USE_SSH=true yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/packages/docs/docs/guides/0-getting-started.md b/packages/docs/docs/guides/0-getting-started.md new file mode 100644 index 0000000000..529842024b --- /dev/null +++ b/packages/docs/docs/guides/0-getting-started.md @@ -0,0 +1,23 @@ +--- +title: Get Started with Request +keywords: +description: Learn how to integrate Request network and its features. +--- + +import IntegrationOptions from '../../src/components/integration-options'; + +Request enables a **global cooperative financial system**, where people and organizations are in full control over their financial data and choices. + +Users share payment requests securely thanks to a common network that keeps track of changes. Financial experiences become seamless with Request. + +## Integration options + +Different integration schemas are possible with Request. This tutorial aims to discover the features of the network and at the same time to guide you through different options. + + + +Within this guide, we naturally guide you through all options in order of complexity. For a detailed overview, [head to the integration options](/integration-options) + +:::info +If you are already familiar with basic concepts of Request, you can jump to the [API](./3-Portal-API/0-portal-intro.md), the [Request Client](./5-request-client/0-intro.md) or the [Node setup guide](./6-hosting-a-node/0-intro.md). +::: diff --git a/packages/docs/docs/guides/1-first-request.md b/packages/docs/docs/guides/1-first-request.md new file mode 100644 index 0000000000..cfab104261 --- /dev/null +++ b/packages/docs/docs/guides/1-first-request.md @@ -0,0 +1,69 @@ +--- +title: Create your first Request +sidebar_label: First request creation +keywords: [Request creation, API] +description: Learn how to integrate Request network and its features. + +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; + +Get an outlook of the Request network in a matter of minutes with the API. + +:::info +This is a very fast ramp to the Request network, using the Portal API, a centralized option. +If you want to jump to the decentralized options, head towards the [Request Client section](./5-request-client/0-intro.md). +::: + +## Request identity and API Key + +Head towards [the Request Portal dashboard](https://dashboard.request.network) and create your account, you will need it to get your API keys and pursue the first steps of Request. + + +Getthing the API key from the Portal + +More info about the Request Portal [in the next section](./3-Portal-API/0-portal-intro.md). + +## Create your first request + +To create a payment request or invoice, you must create a basic Request object which outlines some information such as the receiving payment address, which payment network is being used, the currency and the amount expected. + +```jsx +const axios = require('axios') + +const API_KEY = 'YOUR_API_KEY'; +const requestParams = { + "currency": "BTC", + "expectedAmount": "100000000", + "payment": { + "type": "bitcoin-testnet", + "value": "mgcZRSj6ngfKBUHr2DGBqCfHSSYBDSbjph" + }, +}; + +const request = await axios.post('https://api.request.network/requests', requestParams, { + headers: { Authorization: API_KEY } +}) + +console.log(request.data); +``` + +The `data` object contains a `requestId` field that you can use for other API calls. + +## Check that it worked + +You can check that the request was created by heading towards [the list of requests in your dashboard](https://dashboard.request.network). The request should be listed on top of the list. + +By clicking on the request row, you can see the details. + +You can see that the status is Pending: the request has not been paid yet. We will deal with payment detection [later](./3-Portal-API/2-payment-status.md). + +## Wrap-up + +The request created is proof that some money has been requested by a person or company, and its pending status the proof that the money is still due. + +In the next sections, you will also see how to transform such a basic creation into seamless financial workflows: + +* For the payer who can pay in one click, because we have all the information he needs + +* For the requester to keep track of his due payments diff --git a/packages/docs/docs/guides/3-Portal-API/0-portal-intro.md b/packages/docs/docs/guides/3-Portal-API/0-portal-intro.md new file mode 100644 index 0000000000..6f73dc34d8 --- /dev/null +++ b/packages/docs/docs/guides/3-Portal-API/0-portal-intro.md @@ -0,0 +1,99 @@ +--- +title: Portal Introduction +sidebar_label: Portal Introduction +keywords: [Request, REST, API, Tutorial, Portal] +description: Learn how to integrate Request network and its features. + +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; + +Request Portal simplifies the use of the Request protocol, abstracting all the blockchain complexity. You can create requests and manage your users' requests through a REST API. + +Our API accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes and Bearer authentication. + +Aside from the guide, you can also consult [the Portal API documentation](/portal). + +## Authentication + +All API endpoints are authenticated. +Two mechanisms are currently allowed: + +- API Key, explained below, to be used for scripting and test purposes +- OAuth, explained in the [Apps](./3-api-apps.md) section + +## Portal outlook + +If you have not already done it, head towards [the Request Portal dashboard](https://dashboard.request.network) and create your account. + +Once your account is created, you are able to: + +- Create a request, which is useful for manual testing for example +- List requests you sent or received, useful for debugging +- Know your Request identity (cf. below) +- Access your API keys, by clicking on your account and then Settings. + +Getting the API key from the Portal + +You have two API keys, use the Test one to follow this guide. + +## Request identity + +Senders and recipients of money transfer requests need a way to trust each other. The identity is how we certify the debtor about the authenticity of the request sender, limiting frauds like SCAM for example. +With decentralized integration options (cf. [the Request client](/docs/guides/5-Request-client/0-intro), end users manage their private keys, but the Portal simplifies their life. + +This simplification should be applied with great care, we do not recommend using the Request Portal API for critical cases where a lot of money or public reputation is at stake. If you want full control over the security of your finance, you should handle your keys, and the same applies to your users. Have a look at [the integration options](/integration-options) to take the best decision. + +## How to list the requests associated with your identity + +Head to the Portal to create a first manual request, and use the snippet below to fetch requests associated with your identity + +```jsx +import ReactDOM from "react-dom"; +import React, { useState, useEffect } from "react"; +import axios from "axios"; + +const API_KEY = "YOUR-API-KEY"; + +function RequestsList() { + const [requests, setRequests] = useState([]); + + useEffect(() => { + const fetchResult = async () => { + const result = await axios.get("https://api.request.network/requests/", { + headers: { Authorization: API_KEY } + }); + console.log(result); + setRequests(result.data); + }; + fetchResult(); + }, []); + + return ( +
+

The most basic list of payment requests

+
    + {requests.map(request => { + return ( +
  • + {request.requestInput.expectedAmount} {request.requestInput.currency} +
  • + ); + })} +
+
+ ); +} + +const rootElement = document.getElementById("root"); +ReactDOM.render(, rootElement); + + +const result = await axios.get('https://api.request.network/requests/' + requestId, { + headers: { Authorization: API_KEY }, +}); +``` + +The expected result should be a list of requests with amounts and currencies. Depending on your currency, some amounts seem too big. We will see later how to display amounts properly. + +As you can see, manipulating requests with the Portal API is very straight-forward. What you can notice is the use of `request.requestInput.expectedAmount` and `request.requestInput.currency`. We will detail on the next page how to manipulate different details of the request. You can also have more details on the [Portal API Docs](/portal). diff --git a/packages/docs/docs/guides/3-Portal-API/1-create-and-share-request.md b/packages/docs/docs/guides/3-Portal-API/1-create-and-share-request.md new file mode 100644 index 0000000000..af1a6af909 --- /dev/null +++ b/packages/docs/docs/guides/3-Portal-API/1-create-and-share-request.md @@ -0,0 +1,108 @@ +--- +title: Request API - Let's play +keywords: [ERC20, DAI, Request, Portal, API] +description: Learn how to integrate Request network and its features. + +--- + +## Create a request in DAI + +On this page, you will learn to create requests in DAI or in any ERC20, whose payment will be automatically detected. + +DAI is one of the most frequently used currencies on the Request network. As the most popular stablecoin, it allows merchants and e-commerce software builders to propose blockchain-powered payments, finance, and accounting, without having to deal with the change risks. + +The Portal can detect payments of requests in ETH, BTC, and ERC20. After the example below, you will understand that smart invoices can open many use cases and that reconciliation processes between bank statements and accounting is already outdated. + +### Prerequisites + +Before you execute the code below, don't forget to: + +* Change the `API_KEY`, with your test API key +* Change the payment value with one of your Ethereum addresses + +Even if for this tests the transaction will be done over the Rinkeby network, it's more realistic to configure your own address. + +And now, let's look at the code: + +```jsx +import ReactDOM from "react-dom"; +import React, {useState} from "react"; +import axios from "axios"; +import { ethers } from "ethers"; + +const API_KEY = "YOUR_API_KEY"; + +function CreateDAIRequest() { + + const [paymentLink, setPaymentLink] = useState([]); + + async function createRequest() { + + // When the button is clicked, make a POST query to create the request + const result = await axios.post( + `https://api.request.network/requests/`, + { + + // The same works with any ERC20 + currency: "DAI", + + // Here we want to create a $49.99 request + expectedAmount: ethers.utils.parseUnits("49.99", 18).toString(), + payment: { + + // Proxy contract: the payment will be detected automatically + // and the same address can be used several times + type: "erc20-proxy-contract", + + // This is where the funds will come, you probably want to put your address + value: "0xAa0c45D2877373ad1AB2aa5Eab15563301e9b7b3" + } + }, + { + headers: { + Authorization: API_KEY + } + } + ); + + // Once the query returns, we know the request ID + // The payment page can only query and show the request once it is broadcasted over Ethereum. + setPaymentLink("https://pay.request.network/" + result.data['requestId']); + return result.data['requestId']; + } + + return ( + <> + + {paymentLink} + + ); +} + +const rootElement = document.getElementById("root"); +ReactDOM.render(, rootElement); + +``` + +Try it! By clicking on the button, a request is created and with its ID, we display the payment page URL. There are several ways to pay a request, we will come back on that later, but the payment page is the most convenient to start. Did you notice that Metamask did not prompt you? That's because the Portal takes care of the blockchain transaction, as well as the fees. + +Now **what happens if you click on the payment link right after the request creation?** + +The payment page throws a "Your request was not found" error, why? + +Remember that the Portal abstracts the blockchain access. By doing so, a request created on the portal does not exist on every node of the network, yet. And the payment page is another node on the network. + +**To summarize, before you can access the payment page, you need to wait a few seconds.** + +You can check the request status and details in the [dashboard](https://dashboard.request.network/) + + +## Sharing the request to get paid + +Once a user has created a request, you need to support him alerting the payer. + +The first way is to let the user share a payment URL with the payer. From a UX point of view, it forces him to switch context, but mobile apps often propose this solution. Keep in mind that for the recipient, it looks more secure to click on a link directly sent by a known contact. The best payment page so far is the one we have made, check it out! You can find the link in front of each request on your dashboard. The URL is `https://pay.request.network/{requestId}` + +Another way is to handle the notification in your backend, either within your app (if the payer also uses it) or with an e-mail for example. For app-embedded payment requests, it is **strongly advised** to provide the payer with a white-list feature, and to prevent him from clicking on requests sent by strangers. + +Whatever the solution you pick, you should consider the UX / security tradeoff with great attention. diff --git a/packages/docs/docs/guides/3-Portal-API/2-payment-status.md b/packages/docs/docs/guides/3-Portal-API/2-payment-status.md new file mode 100644 index 0000000000..3f31b873e0 --- /dev/null +++ b/packages/docs/docs/guides/3-Portal-API/2-payment-status.md @@ -0,0 +1,67 @@ +--- +title: Payment status with the Portal API +sidebar_label: Payment status +keywords: +description: Learn how to integrate Request network and its features. +--- + +After creation, each request has a payment status, you can view the data of a request which contains the payment status via: + +```jsx +const result = await axios.get(`https://api.request.network/requests/${requestId}`); +const request = result.data.request; +``` + +You will receive back an object that looks like this: + +```jsx +/* +{ + balance, + state, + expectedAmount, + ... +} +*/ +``` + +To get the payment status of a Request you can use the requestData object to check if the balance is greater than or equal to the expectedAmount. + +If the balance >= expectedAmount - this means the request is paid. +If the balance > 0 but < expectedAmount - this means the request is partially paid. +If the balance == 0 - this means the request is unpaid. + +You can use the following snippet to see if the request has been paid. + +```jsx +// Import Big Number package +const BN = require('bn.js'); +(async () => { + // Check the balance of the request + const result = await axios.get(`https://api.request.network/requests/${requestId}`); + const request = result.data.request; + + const balanceObject = request.balance; + + if (!balanceObject) { + console.error('balance no set'); + return; + } + if (balanceObject.error) { + console.error(balanceObject.error.message); + return; + } + + console.log(`Balance of the request in ETH: ${balanceObject.balance}`); + + // Check if the request has been paid + // Convert the balance to big number type for comparison + const expectedAmount = new BN(requestData.expectedAmount); + const balanceBigNumber = new BN(balanceObject.balance); + + // Check if balanceBigNumber is greater or equal to expectedAmount + const paid = balanceBigNumber.gte(expectedAmount); +})(); +``` + +If the Request is unpaid, it might be useful to use the metadata field called ‘state’ - the state will return the current payment status of the request, either ‘created’, ‘accepted’, ‘pending’ or ‘canceled’. diff --git a/packages/docs/docs/guides/3-Portal-API/3-api-apps.md b/packages/docs/docs/guides/3-Portal-API/3-api-apps.md new file mode 100644 index 0000000000..0a2c8ff7ed --- /dev/null +++ b/packages/docs/docs/guides/3-Portal-API/3-api-apps.md @@ -0,0 +1,51 @@ +--- +title: API Apps +keywords: [Request, Apps, API] +description: Learn how to integrate Request network and its features. + +--- +import useBaseUrl from '@docusaurus/useBaseUrl'; + +## Introduction + +Developers can build on top of Request API by creating Applications. + +Your application will be able to read, create, and perform actions on requests, **on behalf of your users**, once they grant this permission. + +## Getting started + +:::info +Unlock this option by getting in touch with us on the [Request Hub](https://join.slack.com/t/requesthub/shared_invite/enQtMjkwNDQwMzUwMjI3LTc5NDRmN2YyMTVhZTBjNDE2MWU2YTBlYWIzYmJlYzNkMWQ5MzVmYzEzNGVmYjliNDQ4MjkyNTBiYjk4MDk3ZGE). +::: + +Authenticate your app into the Request Portal + +- First, make sure you have [signed up](https://dashboard.request.network/signup) to Request +- Fork our [demo codesandbox](https://codesandbox.io/s/request-api-apps-zqt8o) +- Copy the generated URL for your codesandbox' app (You can get it in the codesandbox Browser; it should look like https://xxxx.csb.app) +- Go to https://dashboard.request.network/settings/apps and click "Create" + - Choose whatever name you want + - Input your codesandbox URL as "Callback URL" and "Logout URL" + - Click Create + - Copy the ID field in the line that just appeared. +- In codesandbox, go to the `.env` file and set the variable `REACT_APP_CLIENT_ID` to your generated Client ID. +- Now, click the Login button on the generated example. You should be able to log in using your Request account. + +:::info +To properly test your integration, you should connect to the sample app using different credentials than those of your app. Typically, you should have a fake user that you use to perform end-to-end tests. + +As an app developer, you will _not_ see your users' requests in your dashboard. +::: + +## Authenticating users + +Users of an application built on top of Request Portal API need to authenticate with the OAuth2 mechanism. +We recommend using [Auth0 SDKs](https://auth0.com/docs/libraries#sdks) to integrate it, but you are free to choose whatever you want. + +The authentication's domain is `auth.request.network`. + +In the codesandbox app above, you can check how onboarding looks like for first time users of Request. + +## Get help + +Do you have questions ? Or do you have trouble authenticating your app? Get in touch with us on the [Request Hub](https://join.slack.com/t/requesthub/shared_invite/enQtMjkwNDQwMzUwMjI3LTc5NDRmN2YyMTVhZTBjNDE2MWU2YTBlYWIzYmJlYzNkMWQ5MzVmYzEzNGVmYjliNDQ4MjkyNTBiYjk4MDk3ZGE). diff --git a/packages/docs/docs/guides/3-Portal-API/4-api-encryption.md b/packages/docs/docs/guides/3-Portal-API/4-api-encryption.md new file mode 100644 index 0000000000..ef2fd239c0 --- /dev/null +++ b/packages/docs/docs/guides/3-Portal-API/4-api-encryption.md @@ -0,0 +1,63 @@ +--- +title: Encryption with the API +sidebar_label: Encryption with the API +keywords: [Request, encryption, API] +description: Learn how to integrate Request network and its features. + +--- + +# About encryption + +By default, anything you store on Request can be read by anyone. That might be what you are looking for, or not! + +To give you control over this, the Request Protocol supports end-to-end encryption. It means that no one outside of a request stakeholder (usually, its payer and payee) can read its information. + +Request Portal API, however, **do not** offer end-to-end encryption, but **does** allow you to remove public access. +What it means is that your request would be stored encrypted on the Network (Ethereum + IPFS) but we, at Request, could have access to the request data. + +:::info +Although we technically have access to your request data, we will never use or share this data. +We are planning to withdraw our own access to any encrypted data to provide end-to-end encryption with the API, while keeping the best possible experience for our builders. + +If end-to-end encryption is paramount for your usage, we recommend you use the [Request Client](../5-request-client/0-intro.md) instead of the Portal API. +::: + + +# Handle encrypted requests + +## Create an encrypted request + +Creating an encrypted request with the API is very easy. You simply have to add `encrypted: true` to the payload. + +```javascript +const apiKey = "YOUR_API_KEY"; +await axios.post(`https://api.request.network/requests/`,{ + currency: 'EUR', + expectedAmount: "1000", + payment: { + type: 'declarative', + value: {} + }, + payer: { + type: 'email', + value: 'foo@bar.com' + }, + encrypted: true + }, + { + headers: { + Authorization: apiKey + } + }) +``` + +## Specify the stakeholders +By default, you, as payee and creator of a request, will always be granted with Read access to the request. + +If you specify a Payer that is also using Request Portal API, they will be given access to the request as well. + +For other cases (Payer not using the API, third party access), you can specify as many public keys as you want to the `stakeholders` field. To know more about the public key format, please refer to [this page](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/transaction-manager/specs/encryption.md). + + +## Request decryption +Reading encrypted requests is the same as non-encrypted ones, the API handles the decryption for you! This applies only if the request was created through the API; if it was created outside of the API, an encrypted request will not be found on the API. diff --git a/packages/docs/docs/guides/3-Portal-API/5-api-conclusion.md b/packages/docs/docs/guides/3-Portal-API/5-api-conclusion.md new file mode 100644 index 0000000000..87b00956ca --- /dev/null +++ b/packages/docs/docs/guides/3-Portal-API/5-api-conclusion.md @@ -0,0 +1,23 @@ +--- +title: Request through the REST API - wrap-up +sidebar_label: REST API wrap-up +keywords: +description: Learn how to integrate Request network and its features. +--- + + +Request API is an abstraction over the Request Network for easy, but centralized, access. It's the fastest way to get started and integrate Request to your product. + +## Pros +* Simple REST API, familiar for classic web developers +* No key management +* Faster response time +* Notifications on new requests and received payments +* Send requests to other API users based on their email, instead of their ETH address. +* Easy to build applications on top of Request (see [here](./3-api-apps.md)) +* Easy choice between fully public data or encrypted data on the network + +## Cons +* Not fully decentralized +* Users need a Request account to connect +* Encryption is not end-to-end (see [here](./4-api-encryption.md)) diff --git a/packages/docs/docs/guides/4-request-payment/0-intro.md b/packages/docs/docs/guides/4-request-payment/0-intro.md new file mode 100644 index 0000000000..c0caeecc13 --- /dev/null +++ b/packages/docs/docs/guides/4-request-payment/0-intro.md @@ -0,0 +1,89 @@ +--- +title: Pay a request +keywords: [Request payment library] +description: Learn how to integrate Request network and its features. + +--- + + +## Introduction + +In the previous sections, you have learned how to create a request for payment. We will now explain to you how to pay it. + +This section is useful if: +* You plan to embed request payment features like a Pay button +* You want to test the payment stage of requests you create + +The payment of a request depends on its [payment network](../5-request-client/1-payment-networks.md#types-of-payment-network): + +- Address-based payment networks (available for BTC and ERC20) don't have any specific requirement: any payment sent to the specified address will be considered a payment of this request. Never re-use an address! +- Input data payment networks (ETH only) simply requires you to specify the [Payment Reference](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-eth-input-data-0.2.0.md#description) in the data of the transaction. +- Proxy payment networks (ETH and ERC20) require you to call a smart contract method that forwards the payment. + +To simplify the payment procedures of the various payment networks, you can use the dedicated library, [@requestnetwork/payment-processor](https://www.npmjs.com/package/@requestnetwork/payment-processor). +On top of calculating the Payment Reference and handling the transaction for you, it provides a few utilities to ensure the user meets all requirements to pay the request (enough funds for example) + + +## About the payment-processor library + +### Install + +```bash +npm install @requestnetwork/payment-processor +# or +yarn add @requestnetwork/payment-processor +``` + + +### Usage + +#### ETH request + +```typescript +import { hasSufficientFunds, payRequest } from "@requestnetwork/payment-processor"; + +const requestNetwork = new RequestNetwork(); + +// usually, the connected account. +const account = "[WALLET_ADDRESS]"; + +const request = await requestNetwork.fromRequestId('[REQUEST_ID]'); +const requestData = request.getData(); +if (!(await hasSufficientFunds(requestData, account))) { + throw new Error('You do not have enough funds to pay this request'); +} +const tx = await payRequest(requestData); +await tx.wait(1); +``` + +#### ERC20 request +```typescript +import { + approveErc20, + hasErc20Approval, + hasSufficientFunds, + payRequest +} from "@requestnetwork/payment-processor"; + +// usually, the connected account. +const account = "[WALLET_ADDRESS]"; + +const requestNetwork = new RequestNetwork(); + +const request = await requestNetwork.fromRequestId('[REQUEST_ID]'); +const requestData = request.getData(); +if (!(await hasSufficientFunds(requestData, account))) { + throw new Error('You do not have enough funds to pay this request'); +} +if (!(await hasErc20Approval(requestData, account))) { + const approvalTx = await approveErc20(requestData); + await approvalTx.wait(1); +} +const tx = await payRequest(requestData); +await tx.wait(1); +``` + +:::info About ERC20 Contract Approval +Because an ERC20 transaction cannot contain input data, we need to go through a smart contract to document the payment. The user must allow this contract to spend tokens on its behalf. Read more [here](https://medium.com/ethex-market/erc20-approve-allow-explained-88d6de921ce9) +::: + diff --git a/packages/docs/docs/guides/4-request-payment/1-multisig.ts b/packages/docs/docs/guides/4-request-payment/1-multisig.ts new file mode 100644 index 0000000000..4036c03759 --- /dev/null +++ b/packages/docs/docs/guides/4-request-payment/1-multisig.ts @@ -0,0 +1,69 @@ +/** + * # Pay a request with a Multisig + * + * ## Pay through a proxy-contract with a multisig + * + * The imports you will need: + */ + +import { Contract, ContractTransaction, Signer } from 'ethers'; + +import { + encodeApproveErc20, + encodePayErc20Request, +} from '@requestnetwork/payment-processor/dist/payment/erc20-proxy'; +import { getRequestPaymentValues } from '@requestnetwork/payment-processor/dist/payment/utils'; +import { ClientTypes } from '@requestnetwork/types'; + +/** + * In this example, we will use the [Gnosis multisig](https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol). here its partial abi: + */ +const multisigAbi = [ + 'function submitTransaction(address _destination, uint _value, bytes _data) returns (uint)', +]; + +/** + * ### Pay ETH request + */ +export const payEthWithMultisig = async ( + request: ClientTypes.IRequestData, + multisigAddress: string, + signer: Signer, +): Promise => { + const multisigContract = new Contract(multisigAddress, multisigAbi, signer); + + const { paymentAddress, paymentReference } = getRequestPaymentValues(request); + return multisigContract.submitTransaction(paymentAddress, 0, paymentReference); +}; + +/** + * ### Pay ERC20 request + * #### Approve ERC20 spending + */ +export const approveErc20WithMultisig = async ( + request: ClientTypes.IRequestData, + multisigAddress: string, + signer: Signer, +): Promise => { + const multisigContract = new Contract(multisigAddress, multisigAbi, signer); + const tokenAddress = request.currencyInfo.value; + + return multisigContract.submitTransaction(tokenAddress, 0, encodeApproveErc20(request, signer)); +}; +/** + * #### Pay ERC20 request + */ +export const payErc20WithMultisig = async ( + request: ClientTypes.IRequestData, + multisigAddress: string, + signer: Signer, +): Promise => { + const multisigContract = new Contract(multisigAddress, multisigAbi, signer); + const tokenAddress = request.currencyInfo.value; + + return multisigContract.submitTransaction( + tokenAddress, + 0, + encodePayErc20Request(request, signer), + ); +}; diff --git a/packages/docs/docs/guides/5-request-client/0-intro.md b/packages/docs/docs/guides/5-request-client/0-intro.md new file mode 100644 index 0000000000..a8205cbc0f --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/0-intro.md @@ -0,0 +1,51 @@ +--- +title: Request Network Client introduction +sidebar_label: Introduction +keywords: [Request Network Client, JS Library] +description: Learn how to integrate Request network and its features. + +--- + +Collecting payments on your project consists of using the Request Client to create a request, detect the payment, and provide status updates of the Request. + +This package allows you to interact with the Request blockchain through the Request nodes. This client-side library uses Request nodes as servers, which are connected in HTTP. + +You can view the documentation about the [Request Node here](../6-hosting-a-node/0-intro.md). + +It ships both as a commonjs and a UMD module. This means you can use it in node application and in web pages. +Request uses this library, to track and handle all the states of the payment until it’s completed. + +1. Install the Request Client +```shell +# install the request js library +npm install @requestnetwork/request-client.js +# install a request signature provider (e.g: web3-signature to use Metamask) +npm install @requestnetwork/web3-signature +``` + +2. Import the client +```jsx +import { RequestNetwork } from '@requestnetwork/request-client.js' + +const RequestNetwork = require('@requestnetwork/request-client.js'); +``` + +3. Configure the node + +```jsx +const requestNetwork = new RequestNetwork({ + // The Rinkeby Gateway is the node hosted by Request + // You can use it on the Rinkeby network without limit, for testing and discovery of the library + nodeConnectionConfig: { baseURL: 'https://gateway-rinkeby.request.network/' }, +}); +``` + +4. What node should you use? + +In order to follow this guide or test your integration, you should use `https://gateway-rinkeby.request.network`. + +For production, you have two options, [compared in the integrations section](/integration-options): + +* If you want to host your own Request Node, [have a look at the next section](../6-hosting-a-node/0-intro.md) + +* If you prefer to use a node as a service, Request hosts one for you at this location: `https://gateway.request.network`. For the moment, it comes free of charges and fees. If you reach the limit or want to know more, [get in touch with us!](https://join.slack.com/t/requesthub/shared_invite/enQtMjkwNDQwMzUwMjI3LWNlYTlmODViMmE3MzY0MWFiMTUzYmNiMWEyZmNiNWZhMjM3MTEzN2JkZTMxN2FhN2NmODFkNmU5MDBmOTUwMjA) diff --git a/packages/docs/docs/guides/5-request-client/1-payment-networks.md b/packages/docs/docs/guides/5-request-client/1-payment-networks.md new file mode 100644 index 0000000000..4917d94727 --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/1-payment-networks.md @@ -0,0 +1,145 @@ +--- +title: How payment networks work +sidebar_label: Payment networks +keywords: [Request Client, Payment networks] +description: Learn how to integrate Request network and its features. + +--- +import SupportedErc20 from '../../../src/components/supported-erc20'; + +When a user creates and sends a request, he expects to receive the correct amount of money. But how does he keep track of the payments due and received? Request is a ledger that documents requests for payment and how to agree on their completion. + +There are different methods available for the payee and payer to agree on the payment status, and that is when payment networks come into play. **A payment network is a predefined set of rules on how to agree on the payment status of a specific request.** + +A payment network is defined by: +* The information, defined at the request creation, necessary to be able to detect payments +* A payment method, the method used to perform a detectable payment +* A payment detection method, the process to determine the amount paid by the payer through the payment method + +## Types of payment detection + +There are three ways to get consensus on a payment status. + +### Address based + +For these payment networks, a request contains a payment address that is unique to the request. +The balance of the request is computed by reading all the inbound transfers to the payment address. To pay the request, the payer performs a normal transfer to the payment address. +Outbound transfers are not taken into consideration to compute the request's balance. +The address must be created exclusively for the request since every inbound transfer to the addresses is considered a payment. For example, if a Bitcoin request is created with a payment address that has already received 1 BTC, the request balance will be 1 BTC even though the payee hasn't received any funds from the payer. + +For address based payment requests, the refund address also has to be exclusive to this payment refund. + +### Reference based + +For these payment networks, a request contains one payment address. This address doesn't have to be exclusive to the request. +The balance is computed by reading transfers to the payment address containing a specific payment reference, defined by the request ID and payment address. + +For ETH, we can tag and detect the payment reference directly in the transaction, using the `input data` field of the transaction. + +When we cannot use `input data` or equivalent, typically for ERC20, we use a *proxy smart contract* to document the payment reference. +The smart contract forwards a currency transfer and stores a reference. + +If you need the proxy smart contract addresses, we list the most relevant ones below. + +[Proxy smart contracts for ERC20](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/smart-contracts/artifacts/ERC20Proxy/artifacts.json): +```json +"mainnet": { + "address": "0x5f821c20947ff9be22e823edc5b3c709b33121b3", +}, +"rinkeby": { + "address": "0x162edb802fae75b9ee4288345735008ba51a4ec9", +} +``` +[Proxy smart contracts for ETH when input data cannot be used](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/smart-contracts/artifacts/EthereumProxy/artifacts.json): +```json +"mainnet": { + "address": "0x37a8f5f64f2a84f2377481537f04d2a59c9f59b6", +}, +"rinkeby": { + "address": "0x9c6c7817e3679c4b3f9ef9486001eae5aaed25ff", +} +``` + +For reference based payment requests, the references for the main payment and the refund are different. + +### Declarative + +For these payment networks, the request doesn't require any additional data. The request's stakeholders declare sending and receiving payments or refunds manually. Optionally, the creator of the request can specify the information to describe how the payment should occur, but this data will not be used to detect the payment. +The payee declares the received payments and the payer declares the received refunds. The balance of the request is the sum of declared payments minus the sum of declared refunds. +The payee can also declare the sent refunds and the payer the sent payments. These declarations are used only for documentation purposes and aren't taken into consideration to compute the request balance. + +This type of payment network can be used with every currency. + +## Currencies + +The currencies that are supported for automated payment detection are +* Bitcoin +* Ether +* ERC20 + +### Bitcoin + +A Bitcoin request requires an address based payment network. +This payment network is sufficient for Bitcoin requests because generating a new address for every inbound BTC transfers is already part of Bitcoin's good practices. + +### Ether + +Because one Ethereum address is generally used many times to receive and send transactions, we need a way to identify payments for a specific request without having to create a new address. Therefore, we use a reference-based payment network for ether requests. +*Input data* and *proxy contract* methods can be used to reference the ether transfer. + +### ERC20 + +Request is compatible with every ERC20 currency, but some of them have to be detailed manually. We use Metamask's package [`eth-contract-metadata`](https://github.com/MetaMask/eth-contract-metadata) to automatically fetch smart contracts and currency codes of main currencies. + +#### Listed ERC-20: + + + +For listed ERC20 currencies, you can use the code directly. +```typescript +// New request for most common currencies, such as DAI or BAT: +const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: { + currency: 'DAI', + expectedAmount: '1000000000000000000', + payee: payeeIdentity, + payer: payerIdentity, + }, + signer: payeeIdentity, +}); +``` + +For additional ERC20 tokens, or specific neworks, you have to mention the contract address and network identifier. +```typescript +const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: { + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + }, + expectedAmount: '1000', + payee: payeeIdentity, + payer: payerIdentity, + }, + signer: payeeIdentity, +}); +``` + +The most convenient way to implement ERC20 requests is with a *proxy contract* payment network. +Note that the smart contract deployed for ERC20 tokens is different than the one deployed for ether. + +ERC20 requests payment detection can also be address based, but using the proxy contract payment network is the most convenient. + +### Other currencies + +If you would like to create a request with a currency we don't support, you have two options: +- Create a declarative request +- Contact us for your currency requirements: **Join the Request Hub** [**here**](https://join.slack.com/t/requesthub/shared_invite/enQtMjkwNDQwMzUwMjI3LWNlYTlmODViMmE3MzY0MWFiMTUzYmNiMWEyZmNiNWZhMjM3MTEzN2JkZTMxN2FhN2NmODFkNmU5MDBmOTUwMjA) +- Contribute to the protocol creating a dedicated payment network for this currency, by: + - Writing the specification (following the [advanced logic specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/advanced-logic-specs-0.1.0.md) and get inspired by the [others payment networks](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/advanced-logic/specs)) + - Developing the new payment network in the [advanced-logic package](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/advanced-logic/src/extensions/payment-network). + - Developing the payment detection in the payment [payment-detection package](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/payment-detection). + - (OPTIONAL) Developing the payment processing in the [payment-processor package](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/payment-processor) diff --git a/packages/docs/docs/guides/5-request-client/2-erc20-payment-detection.ts b/packages/docs/docs/guides/5-request-client/2-erc20-payment-detection.ts new file mode 100644 index 0000000000..3512f9344e --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/2-erc20-payment-detection.ts @@ -0,0 +1,171 @@ +/** + * # Creating an ERC20 request + * + * This is an example of creating a request using an erc20 payment network + * The request will be stored in memory and cleared as soon as the script is finished running. + */ + +/** + * ## Basics + * + * For both erc20 payment networks, Request Client must be initialized. + */ + +/** + * ### Imports + * + * First we import the 2 packages we will need to create the request: + */ +// The signature provider allows us to sign the request +import { EthereumPrivateKeySignatureProvider } from '@requestnetwork/epk-signature'; +// RequestNetwork is the interface we will use to interact with the Request network +import * as RequestNetwork from '@requestnetwork/request-client.js'; + +/** + * ### Identity + * + * To create a request we need to declare the identities of the parties involved. + * Identities are the unique identifier of a request user. They are generally different from payment addresses but can be the same. They identify an entity like a person or business. + */ + +// Here we declare the payee identity, with the payee identity ethereum address +const payeeIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0x627306090abab3a6e1400e9345bc60c78a8bef57', +}; + +// Here we declare the (optional, but recommended) payer identity address. +const payerIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0xF317BedAA5c389F2C6f469FcF25e0752C7228Ba6', +}; + +/** + * ### Signature Provider + * + * The Ethereum private key signature provider allows a user to pass in their private Ethereum key to sign a request. The signer is either the payee or the payer. + * The signature is proof of who created the request and of its integrity (that no data changed after it was signed). + * This process is similar to the signature of an Ethereum transaction. + */ + +// The signature info requires the request creator private key. +// For this demo purposes, we hard-coded the private key. Please be careful with how you store and handle your private key since it's a very sensitive piece of data. +const payeeSignatureInfo = { + method: RequestNetwork.Types.Signature.METHOD.ECDSA, + privateKey: '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3', +}; + +const signatureProvider = new EthereumPrivateKeySignatureProvider(payeeSignatureInfo); + +// We can initialize the RequestNetwork class with the signature provider and inform we will be using the mock storage. +const requestNetwork = new RequestNetwork.RequestNetwork({ + signatureProvider, + useMockStorage: true, +}); + +/** + * ### Request Information + * + * In the next section of code we declare the request information. + */ + +// The main request info, with the currency, amount (in the smallest denominator), payee identity and payer identity +const requestInfo: RequestNetwork.Types.IRequestInfo = { + currency: 'REQ', + expectedAmount: '1000000000000000000', // 1 REQ + payee: payeeIdentity, + payer: payerIdentity, +}; + +/** + * ## Request creation with the proxy contract payment network + * + * To create a request using erc20 proxy contract payment network, we need to provide the payment network parameters to the request creation parameters. + * Proxy contract payment network parameters are the same as the address-based payment network: the payment and eventually the refund address. + */ + +const proxyContractPaymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress: '0x92FC764853A9A0287b7587E59aDa47165b3B2675', + }, +}; + +const proxyContractCreateParams = { + proxyContractPaymentNetwork, + requestInfo, + signer: payeeIdentity, +}; + +// Finally create the request and print its id +(async () => { + const request = await requestNetwork.createRequest(proxyContractCreateParams); + + console.log(`Request created with erc20 proxy contract payment network: ${request.requestId}`); +})(); + +/** + * ## Request creation with address-based payment network + * + * To create a request using the erc20 address-based payment network, we need to provide the payment network parameters to the request creation parameters. + */ + +const addressBasedPaymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED, + parameters: { + paymentAddress: '0x92FC764853A9A0287b7587E59aDa47165b3B2675', + }, +}; + +const addressBasedCreateParams = { + addressBasedPaymentNetwork, + requestInfo, + signer: payeeIdentity, +}; + +// Finally create the request and print its id +(async () => { + const request = await requestNetwork.createRequest(addressBasedCreateParams); + + console.log('Request created with erc20 address based payment network:'); + console.log(request.requestId); +})(); + +/** + * ## Checking the balance of an erc20 request + * + * The function getData() of a request provides its balance + * This function is available independently of the payment network used + */ + +// Import Big Number package +const BN = require('bn.js'); + +(async () => { + // Use a proxy contract request for example + const request = await requestNetwork.createRequest(proxyContractCreateParams); + + // Check the balance of the request + const requestData = request.getData(); + const balanceObject = requestData.balance; + + if (!balanceObject) { + console.error('balance no set'); + return; + } + + if (balanceObject.error) { + console.error(balanceObject.error.message); + return; + } + + console.log(`Balance of the erc20 proxy contract request: ${balanceObject.balance}`); + + // Check if the request has been paid + // Convert the balance to big number type for comparison + const expectedAmount = new BN(requestData.expectedAmount); + const balanceBigNumber = new BN(balanceObject.balance); + + // Check if balanceBigNumber is greater or equal to expectedAmount + const paid = balanceBigNumber.gte(expectedAmount); +})(); diff --git a/packages/docs/docs/guides/5-request-client/3-eth-payment-detection.ts b/packages/docs/docs/guides/5-request-client/3-eth-payment-detection.ts new file mode 100644 index 0000000000..221e95429e --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/3-eth-payment-detection.ts @@ -0,0 +1,139 @@ +/** + * # Creating a request in ETH + * + * This is an example of creating a request using the ether payment network + * The request will be stored in memory and cleared as soon as the script is finished running. + */ + +/** + * ## Basics + * + * Before creating a request, the Request Client must be initialized. + */ + +/** + * ### Imports + * + * First we import the 2 packages we will need to create the request: + */ +// The signature provider allows us to sign the request +import { EthereumPrivateKeySignatureProvider } from '@requestnetwork/epk-signature'; +// RequestNetwork is the interface we will use to interact with the Request network +import * as RequestNetwork from '@requestnetwork/request-client.js'; + +/** + * ### Identity + * + * To create a request we need to declare the identities of the parties involved. + * Identities are the unique identifier of a request user. They are generally different from payment addresses but can be the same. They identify an entity like a person or business. + */ + +// Here we declare the payee identity, with the payee identity ethereum address +const payeeIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0x627306090abab3a6e1400e9345bc60c78a8bef57', +}; + +// Here we declare the (optional, but recommended) payer identity address. +const payerIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0xF317BedAA5c389F2C6f469FcF25e0752C7228Ba6', +}; + +/** + * ### Signature Provider + * + * The Ethereum private key signature provider allows a user to pass in their private Ethereum key to sign a request. The signer is either the payee or the payer. + * The signature is proof of who created the request and of its integrity (that no data changed after it was signed). + * This process is similar to the signature of an Ethereum transaction. + */ + +// The signature info requires the request creator private key. +// For this demo purposes, we hard-coded the private key. Please be careful with how you store and handle your private key since it's a very sensitive piece of data. +const payeeSignatureInfo = { + method: RequestNetwork.Types.Signature.METHOD.ECDSA, + privateKey: '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3', +}; + +const signatureProvider = new EthereumPrivateKeySignatureProvider(payeeSignatureInfo); + +// We can initialize the RequestNetwork class with the signature provider and inform we will be using the mock storage. +const requestNetwork = new RequestNetwork.RequestNetwork({ + signatureProvider, + useMockStorage: true, +}); + +/** + * ### Request Information + * + * In the next section of code we declare the request information. + */ + +// The main request info, with the currency, amount (in the smallest denominator), payee identity and payer identity +const requestInfo: RequestNetwork.Types.IRequestInfo = { + currency: 'ETH', + expectedAmount: '1000000000000000000', // The amount is in wei, this equals 1 Ether + payee: payeeIdentity, + payer: payerIdentity, +}; + +/** + * ## Request creation + * + * To create a request using ether payment network, we need to provide the payment network parameters to the request creation parameters. + * For the ether payment network, it's simply the payment address (and eventually the refund address). + */ + +const ethInputDataPaymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0x92FC764853A9A0287b7587E59aDa47165b3B2675', + }, +}; + +const ethInputDataCreateParams = { + ethInputDataPaymentNetwork, + requestInfo, + signer: payeeIdentity, +}; + +// Finally create the request and print its id +(async () => { + const request = await requestNetwork.createRequest(ethInputDataCreateParams); + + console.log(`Request created with ether payment network: ${request.requestId}`); +})(); + +/** + * ## Checking balance + * + * The function getData() of a request provides its balance + */ + +// Import Big Number package +const BN = require('bn.js')(async () => { + const request = await requestNetwork.createRequest(ethInputDataCreateParams); + + // Check the balance of the request + const requestData = request.getData(); + const balanceObject = requestData.balance; + + if (!balanceObject) { + console.error('balance no set'); + return; + } + if (balanceObject.error) { + console.error(balanceObject.error.message); + return; + } + + console.log(`Balance of the ether input data request: ${balanceObject.balance}`); + + // Check if the request has been paid + // Convert the balance to big number type for comparison + const expectedAmount = new BN(requestData.expectedAmount); + const balanceBigNumber = new BN(balanceObject.balance); + + // Check if balanceBigNumber is greater or equal to expectedAmount + const paid = balanceBigNumber.gte(expectedAmount); +})(); diff --git a/packages/docs/docs/guides/5-request-client/4-btc-payment-detection.ts b/packages/docs/docs/guides/5-request-client/4-btc-payment-detection.ts new file mode 100644 index 0000000000..2046cf3e0f --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/4-btc-payment-detection.ts @@ -0,0 +1,141 @@ +/** + * # Creating a request in BTC + * + * This is an example of creating a request using the Bitcoin payment network + * The request will be stored in memory and cleared as soon as the script is finished running. + */ + +/** + * ## Basics + * + * Before creating a request, the Request Client must be initialized. + */ + +/** + * ### Imports + * + * First we import the 2 packages we will need to create the request: + */ +// The signature provider allows us to sign the request +import { EthereumPrivateKeySignatureProvider } from '@requestnetwork/epk-signature'; +// RequestNetwork is the interface we will use to interact with the Request network +import * as RequestNetwork from '@requestnetwork/request-client.js'; + +/** + * ### Identity + * + * To create a request we need to declare the identities of the parties involved. + * Identities are the unique identifier of a request user. They are generally different from payment addresses but can be the same. They identify an entity like a person or business. + */ + +// Here we declare the payee identity, with the payee identity ethereum address +const payeeIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0x627306090abab3a6e1400e9345bc60c78a8bef57', +}; + +// Here we declare the (optional, but recommended) payer identity address. +const payerIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0xF317BedAA5c389F2C6f469FcF25e0752C7228Ba6', +}; + +/** + * ### Signature Provider + * + * The Ethereum private key signature provider allows a user to pass in their private Ethereum key to sign a request. The signer is either the payee or the payer. + * The signature is proof of who created the request and of its integrity (that no data changed after it was signed). + * This process is similar to the signature of an Ethereum transaction. + */ + +// The signature info requires the request creator private key. +// For this demo purposes, we hard-coded the private key. Please be careful with how you store and handle your private key since it's a very sensitive piece of data. +const payeeSignatureInfo = { + method: RequestNetwork.Types.Signature.METHOD.ECDSA, + privateKey: '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3', +}; + +const signatureProvider = new EthereumPrivateKeySignatureProvider(payeeSignatureInfo); + +// We can initialize the RequestNetwork class with the signature provider and inform we will be using the mock storage. +const requestNetwork = new RequestNetwork.RequestNetwork({ + signatureProvider, + useMockStorage: true, +}); + +/** + * ### Request Information + * + * In the next section of code we declare the request information. + */ + +// The main request info, with the currency, amount (in the smallest denominator), payee identity and payer identity +const requestInfo: RequestNetwork.Types.IRequestInfo = { + currency: 'BTC', + expectedAmount: '100000000 ', // The amount is in satoshi, this equals 1 BTC + payee: payeeIdentity, + payer: payerIdentity, +}; + +/** + * ## Request creation + * + * To create a request using Bitcoin payment network, we need to provide the payment network parameters to the request creation parameters. + * For the Bitcoin payment network, it's simply the payment address (and eventually the refund address). + */ + +// If you want to create a request with testnet bitcoin you must use the payment network: +// RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED +const bitcoinAddressBasedPaymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, + parameters: { + paymentAddress: '16uyvigo8mMAfE3Ctr5Rwgab1aWNDPDMZD', + }, +}; + +const bitcoinAddressBasedCreateParams = { + bitcoinAddressBasedPaymentNetwork, + requestInfo, + signer: payeeIdentity, +}; + +// Finally create the request and print its id +(async () => { + const request = await requestNetwork.createRequest(bitcoinAddressBasedCreateParams); + + console.log(`Request created with Bitcoin payment network: ${request.requestId}`); +})(); + +/** + * ## Checking balance + * + * The function getData() of a request provides its balance + */ + +// Import Big Number package +const BN = require('bn.js')(async () => { + const request = await requestNetwork.createRequest(bitcoinAddressBasedCreateParams); + + // Check the balance of the request + const requestData = request.getData(); + const balanceObject = requestData.balance; + + if (!balanceObject) { + console.error('balance no set'); + return; + } + if (balanceObject.error) { + console.error(balanceObject.error.message); + return; + } + + console.log(`Balance of the bitcoin address based request: ${balanceObject.balance}`); + + // Check if the request has been paid + // Convert the balance to big number type for comparison + const expectedAmount = new BN(requestData.expectedAmount); + const balanceBigNumber = new BN(balanceObject.balance); + + // Check if balanceBigNumber is greater or equal to expectedAmount + const paid = balanceBigNumber.gte(expectedAmount); +})(); diff --git a/packages/docs/docs/guides/5-request-client/5-declarative-payment-network.ts b/packages/docs/docs/guides/5-request-client/5-declarative-payment-network.ts new file mode 100644 index 0000000000..3f799993fc --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/5-declarative-payment-network.ts @@ -0,0 +1,160 @@ +/** + * # Creating a request using the Declarative payment network + * + * This is an example of creating a request using the Declarative payment network + * The request will be stored in memory and cleared as soon as the script is finished running. + */ + +/** + * ## Basics + * + * Before creating a request, the Request Client must be initialized. + */ + +/** + * ### Imports + * + * First we import the 2 packages we will need to create the request: + */ +// The signature provider allows us to sign the request +import { EthereumPrivateKeySignatureProvider } from '@requestnetwork/epk-signature'; +// RequestNetwork is the interface we will use to interact with the Request network +import * as RequestNetwork from '@requestnetwork/request-client.js'; + +/** + * ### Identity + * + * To create a request we need to declare the identities of the parties involved. + * Identities are the unique identifier of a request user. They are generally different from payment addresses but can be the same. They identify an entity like a person or business. + */ + +// Here we declare the payee identity, with the payee identity ethereum address +const payeeIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0x627306090abab3a6e1400e9345bc60c78a8bef57', +}; + +// Here we declare the (optional, but recommended) payer identity address. +const payerIdentity = { + type: RequestNetwork.Types.Identity.TYPE.ETHEREUM_ADDRESS, + value: '0xF317BedAA5c389F2C6f469FcF25e0752C7228Ba6', +}; + +/** + * ### Signature Provider + * + * The Ethereum private key signature provider allows a user to pass in their private Ethereum key to sign a request. The signer is either the payee or the payer. + * The signature is proof of who created the request and of its integrity (that no data changed after it was signed). + * This process is similar to the signature of an Ethereum transaction. + */ + +// The signature info requires the request creator private key. +// For this demo purposes, we hard-coded the private key. Please be careful with how you store and handle your private key since it's a very sensitive piece of data. +const payeeSignatureInfo = { + method: RequestNetwork.Types.Signature.METHOD.ECDSA, + privateKey: '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3', +}; + +const signatureProvider = new EthereumPrivateKeySignatureProvider(payeeSignatureInfo); + +// We can initialize the RequestNetwork class with the signature provider and inform we will be using the mock storage. +const requestNetwork = new RequestNetwork.RequestNetwork({ + signatureProvider, + useMockStorage: true, +}); + +/** + * ### Request Information + * + * In the next section of code we declare the request information. + */ + +// The main request info, with the currency, amount (in the smallest denominator), payee identity and payer identity +const requestInfo: RequestNetwork.Types.IRequestInfo = { + currency: 'EUR', // The Declarative payment network is currency agnostic, we can use any currency supported by Request + expectedAmount: '100', // This equals 1 EUR + payee: payeeIdentity, + payer: payerIdentity, +}; + +/** + * ## Request creation with the declarative payment network + * + * To create a request using a declarative payment network, we need to provide the payment network parameters to the request creation parameters. + * The parameters are paymentInfo and optionally refundInfo + * This value can be any Javascript object. This object should allow the payer to know how to pay the request. + * For example, for a European transfer, we could provide the IBAN and the BIC of the receiving bank account + */ + +const declarativePaymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: { + paymentInfo: { + IBAN: 'FR123456789123456789', + BIC: 'CE123456789', + }, + }, +}; + +const declarativeCreateParams = { + declarativePaymentNetwork, + requestInfo, + signer: payeeIdentity, +}; + +// Finally create the request and print its id +(async () => { + const request = await requestNetwork.createRequest(declarativeCreateParams); + + console.log(`Request created with Declarative payment network: ${request.requestId}`); +})(); + +/** + * ## Declaring sent and received payments and checking balance + * + * The Declarative payment network doesn't provide payment detection method to determine the balance of the request + * The balance of the request is defined by the declared payments by the payee and the declared refunds by the payer + */ + +// Import Big Number package +const BN = require('bn.js')(async () => { + const request = await requestNetwork.createRequest(declarativeCreateParams); + + // Declare received payments + // The payee can declare the amount received, this amount will be added to the balance of the request + request.declareReceivedPayment('1000', 'payment received', payeeIdentity); + + // The payer can declare a sent payment, this amount is not taken into account for the request balance + // But the note provided can help to solve dispute + request.declareSentPayment('1000', 'payment sent', payerIdentity); + + // Declare received refunds + // The payer can declare received refunds, this amount will be subtracted from the balance of the request + request.declareReceivedPayment('900', 'refund received', payerIdentity); + + // Declaring a sent refund, this amount is not taken into account for the request balance + request.declareSentPayment('900', 'received too much', payeeIdentity); + + // Check the balance of the request + const requestData = request.getData(); + const balanceObject = requestData.balance; + + if (!balanceObject) { + console.error('balance no set'); + return; + } + if (balanceObject.error) { + console.error(balanceObject.error.message); + return; + } + + console.log(`Balance of the declarative request: ${balanceObject.balance}`); + + // Check if the request has been paid + // Convert the balance to big number type for comparison + const expectedAmount = new BN(requestData.expectedAmount); + const balanceBigNumber = new BN(balanceObject.balance); + + // Check if balanceBigNumber is greater or equal to expectedAmount + const paid = balanceBigNumber.gte(expectedAmount); +})(); diff --git a/packages/docs/docs/guides/5-request-client/6-signature-provider.md b/packages/docs/docs/guides/5-request-client/6-signature-provider.md new file mode 100644 index 0000000000..b17842104c --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/6-signature-provider.md @@ -0,0 +1,224 @@ +--- +title: Use your own signature mechanism +keywords: [Request, signature, signatureProvider] +description: Learn how to build your own signature provider. +--- + +In a previous chapter, we used the signature providers `@requestnetwork/web3-signature` and `@requestnetwork/epk-signature` (this one is made for test purpose). But, if you are not using web3, you need to inject your own signature mechanism to the request client. +This is fairly simple, you need to implement a class following this interface: (see on [github](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/types/src/signature-provider-types.ts)) + +```typescript +export interface ISignatureProvider { + supportedMethods: Signature.METHOD[]; + supportedIdentityTypes: Identity.TYPE[]; + + sign: (data: any, signer: Identity.IIdentity) => Promise; +} +``` + +## Example 1 + +For example, your own package to sign need an ethereum address and return the signature as a hexadecimal string: + +```typescript +class mySignaturePackage { + /** + * Sign data + * + * @param data the data to sign + * @param address the address to sign with + * @returns a promise resolving the signature + */ + public async sign(data: any, address: string): Promise; +} +``` + +Your signature provider would look like: + +```typescript +import { IdentityTypes, SignatureProviderTypes, SignatureTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; + +// Your package +import mySignaturePackage from 'mySignaturePackage'; + +/** + * Implementation of the signature provider for my wallet + */ +export default class MySignatureProvider implements SignatureProviderTypes.ISignatureProvider { + /** list of supported signing method */ + public supportedMethods: SignatureTypes.METHOD[] = [SignatureTypes.METHOD.ECDSA]; + /** list of supported identity types */ + public supportedIdentityTypes: IdentityTypes.TYPE[] = [IdentityTypes.TYPE.ETHEREUM_ADDRESS]; + + /** + * Signs data + * + * @param string data the data to sign + * @returns IIdentity the identity to sign with if not given, the default signer will be used + * + * @returns string the signature + */ + public async sign( + data: any, + signer: IdentityTypes.IIdentity, + ): Promise { + if (!this.supportedIdentityTypes.includes(signer.type)) { + throw Error(`Identity type not supported ${signer.type}`); + } + + // Hash the normalized data (e.g. avoid case sensitivity) + const hashData = Utils.crypto.normalizeKeccak256Hash(data).value; + + // use your signature package + const signatureValue = mySignaturePackage.sign(hashData, signer.value); + + return { + data, + signature: { + method: SignatureTypes.METHOD.ECDSA, + value: signatureValue, + }, + }; + } +} +``` + +Now you can inject it into the request client: + +```typescript +import MySignatureProvider from 'mySignatureProvider'; + +const mySignatureProvider = new MySignatureProvider(); + +// We can initialize the RequestNetwork class with the signature provider +const requestNetwork = new RequestNetwork.RequestNetwork({ + signatureProvider: mySignatureProvider, +}); +``` + +## Example 2 + +For example, your own package to sign need an internal identifier and return the signature as a Buffer: + +```typescript +class mySignaturePackage { + /** + * Sign a Buffer + * + * @param data the data to sign + * @param walletId a way to get the right wallet to sign with + * @returns a promise resolving the signature + */ + public async sign(data: Buffer, walletId: number): Promise; +} +``` + +Your signature provider would look like: + +```typescript +import { IdentityTypes, SignatureProviderTypes, SignatureTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; + +// Your package +import mySignaturePackage from 'mySignaturePackage'; + +/** Type of the dictionary of wallet id indexed by address */ +type IWalletIdDictionary = Map; + +/** + * Implementation of the signature provider for my wallet + */ +export default class MySignatureProvider + implements SignatureProviderTypes.ISignatureProvider { + /** list of supported signing method */ + public supportedMethods: SignatureTypes.METHOD[] = [SignatureTypes.METHOD.ECDSA]; + /** list of supported identity types */ + public supportedIdentityTypes: IdentityTypes.TYPE[] = [IdentityTypes.TYPE.ETHEREUM_ADDRESS]; + + /** Dictionary containing all the private keys indexed by address */ + private walletIdDictionary: IWalletIdDictionary; + + constructor(identity?: IdentityTypes.IIdentity?, walletId?: number) { + this.walletIdDictionary = new Map(); + + if (identity && walletId) { + this.addSignatureParameters(identity, walletId); + } + } + + /** + * Signs data + * + * @param string data the data to sign + * @returns IIdentity the identity to sign with. If not given, the default signer will be used + * + * @returns string the signature + */ + public async sign( + data: any, + signer: IdentityTypes.IIdentity, + ): Promise { + if (!this.supportedIdentityTypes.includes(signer.type)) { + throw Error(`Identity type not supported ${signer.type}`); + } + + // toLowerCase to avoid mismatch because of case + const walletId: number | undefined = this.walletIdDictionary.get(signer.value.toLowerCase()); + + if (!walletId) { + throw Error(`Identity unknown: ${signer.type}, ${signer.value}`); + } + + // Hash the normalized data (e.g. avoid case sensitivity) + const hashData = Utils.crypto.normalizeKeccak256Hash(data).value; + + // convert the hash from a string '0x...' to a Buffer + const hashDataBuffer = Buffer.from(hashData.slice(2), 'hex') + + // use your signature package + const signatureValueBuffer = mySignaturePackage.sign(hashDataBuffer, walletId); + + // convert the signature to a string '0x...' + const signatureValue = `0x${signatureValueBuffer.toString('hex')}` + + return { + data, + signature: { + method: SignatureTypes.METHOD.ECDSA, + value: signatureValue, + }, + }; + } + + /** + * Function to add a new identity in the provider + * + * @param identity the new identity + * @param walletId the wallet id matching the identity + */ + public addIdentity(identity: IdentityTypes.IIdentity, walletId: number): void { + if (!this.supportedIdentityTypes.includes(identity.type)) { + throw Error(`Identity type not supported ${identity.type}`); + } + + this.walletIdDictionary.set(identity.value.toLowerCase(), walletId); + } +} +``` + +Now you can inject it into the request client: + +```typescript +import MySignatureProvider from 'mySignatureProvider'; + +const mySignatureProvider = new MySignatureProvider(anIdentity, aWalletId); + +// We can initialize the RequestNetwork class with the signature provider +const requestNetwork = new RequestNetwork.RequestNetwork({ + signatureProvider: mySignatureProvider, +}); + +// later on, you can even add more supported identities +mySignatureProvider.addIdentity(anotherIdentity, anotherWalletId); +``` diff --git a/packages/docs/docs/guides/5-request-client/7-encryption.md b/packages/docs/docs/guides/5-request-client/7-encryption.md new file mode 100644 index 0000000000..6a36a095d5 --- /dev/null +++ b/packages/docs/docs/guides/5-request-client/7-encryption.md @@ -0,0 +1,119 @@ +--- +title: Handling encryption with the JS library +keywords: [Request, new currency, test network, missing currency, testnet] +description: Learn how to integrate Request network and its features. +--- + +| WARNING: The Request encryption mechanism is still under audit, use it at your own risk! | +| ---------------------------------------------------------------------------------------- | + + +| WARNING: Manipulate private keys must be done with care, losing them can lead to a loss of data and privacy! | +| ------------------------------------------------------------------------------------------------------------ | + + +A request can be encrypted in order to make its details private to selected stakeholders. In this guide, we won't explain how encryption is managed under the hood. We will mention encryption or decryption of requests with payers' and payee's keys, wherein the reality we use an intermediate symmetric key. See more details on [github](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/transaction-manager/specs/encryption.md) + +The encryption is managed by the transaction layer, see more details on [the Request Protocol section](../7-protocol/2-transaction.md). + +To manipulate encrypted request you need a Decryption Provider, e.g: + +- Ethereum Private Key Decryption Provider (provided by Request for illustration), using directly the private keys. _This provider manipulates private keys in clear, which is not completely secure. Please consider creating your own, see below._ +- A browser extension is under development + +You can also create your own decryption provider following the [specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/transaction-manager/specs/decryption-provider.md). Feel free to contact us for any help or any idea about it: **Join the Request Hub** [**here**](https://join.slack.com/t/requesthub/shared_invite/enQtMjkwNDQwMzUwMjI3LWNlYTlmODViMmE3MzY0MWFiMTUzYmNiMWEyZmNiNWZhMjM3MTEzN2JkZTMxN2FhN2NmODFkNmU5MDBmOTUwMjA) + +## Create an encrypted request + +Ethereum Private Key Decryption Provider (see on [github](https://github.com/RequestNetwork/requestNetwork/tree/development/packages/epk-decryption)) + +```typescript +import EPKDecryptionProvider from '@requestnetwork/epk-decryption'; + +const decryptionProvider = new EPKDecryptionProvider({ + # Warning: private keys should never be stored in clear, this is a basic tutorial + key: '0x4025da5692759add08f98f4b056c41c71916a671cedc7584a80d73adc7fb43c0', + method: RequestNetwork.Types.Encryption.METHOD.ECIES, +}); + +const requestNetwork = new RequestNetwork({ + decryptionProvider, + signatureProvider, + useMockStorage: true, +}); +``` + +Then you can create an encrypted request: + +```typescript +const payeeEncryptionPublicKey = { + key: + 'cf4a1d0bbef8bf0e3fa479a9def565af1b22ea6266294061bfb430701b54a83699e3d47bf52e9f0224dcc29a02721810f1f624f1f70ea3cc5f1fb752cfed379d', + method: RequestNetwork.Types.Encryption.METHOD.ECIES, +}; +const payerEncryptionPublicKey = { + key: + '299708c07399c9b28e9870c4e643742f65c94683f35d1b3fc05d0478344ee0cc5a6a5e23f78b5ff8c93a04254232b32350c8672d2873677060d5095184dad422', + method: RequestNetwork.Types.Encryption.METHOD.ECIES, +}; + +const invoice = await requestNetwork._createEncryptedRequest( + { + requestParameters, + signer: requestParameters.payee, + paymentNetwork, + }, + [payeeEncryptionPublicKey, payerEncryptionPublicKey], +); +``` + +Note: You must give at least one encryption key you can decrypt with the decryption provider. Otherwise, an error will be triggered after the creation. + +## Get invoice information from its request ID + +Let's step back for a second: the requester sent a request that he encrypted with the payer's public key, as well as with his own, in order to retrieve it later. This is a basic and typical example, but a request can be encrypted with many keys, in order to give access to its status and details. + +If the decryption provider knows a private key matching one of the keys used at the creation, it can decrypt it. Like a clear request you will be able to get it from its request id a request. + +```typescript +const invoiceFromRequestID = await requestNetwork.fromRequestId(requestId); + +const requestData = invoiceFromRequestID.getData(); + +console.log(requestData); + +/* { + requestId, + currency, + expectedAmount, + payee, + payer, + timestamp, + extensions, + version, + events, + state, + creator, + meta, + balance, + contentData, +} */ +``` + +## Accepting/cancelling an invoice information + +Like a clear request, you will be able to update it if the decryption provider is instantiated with a matching private key. + +```typescript +//Accept +await request.accept(payerIdentity); + +//Cancel +await request.cancel(payeeIdentity); + +//Increase the expected amount +await request.decreaseExpectedAmountRequest(amount, payeeIdentity); + +//Decrease the expected amount +await request.increaseExpectedAmountRequest(amount, payerIdentity); +``` diff --git a/packages/docs/docs/guides/6-hosting-a-node/0-intro.md b/packages/docs/docs/guides/6-hosting-a-node/0-intro.md new file mode 100644 index 0000000000..31fbffc0d7 --- /dev/null +++ b/packages/docs/docs/guides/6-hosting-a-node/0-intro.md @@ -0,0 +1,50 @@ +--- +title: Introduction to Request Node hosting +keywords: [Request node] +description: Learn how to integrate Request network and its features. +--- + +Now you should be comfortable with features of the Request network. + +In this guide, we will explain what is the Request Node and help you to run your own. + +## What is the Request Node? + +Request Nodes are servers that run the lower layers of the Request Protocol. They connect to the Ethereum and IPFS networks, to store and retrieve request transactions. Protocol users can interact with the Request Node through HTTP using the [request-client.js](../5-request-client/0-intro.md) library. + +## Why run your own Node? + +First, running the Node locally on your machine will allow you to test your code using the Request Client easily. + +You may also want to host your Node in a server. Hosting your Node is the most decentralized setup possible. It allows you to: + +- Store your data and make sure it is safely backed up +- Be technically independent: own your servers and control how you manage them +- Use custom configuration settings + +## How to run your Node? + +There are currently three supported ways to run a Request Node: + +- Run from [**Docker**](./1-docker.md). The easiest way to run the Request Node. +- Run the [**code**](./2-code.md) from the git repository. Especially useful if you are making changes to the protocol layers. +- Use our kubernetes [**helm**](./3-helm.md) charts. The best solution if you want to host your Node on a Kubernetes cluster. + +On the next pages, you can find out detailed steps on how to run each one of these. + +## Prerequisites + +Request uses IPFS and Ethereum to store request transactions. For this reason, the Node needs connections to an Ethereum node and an IPFS node. + +### Ethereum node + +You can use any HTTP/S Ethereum node to run your Request Node. +For local development, you can use ganache-cli, a local Ethereum RPC client for tests (explained in more detail on the following pages). + +An easy way to get going with Ethereum Mainnet or Rinkeby is to use services like Infura, that will expose an Ethereum node API for you. + +### IPFS node + +Request uses a dedicated IPFS network to store our data. This means that you will need an IPFS node configured to connect to our network. You can check [this page](../7-protocol/6-request-ipfs-network.md) if you want more details on our dedicated network. + +The good news is it's easy to set up our IPFS node and we will show it to you on our next steps. diff --git a/packages/docs/docs/guides/6-hosting-a-node/1-docker.md b/packages/docs/docs/guides/6-hosting-a-node/1-docker.md new file mode 100644 index 0000000000..26070571e0 --- /dev/null +++ b/packages/docs/docs/guides/6-hosting-a-node/1-docker.md @@ -0,0 +1,77 @@ +--- +title: Running a node with Docker +keywords: [Request node, setup, docker] +description: Learn how to integrate Request network and its features. +--- + +Running a Request Node with Docker is easy. There are only a few requirements: + +- Docker installed on your system; +- A web3 provider (we recommend using a service like [infura](https://infura.io)); +- An Ethereum wallet with some funds for gas (if you plan on creating requests through this node); + +## Launching the IPFS node + +To launch the IPFS node run: + +```bash +docker run -p 5001:5001 -p 4001:4001 requestnetwork/request-ipfs +``` + +This command will launch the IPFS node with Request network configurations. + +## Launching the Request Node + +To launch the Request node you can run: + +```bash +docker run -p 3000:3000 -e MNEMONIC="" -e WEB3_PROVIDER_URL="" -e ETHEREUM_NETWORK_ID="" -e IPFS_HOST="host.docker.internal" requestnetwork/request-node +``` + +The environment variables passed to the script are: + +- **MNEMONIC** should be the node wallet mnemonic seed. +- **WEB3_PROVIDER_URL** should be the URL to your web3 provider. +- **ETHEREUM_NETWORK_ID** should be either `1` for Mainnet or `4` for Rinkeby. +- **IPFS_HOST** is the URL of your IPFS node. Here we use the Docker host URL. + +That's it! Now your Node should be running and syncing to the network. +Give it some minutes to finish synchronizing and its API will be available on `http://localhost:3000`. + +If you want to know more about the available options you can pass to the node, you can [check them here](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-node#options). + +## Using Docker Compose + +We can (and should) use docker-compose to make it simpler to launch your local Request Node. +With [Docker Compose](https://docs.docker.com/compose/) installed, use the following `docker-compose.yml` file: + +```yml +version: '3.1' + +services: + request-node: + image: requestnetwork/request-node + environment: + IPFS_HOST: ipfs + ETHEREUM_NETWORK_ID: 4 + WEB3_PROVIDER_URL: https://rinkeby.infura.io/v3/ + MNEMONIC: + ports: + - '3000:3000' + depends_on: + - ipfs + + ipfs: + image: requestnetwork/request-ipfs + ports: + - '4001' + - '5001' +``` + +Now you can run: + +```bash +docker-compose up +``` + +Your node should start initializing. diff --git a/packages/docs/docs/guides/6-hosting-a-node/2-code.md b/packages/docs/docs/guides/6-hosting-a-node/2-code.md new file mode 100644 index 0000000000..f23fadb17b --- /dev/null +++ b/packages/docs/docs/guides/6-hosting-a-node/2-code.md @@ -0,0 +1,102 @@ +--- +title: Running from the code repository +keywords: [Request node, test, ganache, local node] +description: Learn how to integrate Request network and its features. +--- + +If you can't use Docker or you want to run your node locally, from the source code, you can follow the steps in this document. +Running the Node in this way is useful for debugging and developing the Node itself. + +# Running locally + +To run a Request Node locally for tests, make sure you have the necessary IPFS and Ethereum nodes available. + +You can run the following steps to launch a fully local test Request Node. + +## Cloning the repository + +Let's clone the repository, install and build dependencies: + +```bash +git clone https://github.com/RequestNetwork/requestNetwork.git +cd requestNetwork +yarn install +yarn build +``` + +You are ready to run the local test Node. You will need three different consoles for Ethereum, IPFS, and Request. + +## Launching IPFS locally + +First, make sure you [installed IPFS](https://docs.ipfs.io/guides/guides/install/) locally. + +Now you need to configure your IPFS to connect to our [dedicated network](../7-protocol/6-request-ipfs-network.md). We have a script to make it easy for you: + +```bash +cd packages/request-node +yarn init-ipfs +``` + +Now you can run IPFS with: + +```bash +ipfs daemon +``` + +## Running an Ethereum node + +If you want to test using Ethereum mainnet and rinkeby, you can launch your Ethereum node or connect to a service like infura. + +If you want to debug and test, you may be interested in using a local Ethereum network. + +### Local network using docker + +The easiest way to run a local Ethereum network is by using our pre-configured ganache Docker image. +If you have Docker you can just run: + +``` +docker run --name ganache -d -p 8545:8545 requestnetwork/ganache +``` + +### Local network using ganache-cli + +You can also run ganache-cli to set up a local network. + +Install and run [ganache-cli](https://github.com/trufflesuite/ganache-cli) using: + +```bash +yarn global add ganache-cli +cd packages/smart-contracts +yarn ganache +``` + +Now you have ganache-cli running on your second console. +We're still missing all the important smart-contracts that Request uses. On a new console, run: + +```bash +cd packages/smart-contracts +yarn deploy +``` + +Done! Your local Ethereum network is ready for testing. + +## Running the Request Node + +Now it's time to run the Node: + +```bash +cd packages/request-node +yarn start +``` + +Your Request Node should be running! If you want to run it using a different Ethereum network, mnemonic, or a different IPFS server, you can check out the available options for the node [here](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-node#options). + +### NPX + +If for some reason you want to run the Node without Docker, but don't need to make changes to the repository, you can also use npx to run it directly from npm: + +```bash +npx @requestnetwork/request-node [OPTIONS] +``` + +If you got to this point you know what Node [options](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-node#options) you should be using 🙂. diff --git a/packages/docs/docs/guides/6-hosting-a-node/3-helm.md b/packages/docs/docs/guides/6-hosting-a-node/3-helm.md new file mode 100644 index 0000000000..211b14b442 --- /dev/null +++ b/packages/docs/docs/guides/6-hosting-a-node/3-helm.md @@ -0,0 +1,31 @@ +--- +title: Deploying a node in Kubernetes with Helm +keywords: [Request node, helm, kubernetes] +description: Learn how to integrate Request network and its features. +--- + +Deploying a Request Node on Kubernetes is straightforward using our [helm](https://helm.sh/) chart. + +You can see our chart in our [git repository](https://github.com/RequestNetwork/request-helm-charts/tree/master/request-node). + +## Adding the chart + +We host our chart on our helm repository, since we upgrade it frequently. +To add our chart you can run: + +```bash +helm repo add request https://request-charts.storage.googleapis.com +helm repo update +``` + +## Installing the chart + +To install our chart with the release name `my-release`, you can run: + +```bash +helm install --name my-release request/request-node --set nodeEnv.mnemonic=,nodeEnv.web3ProviderUrl=,nodeEnv.networkId= +``` + +You will need to set up some required values, like mnemonic, web3ProviderUrl (you can use [infura](https://www.infura.io) API) and networkId (either `1` for mainnet or `4` for Rinkeby). + +You can check out all our chart configuration options [here](https://github.com/RequestNetwork/request-helm-charts/tree/master/request-node#configuration). diff --git a/packages/docs/docs/guides/7-protocol/0-intro.md b/packages/docs/docs/guides/7-protocol/0-intro.md new file mode 100644 index 0000000000..2f967cd5d2 --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/0-intro.md @@ -0,0 +1,35 @@ +--- +title: Introduction to the Request Protocol +keywords: [Request protocol, IPFS, Ethereum] +description: Learn how to integrate Request network and its features. + +--- + +Request is an open and unique database for payment requests including invoices or individual payment requests. It is aimed to be universal and to power products used by different companies from startups to large organizations, from the private to the public sector. + +The Request Protocol is the core of Request. It's the bottom layer that defines and handles the data of a request and persists them to a distributed ledger to make Request open, trustless, secure and resilient. + +This section is aimed at helping you understand how the protocol is structured, how it works and how it meets its requirements. It is particularly useful if you want to propose changes or implement it yourself. + +# Overview + +The Request Protocol has one basic purpose: **to persist, on a distributed ledger, data representing requests and to be able to retrieve these data in an efficient way**. + +To organize these different purposes, the Request Protocol follows the layered architecture pattern. Each layer is responsible for a specific task and a specific level of abstraction. This layered architecture also simplifies the understandability of the code, we believe it's an important matter for an open-source project. + +The protocol is composed of four layers: +- Request logic +- Transaction +- Data Access +- Storage + +![](/img/RequestProtocol/1-LayersPresentation.jpg) +*Layers of the Request Protocol, each layer is described in the next section* + +This layered architecture allows packages reusability and makes the protocol more upgradeable. For example, our current implementation uses Ethereum and IPFS but if Storj turns out to be a better solution for storing data into a decentralized database than IPFS, we can simply create a new storage layer that uses Storj over IPFS and make the data-access layer using this new package instead. + +## Interface vs implementation + +The protocol follows a defined interface, each layer has to implement a specific interface. The interfaces for each layer can be found in the Types package of Request Network repository: [https://github.com/RequestNetwork/requestNetwork/tree/master/packages/types](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/types). + +The following pages present the first implementation of the protocol used for the released version of Request V2 on mainnet. diff --git a/packages/docs/docs/guides/7-protocol/1-request-logic.md b/packages/docs/docs/guides/7-protocol/1-request-logic.md new file mode 100644 index 0000000000..3966a1e71b --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/1-request-logic.md @@ -0,0 +1,64 @@ +--- +title: Request Logic +keywords: [Request protocol, Request Logic, Extension, Advanced Logic, Signature] +description: Learn how to integrate Request network and its features. + +--- + +This layer is responsible for the business logic of Request. This is where we define the data structure of a request. + +This layer has three responsibilities: + +- It defines the properties of the requests and the actions performed to them. +- It's responsible for the signature of the actions performed to ensure the request stakeholder identities. +- It manages extensions that can be created to extend the features of the Request Protocol through the Advanced Logic package. + +[https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-logic](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-logic) + +### Actions + +Actions are the basic elements that compose a request. At this layer's point of view, a request is simply a list of different actions. + +![](/img/RequestProtocol/2-RequestPresentation.jpg) +*Example of a request in Request Logic represented by a list of actions* + +- The payee creates the request requesting 1 ETH to the payer +- The payer accepts the request +- The payer increases the expected amount of the request by 1 ETH (the expected amount of the request can only be increased by the payer and decreased by the payee) + +Given the list of these actions, we can interpret the state of the request `0xaaa`, it's a request that has been accepted by the payer where he will have to pay 2 ETH to the payee. + +Note that the request Id is determined by the hash of the `create` action. Therefore, this action doesn't specify the request Id since it doesn't exist yet. The update actions (`accept` and `increaseExpectedAmount`) specify the request Id in their data. + +There are two kinds of action: + +- Create: This action is not related to an existing request, it will create a new one +- Update: All other actions, it will update the state of an existing request + +### Signature + +In addition to providing the structure to form an action composing a request, the logic layer is also responsible for signing the action. + +In order to abstract the signing process from the layer (and eventually be able to use it in other packages), the signing process is done through external packages named signature providers. + +The protocol repository currently contains two signature provider packages: + +- epk-signature ([https://github.com/RequestNetwork/requestNetwork/tree/master/packages/epk-signature](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/epk-signature)) +- web3-signature ([https://github.com/RequestNetwork/requestNetwork/tree/master/packages/web3-signature](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/web3-signature)) + +Both packages use the Elliptic Curve Digital Signature Algorithm (ECDSA) used in Ethereum. web3-signature will connect to Metamask to ask users to sign request while for epk-signature, the private keys are clear and managed manually. + +`web3-signature` provider should be used if you want to create a fully-decentralized solution where the users manage their own private key. `epk-signature` provider is adapted when you want to manage the private key for the users and have good flexibility to do it, it's never a good idea to let users handling plain private keys. + +### Advanced Logic + +Simplicity is one of the most important characteristics we want to achieve in the Protocol. This is why the actions available in Request Logic are the minimal set of actions needed for any kind of request for payment. In the same way, the basic request state is universally common to any request, every request has a payee (a recipient), a currency (what requested), an expected amount (how much requested) and a basic state (accepted, canceled). In order to enable more advanced features for the users, we conceived Advanced Logic. + +Advanced Logic is a package that allows the user to define extensions that can be added to the request. An extension is an isolated context inside the request that contains his own actions and his own state. For example, the extension `content-data` allows the user to add any metadata to a request (e.g. the additional data needed for an invoice). The Advanced Logic layer is also where the payment networks allowing payment detection are implemented. + +Similar to Request Logic, a specific extension can define different actions related to it. There is the Create action of the extension and eventually different update actions. The extension is initialized at the same time as the request and any action of the Request Logic can add extension data. There is a specific action, `AddExtensionData`, in Request Logic, only intended to add extension data to the request with no other side-effect. + +![](/img/RequestProtocol/2-AdvancedRequestPresentation.jpg) +*Example of a request with extension data: the payee creates a request with content data and declarative payment information, the payer accepts the request and declares a sent payment in the same time, finally, the payee declares the received payment* + +The specification for each extension can be found at this link: [https://github.com/RequestNetwork/requestNetwork/tree/master/packages/advanced-logic/specs](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/advanced-logic/specs) diff --git a/packages/docs/docs/guides/7-protocol/2-transaction.md b/packages/docs/docs/guides/7-protocol/2-transaction.md new file mode 100644 index 0000000000..6e172d12bc --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/2-transaction.md @@ -0,0 +1,32 @@ +--- +title: Transaction +keywords: [Request protocol, Transaction, Encryption] +description: Learn how to integrate Request network and its features. + +--- + +This layer converts actions into transactions to be sent to Data-Access. It also handles the encryption. + +[https://github.com/RequestNetwork/requestNetwork/tree/master/packages/transaction-manager](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/transaction-manager) + +### Encryption + +The transaction layer can encrypt transactions for privacy purposes. + +Having privacy can be important for the payee and the payer. In certain cases, there could be other parties who would need to read the request. For this need, we implemented a solution where an indefinite number of parties can be added to be able to read the request. They are the stakeholders of the request. + +To implement privacy where an indefinite chosen set of stakeholders can read the request we adopted a system composed of two types of key: + +- A unique channel key that is shared to all the stakeholders +- A set of private keys where each is privately held by the stakeholder + +The channel key uses Advanced Encryption Standard (AES), this is a technology for symmetric encryption, this means the key to encrypt and decrypt data is the same. + +The private keys use Elliptic Curve Integrated Encryption Scheme (ECIES), this is a technology for asymmetric encryption. + +When the transaction is received, it will be encrypted only once with the channel key. Every transaction of the same request is encrypted with the same channel key. The set of encrypted transactions forms the channel (hence the name channel key). We made this choice because every request can have a different set of stakeholders (even if the payee and the payer are the same) therefore we want every request to be encrypted with a different key. + +The channel key allows encrypted data to be stored only once. For every stakeholder to be able to read the request, the channel key is encrypted with each stakeholder's public key. These encrypted channel keys are publicly available inside the transaction data. + +![](/img/RequestProtocol/2-Encryption.jpg) +*The different steps to encrypt the transaction* diff --git a/packages/docs/docs/guides/7-protocol/3-data-access.md b/packages/docs/docs/guides/7-protocol/3-data-access.md new file mode 100644 index 0000000000..299fe139c1 --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/3-data-access.md @@ -0,0 +1,30 @@ +--- +title: Data-access +keywords: [Request protocol, Data-access] +description: Learn how to integrate Request network and its features. + +--- + +Data-Access is the layer that organizes the data in the right format before having them being stored in the storage layer. This layer is similar as the persistence layer in the classical layered architecture pattern. + +[https://github.com/RequestNetwork/requestNetwork/tree/master/packages/data-access](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/data-access) + +### Blocks + +Heavy communication with the Storage layer can be costly. For example, for a solution using Ethereum, every Ethereum transactions cost some gas. + +Data-Access layer will gather transactions and batch them into blocks. This solution allows for less communication with the Storage layer. In this case, it will allow consuming less gas for Ethereum transactions. + +### Local cache for accessing transaction + +Data-Access is also responsible for other side tasks: + +- Indexing transactions to allow retrieval +- Accessing transactions through a local cache +- Synchronizing with the storage + +The storage phase is only complete when indexing has completed. Because this indexing is an Ethereum transaction, you cannot know in advance how long it will take. + +It is the reason that, when a block is created or read from the storage, the transactions inside it will be indexed and kept in a local cache. When a user wants to get information about a request, Data-Access will directly fetch them from this local cache. + +Data-Access stays synchronized with the storage layer. For example, it pulls for new blocks, added by other users, in the storage every 10 seconds. diff --git a/packages/docs/docs/guides/7-protocol/4-storage.md b/packages/docs/docs/guides/7-protocol/4-storage.md new file mode 100644 index 0000000000..71caf3809b --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/4-storage.md @@ -0,0 +1,42 @@ +--- +title: Storage +keywords: [Request protocol, Storage, IPFS, Ethereum] +description: Learn how to integrate Request network and its features. + +--- + +Storage defines where the data are stored. How to store these data and how to retrieve them. + +The currently used package, named `ethereum-storage`, uses IPFS to immutably store the data and uses the Ethereum network to persist the IPFS hash of the data and make them permanently available to everyone. + +[https://github.com/RequestNetwork/requestNetwork/tree/master/packages/ethereum-storage](https://github.com/RequestNetwork/requestNetwork/tree/master/packages/ethereum-storage) + +The storage of data implementation is: + +- Open: Anyone should be able to access the data (though it can be encrypted) +- Decentralized: The database is trustless, we don’t have to refer to a third party to trust data +- Resilient: The database should be always available, nobody should be able to shutdown it alone + +### IPFS + +The interplanetary file system (IPFS) is a decentralized network to store and share files: [https://ipfs.io](https://ipfs.io/) + +One of the advantages of IPFS as a storage solution is that it is content addressable. When a file is deleted, if someone reuploads the file, anybody will be able to access it with the same path. For a specific block of data, we will get a specific hash, the hash is persisted on Ethereum to ensure requests immutability. + +### Ethereum + +We use Ethereum to store IPFS hashes. The hashes are stored as event logs of a specific smart contract to stay at a minimal cost. + +The Ethereum smart contracts are also used to enforce the fee cost of storing a block to Request. The user will store the size of the file being stored in addition to the hash. A fee, related to this hash, will be paid in Ether when storing the hash. + +For our solution, we use additional smart contracts for fee verification. Using external smart contracts allows us to implement different fee rules in the future. More information can be found in the ethereum-storage repository. + +The RequestHashStorage smart contract address can be found on [GitHub](https://github.com/RequestNetwork/requestNetwork/blob/1f24dd17353497cc6ee21abf2d7bfda9e63d2ba5/packages/smart-contracts/artifacts/RequestHashStorage/artifacts.json) +```json + "mainnet": { + "address": "0x24a66afda3666fb0202f439708ece45c8121a9bb" +}, +"rinkeby": { + "address": "0x309a3a9898f9cafc26499243a980992156671e5e" +} +``` diff --git a/packages/docs/docs/guides/7-protocol/5-flows.md b/packages/docs/docs/guides/7-protocol/5-flows.md new file mode 100644 index 0000000000..658e834b62 --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/5-flows.md @@ -0,0 +1,75 @@ +--- +title: Data flow +keywords: [Request protocol, IPFS, Ethereum] +description: Learn how to integrate Request network and its features. + +--- + +This page presents the flow of data that occurs when some actions are performed in the protocol. + +## Creating and updating requests + +The next schemas show the data flow that happens when a user performs an `accept` action on a request. + +### Request Logic + +![](/img/RequestProtocol/3-RequestLogicFlow.jpg) +*Request Logic flow* + +![](/img/RequestProtocol/3-AdvancedLogicFlow.jpg) +*Request Logic flow with extension data* + +### Transaction + +![](/img/RequestProtocol/3-TransactionFlow.jpg) +*Transaction flow without encryption* + +![](/img/RequestProtocol/3-TransactionFlowEncrypted.jpg) +*Transaction flow with encryption with 2 stakeholders* + +### Data-access + +![](/img/RequestProtocol/3-DataAccessFlow.jpg) +*Data-access flow. In this example several transactions are batched into the block, this feature is not yet implemented* + +### Storage + +![](/img/RequestProtocol/3-StorageFlow.jpg) +*A new block is added into the storage* + +## Reading requests + +The next schemas show the data flow when the user wants to read the content of a request. + +In this case the user call this function of Request Logic: `getRequestFromId(0xaaa)` that reads the request with the request id: 0xaaa + +### Storage + +There is a permanent data flow between Data Access and Storage layers. + +For performance purposes, Data Access will periodically synchronize with the current state of Storage. When a new, not synchronized block is detected, the block content will be dispatched into the Data Access cache. + +![](/img/RequestProtocol/4-DataAccessAndStorageFlow.jpg) +*Flow for Data Access synchronization* + +### Data-access + +![](/img/RequestProtocol/4-DataAccessFlow.jpg) +*Flow from Data-Access. When a user wants to read a request, Data-Access will read its cache without any communication with the storage layer* + +### Transaction + +![](/img/RequestProtocol/4-TransactionFlow.jpg) +*Flow from Transaction layer. If the request is encrypted, the transactions are decrypted in this layer* + +### Request Logic + +![](/img/RequestProtocol/4-RequestLogicFlow.jpg) +*Request Logic flow. Request Logic will compute the state of the request based on the list of actions. In this case, the increaseExpectedAmount action has been signed by the payer* + +Some actions from the Transaction layer can be invalid, this is the role of Request Logic to filter them in order to give the consistent state of the request to the user. + +For example, only the payer of the request can increase the expected amount of it. If the action `increaseExpectedAmount` is signed by the payee therefore the action is ignored. + +![](/img/RequestProtocol/4-RequestLogicFlowInvalid.jpg) +*In this example the increaseExpectedAmount is signed by the payee, it is therefore invalid. The expectedAmount of the request keeps its initial value: 5* diff --git a/packages/docs/docs/guides/7-protocol/6-request-ipfs-network.md b/packages/docs/docs/guides/7-protocol/6-request-ipfs-network.md new file mode 100644 index 0000000000..3b1c5aebaa --- /dev/null +++ b/packages/docs/docs/guides/7-protocol/6-request-ipfs-network.md @@ -0,0 +1,83 @@ +--- +title: Request IPFS network +sidebar_label: Request IPFS network +description: Learn how to integrate Request network and its features. +--- + +## Why Request uses IPFS? + +Request uses IPFS to store transaction data in a decentralized way. Transactions are actions done on a request, for example: create, accept, reject... + +When files are stored in IPFS, they are kept locally on the IPFS node, and are accessible by any node connected to the network. To access this file IPFS [creates a unique hash](https://medium.com/textileio/whats-really-happening-when-you-add-a-file-to-ipfs-ae3b8b5e4b0f) that identifies the file. We store this hash on the Request smart contract to have a trustless list of transactions. + +## Why Request uses a dedicated IPFS network? + +The main IPFS network has tens of thousands of nodes and a huge amount of files. All of the Request transactions are a tiny fraction of the IPFS network. To find a transaction file, the Request Node IPFS has to [traverse many nodes](https://medium.com/textileio/how-the-ipfs-dht-works-47af8bfd3c6a) on the network. Content retrieval is currently quite slow on IPFS. + +By creating a dedicated IPFS network for Request, our network is isolated from the rest of the IPFS network. This means that all the Request IPFS nodes will only communicate with other Request Nodes. By keeping the network small, we can make sure most nodes are connected between themselves and asking for files can be done directly to a node instead of through a traversal. + +This is a big advantage for us because our nodes end goal is different from that of a normal IPFS node: all the IPFS nodes used by a Request Node should have all the files on the network. This makes the DHT pointless, and the most important factor in discovery time becomes how many nodes every node is connected to. + +These are the main reason why we created the Request IPFS network. + +## The Request IPFS Network + +IPFS has a feature called **private network**. It allows IPFS nodes that share a private key to communicate only among themselves and keep their files private from the open IPFS network. We use this feature to create an IPFS network that is separate from the open one, but we keep this key public. This way, we have a public network that is separate from the open network. + +We also changed some default IPFS configurations on our network, to improve performance and responsiveness. The main change we did is disabling the DHT, so instead of traversing the network to find a file, the nodes will only ask to their neighbor nodes for those files. Since on our network every node is supposed to have every transaction file, those responses tend to be a lot faster \(on most of our test cases the response time went from seconds to a few hundred milliseconds\). + +## Run our IPFS node Docker image + +We distribute a Docker image of our configured IPFS node. +To run it, you can use the command: + +```bash +docker run -p 5001:5001 -p 4001:4001 requestnetwork/request-ipfs +``` + +### Configure your IPFS node to use the Request IPFS network + +There are two easy ways to connect an IPFS node to the Request Network: + +- Use the [requestnetwork/request-ipfs](https://hub.docker.com/r/requestnetwork/request-ipfs) docker image to run your IPFS. It comes pre-configured and you just need to run it. +- [Use the `init-ipfs`](https://github.com/RequestNetwork/requestNetwork-private/blob/development/packages/ethereum-storage/scripts/init-ipfs.js) script available from the [Request Node package](https://github.com/RequestNetwork/requestNetwork-private/tree/development/packages/request-node) or the [Ethereum Storage package](https://github.com/RequestNetwork/requestNetwork-private/tree/development/packages/ethereum-storage). + +```bash +yarn init-ipfs +``` + +### Setting up your IFPS by hand + +If you want to set up your IPFS node yourself, here is the information you would need: + +#### The swarm key + +you should put this file at your IPFS path \(usually in \$IPFS_PATH\) + +**swarm.key** file content: + +```text +/key/swarm/psk/1.0.0/ +/base16/ +5f3af0599d991e5eb4c37da2472aa299759ee3350ba26c125d0c7579dd04dd52 +``` + +#### The configurations + +```bash +# Initialize IPFS +ipfs init + +# Setup the Request IPFS Network bootstraps +ipfs bootstrap rm --all +ipfs bootstrap add /dns4/ipfs-bootstrap.request.network/tcp/4001/ipfs/QmaSrBXFBaupfeGMTuigswtKtsthbVaSonurjTV967Fdxx +ipfs bootstrap add /dns4/ipfs-bootstrap-2.request.network/tcp/4001/ipfs/QmYdcSoVNU1axgSnkRAyHtwsKiSvFHXeVvRonGCAV9LVEj +ipfs bootstrap add /dns4/ipfs-2.request.network/tcp/4001/ipfs/QmPBPgTDVjveRu6KjGVMYixkCSgGtVyV8aUe6wGQeLZFVd +ipfs bootstrap add /dns4/ipfs-survival.request.network/tcp/4001/ipfs/Qmb6a5DH45k8JwLdLVZUhRhv1rnANpsbXjtsH41esGhNCh + +# Disable the DHT +ipfs config Routing.Type none + +# Environment variable to forbid IPFS to connect to the open IPFS network +export LIBP2P_FORCE_PNET=1 +``` diff --git a/packages/docs/docs/guides/8-glossary.md b/packages/docs/docs/guides/8-glossary.md new file mode 100644 index 0000000000..3e0363cc31 --- /dev/null +++ b/packages/docs/docs/guides/8-glossary.md @@ -0,0 +1,110 @@ +--- +title: Glossary +description: Learn how to integrate Request network and its features. +--- + +# Ecosystem + +## Request Portal API + +Request Portal is an API built on top of the Request Protocol that allows third party software to easily integrate Request. It also provides extra features, not available through direct usage of the Request Protocol, like private key management, OAuth authentication, and better performance. + +## Request Client + +The Request Client is a Javascript library made to interact directly with the Request Protocol. The Request Client connects to a Request Node. + +## Request Node + +Request Nodes are HTTP servers exposing an API used to allow Request Client to communicate with the Request Protocol. These servers abstract the complexity of IPFS and Ethereum used by the Request Protocol. + +## Request Protocol + +The Request Protocol is the underlying protocol that powers Request. It defines how requests are stored on a distributed ledger and how to interpret actions performed on them. + +# Request Protocol + +## Action + +An action is signed data added by a request's stakeholder into the Request Protocol that creates or updates the state of a request. A request can be represented by a list of actions. For example, the creation of a request is an action. + +## Balance + +When using a payment network, the balance is the current amount paid for a request. The balance is determined by the payment detection method of the payment network used. + +A request with no payment network provided doesn't have a balance. + +## Confirmed/Pending action + +Request relies on other blockchain technologies to ensure data immutability. Most blockchain doesn't offer transaction instant finality. This means that when performing an action on the request, this action can't directly be confirmed as effective. + +As long as the action hasn't been persisted and is not confirmed, the action is marked as "pending". The "pending" state is useful to have a fast response and good user experience. Until the request is Confirmed, it should not be relied upon. + +## Decryption provider + +A decryption provider is an abstraction of the mechanism that handles the decryption of a request. Depending on use cases, it allows you to give your user full control, or handle some parts for them. + +It is not used for clear requests. + +## Extension + +An extension is a set of actions that extends the feature of a request. A request without extension is a basic request for payment with a payee, a currency and a requested amount. The extension allows for more advanced features. + +## Identity + +The identity is what defines a stakeholder of a request that allows signing or encrypting the request actions. The identity is the public data that identifies the stakeholder. + +## Payment Detection + +A payment detection is a method defined by the payment network to determine the current balance of a request. + +## Payment Network + +A payment network is a predefined set of rules to agree on the balance of a request. The payment network is defined during the creation of the request. + +A payment network is generally related to one currency but it's not always the case (the Declarative payment network is currency agnostic) + +## Request Data + +The request data is the current state of a request, the data of the request after having applied all the confirmed actions on it. + +## Request Id + +The request Id is the number that uniquely identifies a request. This number is computed from the hash of the request creation action. + +## Signature Provider + +A signature provider is an abstraction of identity management and action signatures. Depending on use cases, it allows you to give your user full control, or handle some parts for them. + +## Stakeholder + +A request stakeholder is a party involved with the request. Stakeholders are generally the payer and the payee of the request, or any other third-party allowed to perform actions on it. For encrypted requests, stakeholders are any party that has an interest in reading the request content. + +## Topic + +A topic is a string that is used to index a request. This topic is used for request retrieval. Several requests can share the same topic. + +Every request has its request id and its payee identity as topics (and the payer identity if it is defined). Any custom topic can be appended to a request. + +# Blockchain, Cryptography + +## Confirmation + +Confirmation means that the blockchain transaction has been verified by the network. This happens through a process known as mining, in a proof-of-work system (e.g. Bitcoin). Once a transaction is confirmed, it cannot be reversed. + +## Ether + +Ether is the native token of the Ethereum blockchain which is used to pay for transaction fees, miner rewards, and other services on the network. + +## IPFS + +The Inter-Planetary File System (IPFS) is a protocol and a peer-to-peer network for storing and sharing data in a distributed file system. IPFS uses content-addressing to uniquely identify each file in a global namespace connecting all computing devices. + +IPFS is used by the Request Protocol to ensure data accessibility. + +## Multi-signature + +Multi-signature (multisig) wallets allow multiple parties to require more than one key to authorize a transaction. The needed number of signatures is agreed upon at the creation of the wallet. Multi-signature addresses have a much greater resistance to theft. + +## Private Key + +A private key is a large number that allows you to sign or decrypt messages. Private keys can be thought of as a password; private keys must never be revealed to anyone but you, as they allow you to spend the funds from your wallet through a cryptographic signature. diff --git a/packages/docs/docusaurus.config.js b/packages/docs/docusaurus.config.js new file mode 100755 index 0000000000..365b48678b --- /dev/null +++ b/packages/docs/docusaurus.config.js @@ -0,0 +1,111 @@ +module.exports = { + title: 'Request Docs', + tagline: 'Technical documentation', + url: 'https://docs.request.network', + baseUrl: '/', + favicon: 'img/cropped-favicon-32x32.png', + organizationName: 'requestNetwork', + projectName: 'requestNetwork/packages/docs', + plugins: ['axios'], + themeConfig: { + disableDarkMode: true, + image: 'img/request_docs_thumbnail.png', + navbar: { + title: 'Request Docs', + logo: { + alt: 'Request Network', + src: 'img/logo.svg', + }, + links: [ + { to: 'docs/guides/0-getting-started', label: 'Get started', position: 'left' }, + { to: 'integration-options', label: 'Integration', position: 'left' }, + { to: 'docs/client/index', label: 'Request-client.js', position: 'left' }, + { to: 'portal', label: 'Portal REST API', position: 'left' }, + { + href: + 'https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-logic/specs', + label: 'Protocol Specs', + position: 'left', + }, + { + href: 'https://github.com/RequestNetwork', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Docs', + items: [ + { + label: 'Portal API', + to: 'portal', + }, + { + label: 'Request Client library', + to: 'docs/client/index', + }, + { + label: 'Request Protocol', + to: + 'https://github.com/RequestNetwork/requestNetwork/tree/master/packages/request-logic/specs', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'RequestHub on Slack', + href: + 'https://join.slack.com/t/requesthub/shared_invite/enQtMjkwNDQwMzUwMjI3LTc5NDRmN2YyMTVhZTBjNDE2MWU2YTBlYWIzYmJlYzNkMWQ5MzVmYzEzNGVmYjliNDQ4MjkyNTBiYjk4MDk3ZGE', + }, + { + label: 'GitHub', + href: 'https://github.com/RequestNetwork/', + }, + { + label: 'Discord', + href: 'https://discordapp.com/invite/6aGhs6v', + }, + ], + }, + { + title: 'Social', + items: [ + { + label: 'Blog', + href: 'https://request.network/en/blog/', + }, + { + label: 'Twitter', + href: 'https://twitter.com/RequestNetwork', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} Request Network Stiftung. Built with Docusaurus.`, + }, + }, + themes: ['@docusaurus/theme-live-codeblock'], + presets: [ + [ + '@docusaurus/preset-classic', + { + docs: { + sidebarPath: require.resolve('./sidebars.js'), + editUrl: 'https://github.com/RequestNetwork/requestNetwork/tree/master/packages/docs', + }, + introSideBar: { + sidebarPath: require.resolve('./sidebars.js'), + }, + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + }, + ], + ], +}; diff --git a/packages/docs/package.json b/packages/docs/package.json new file mode 100755 index 0000000000..3f2b03b2d5 --- /dev/null +++ b/packages/docs/package.json @@ -0,0 +1,73 @@ +{ + "name": "@requestnetwork/docs", + "version": "0.1.4", + "private": true, + "description": "Request products technical documentation.", + "keywords": [ + "requestnetwork", + "docs", + "documentation" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RequestNetwork/requestNetwork.git" + }, + "homepage": "https://github.com/RequestNetwork/requestNetwork/tree/master/packages/docs#readme", + "bugs": { + "url": "https://github.com/RequestNetwork/requestNetwork/issues" + }, + "engines": { + "node": ">=8.0.0" + }, + "license": "MIT", + "scripts": { + "start": "docusaurus start", + "prestart": "yarn gen:code2md; yarn gen:client", + "build": "docusaurus build", + "prebuild": "yarn gen:code2md; yarn gen:client", + "swizzle": "docusaurus swizzle", + "preswizzle": "yarn gen:code2md; yarn gen:client", + "deploy": "docusaurus deploy", + "predeploy": "yarn gen:code2md; yarn gen:client", + "gen:code2md": "aurelius 'docs/**/*'", + "gen:client": "typedoc --plugin typedoc-plugin-markdown --theme docusaurus2 --out docs/client/ --exclude '**/*test*' --resolveJsonModule --ignoreCompilerErrors --mode modules --skipSidebar --readme ../request-client.js/README.md ../request-client.js/src; shx rm -rf website", + "clean": "shx rm -rf docs/client" + }, + "dependencies": { + "@docusaurus/core": "2.0.0-alpha.49", + "@docusaurus/preset-classic": "2.0.0-alpha.49", + "@docusaurus/theme-live-codeblock": "2.0.0-alpha.49", + "@docusaurus/utils": "2.0.0-alpha.49", + "@requestnetwork/payment-processor": "0.18.0", + "@requestnetwork/request-client.js": "0.18.0", + "@requestnetwork/smart-contracts": "0.9.0", + "@requestnetwork/types": "0.17.0", + "bn.js": "5.1.1", + "classnames": "2.2.6", + "core-js": "3.6.4", + "eth-contract-metadata": "1.13.0", + "ethers": "4.0.45", + "mobx": "5.15.4", + "react": "16.8.4", + "react-dom": "16.8.4", + "redoc": "2.0.0-rc.23", + "styled-components": "5.0.1" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "aurelius": "0.1.3", + "typedoc": "0.16.10", + "typedoc-plugin-markdown": "2.2.17" + } +} diff --git a/packages/docs/sidebars.js b/packages/docs/sidebars.js new file mode 100755 index 0000000000..a1bf009d06 --- /dev/null +++ b/packages/docs/sidebars.js @@ -0,0 +1,41 @@ +/* eslint-disable spellcheck/spell-checker */ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const fs = require('fs'); +const path = require('path'); + +const docsPath = 'docs'; + +module.exports = { + introSideBar: makeSidebar('guides'), + clientAPI: makeSidebar('client'), +}; + +function makeSidebar(dir) { + const files = fs.readdirSync(path.join(docsPath, dir), { withFileTypes: true }); + + return files.reduce((sidebar, file) => { + if (file.isDirectory()) { + sidebar.push({ + type: 'category', + label: toTitleCase(file.name), + items: makeSidebar(path.join(dir, file.name)), + }); + } else if (file.name.endsWith('.js') || file.name.endsWith('.md')) { + sidebar.push(path.join(dir, file.name.slice(0, -3))); + } + return sidebar; + }, []); +} + +function toTitleCase(string) { + if (string.match(/^[0-9]+-/)) { + string = string.slice(string.match(/^[0-9]+-/)[0].length); + } + return string.charAt(0).toUpperCase() + string.slice(1).replace(/-/g, ' '); +} diff --git a/packages/docs/src/components/hint/index.js b/packages/docs/src/components/hint/index.js new file mode 100644 index 0000000000..0aad6b9dbe --- /dev/null +++ b/packages/docs/src/components/hint/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import styles from './styles.module.css'; + +export default ({ style, children }) => { + let className = styles.hint; + if (style) { + className += ' ' + styles[`hint-${style}`]; + } + + return
{children}
; +}; diff --git a/packages/docs/src/components/hint/styles.module.css b/packages/docs/src/components/hint/styles.module.css new file mode 100644 index 0000000000..9bb77001a6 --- /dev/null +++ b/packages/docs/src/components/hint/styles.module.css @@ -0,0 +1,25 @@ +.hint { + width: 100%; + margin: 32px 0px; + display: block; + padding: 24px 24px calc(0.1px) 50px; + position: relative; + border-left: 4px solid; + border-radius: 3px; + color: #000000; + background-color: rgb(245, 247, 249); +} + +.hint.hint-info { + border-color: rgb(56, 132, 255); +} + +.hint.hint-warning { + border-color: rgb(247, 125, 5); +} + +.hint .content { + color: inherit; + margin: 0px 0px 24px; + position: relative; +} diff --git a/packages/docs/src/components/integration-options/index.js b/packages/docs/src/components/integration-options/index.js new file mode 100644 index 0000000000..45e151f673 --- /dev/null +++ b/packages/docs/src/components/integration-options/index.js @@ -0,0 +1,124 @@ +import React from 'react'; +import classnames from 'classnames'; +import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import styles from './styles.module.css'; + +const options = [ + { + title: "Option A: Portal API", + imageUrl: 'img/ReQ-01.png', + description: ( + <> + Fully managed solution, over REST API + + ), + details: ( + <> +

+ Easiest way to integrate Request without having to manage cryptographic keys or infrastructure, and with a fast data access. +

+

+ Go to the Request Portal in order to get your API keys, and your are good to go. This is the fastest option to integrate, and also the most performant to fetch requests because the Portal caches them for you. More details in the Portal API Documentation or follow the guide. +

+ +

+ Keep in mind that when using the Portal API, Request handles your identity's private key. Request empowers all economical actors to control their finance, and fully decentralized organizations may look for a more distributed option. +

+

+ The Portal is safe to be used in small and medium sized production environments. Request Portal does not move any fund, but it's better to understand the risks +

+ + ), + }, + { + title: "Option B: Network Client", + imageUrl: 'img/REQ-07-hands-02.png', + description: <>Decentralized network usage, with managed hosting, + details: ( + <> +

+ Manage identities, encryption and network interactions yourself but let us host the Request node. +

+

+ You can see the documentation on the Request JavaScript Client documentation or follow the guide. +

+ +

+ The Request Client comes as a library installed with npm. It comes with all the features needed to create, fetch and updates payment requests, including encryption capabilities. This package also comes with a development mode relying on local storage. You manage identities and private keys, which means that no other party can sign Request transactions for you or your users. +

+ + ), + }, + { + title: "Option C: Network Node", + imageUrl: 'img/REQ-05-patterns-02.png', + description: ( + <> + Fully decentralized with self-hosting + + ), + details: ( + <> +

+ Hosting your own node gives you full power over the Request network connections and storage options. + You decide of how requests hashes are pushed to Ethereum and how to store and access details (encrypted or not). +

+

+ Follow the guide to setup your node. +

+

+ You query the node with the same Network Client. For your test environment, you can setup a Rinkeby node or a connection with our hosted Rinkeby node (cf. Option B). +

+ + ), + }, +]; + +function IntegrationOption({ showDetails, imageUrl, title, description, details }) { + const imgUrl = useBaseUrl(imageUrl); + return ( +
+ {imgUrl && ( +
+ {title} +
+ )} +
+ {showDetails && ( + <> + + +

{title}

+ + )} + {!showDetails && ( + +

{title}

+ + )} +
{description}
+ {showDetails && ( +

{details}

+ )} +
+
+ ); +} + +function IntegrationOptions({showDetails = false}) { + + return ( + <> + {options && options.length && ( +
+ {options.map((props, idx) => ( + + ))} +
+ )} + + ); +} + +export default IntegrationOptions; diff --git a/packages/docs/src/components/integration-options/styles.module.css b/packages/docs/src/components/integration-options/styles.module.css new file mode 100644 index 0000000000..36c70cc9fb --- /dev/null +++ b/packages/docs/src/components/integration-options/styles.module.css @@ -0,0 +1,51 @@ +.integrationOptions { + display: flex; + flex-direction: column; +} + +.homeIntegrationOptions { + display: flex; + flex-direction: row; + align-items: baseline; +} + +@media (max-width: 585px) { + .homeIntegrationOptions { + flex-direction: column; + } +} + +.integrationOption { + display: flex; + flex-wrap: nowrap; + align-items: stretch; + padding: 2rem 1rem; + width: 100%; +} + +.colOption { + flex-direction: column; +} + +.rowOption:nth-child(odd) { + flex-direction: row; +} +.rowOption:nth-child(even) { + flex-direction: row-reverse; +} + +.integrationOptionImage { + margin-bottom: 12px; + flex: 2; +} + +.integrationOptionDescription { + padding-right: 24px; + margin-bottom: 24px; + color: #8c8c8c; +} + +.integrationOptionText { + flex: 3; + padding-left: 15px; +} diff --git a/packages/docs/src/components/page-ref/index.js b/packages/docs/src/components/page-ref/index.js new file mode 100644 index 0000000000..387089f3d0 --- /dev/null +++ b/packages/docs/src/components/page-ref/index.js @@ -0,0 +1,29 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; + +export default ({ title, path }) => ( + +
+ + + + + + +
{title}
+
{path}
+
+ +); diff --git a/packages/docs/src/components/page-ref/styles.module.css b/packages/docs/src/components/page-ref/styles.module.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/docs/src/components/redoc.js b/packages/docs/src/components/redoc.js new file mode 100644 index 0000000000..c2f54f952a --- /dev/null +++ b/packages/docs/src/components/redoc.js @@ -0,0 +1,8 @@ +// This component is a hack for Redoc to work with Gatsby +// We import the missing dependencies for redoc directly on this file +import { RedocStandalone } from 'redoc'; +import mobx from 'mobx'; +import styled from 'styled-components'; +import * as promise from 'core-js/es/promise'; + +export default RedocStandalone; diff --git a/packages/docs/src/components/supported-erc20/index.js b/packages/docs/src/components/supported-erc20/index.js new file mode 100644 index 0000000000..cb47f64929 --- /dev/null +++ b/packages/docs/src/components/supported-erc20/index.js @@ -0,0 +1,84 @@ +import React from 'react'; +import contractMap from 'eth-contract-metadata'; +import styles from './styles.module.css'; + +const tokens = Object.entries(contractMap) + .filter(([, { erc20 }]) => !!erc20) + .map(([address, token]) => ({ ...token, address })) + .sort((a, b) => a.symbol > b.symbol); + +const CurrencyList = ({ currencies, selected, onClick }) => { + const currList = currencies.map(token => ( +
  • onClick(token)} + className={selected && token.symbol === selected.symbol ? styles.selected : ''} + > + {token.symbol} {token.name} +
  • + )); + + return
      {currList}
    ; +}; + +const CurrencyDetails = ({ currency }) => { + if (!currency) { + return <>; + } + + return ( +
    +

    + {currency.symbol} + {currency.name} +

    +
      +
    • + Symbol: {currency.symbol} +
    • +
    • + Decimals: {currency.decimals} +
    • +
    • + Smart contract: {currency.address} +
    • +
    +
    + ); +}; + +export default () => { + const [currencies, setCurrencies] = React.useState(tokens); + const [search, setSearch] = React.useState(''); + const [selectedCurrency, setSelectedCurrency] = React.useState( + tokens.find(r => r.symbol === 'REQ'), + ); + + React.useEffect(() => { + if (!search) { + setCurrencies(tokens); + } else { + setCurrencies( + tokens.filter(token => token.symbol.toLowerCase().includes(search.toLowerCase())), + ); + } + }, [search]); + + return ( +
    + setSearch(e.target.value)} + /> + setSelectedCurrency(token)} + selected={selectedCurrency} + /> + +
    + ); +}; diff --git a/packages/docs/src/components/supported-erc20/styles.module.css b/packages/docs/src/components/supported-erc20/styles.module.css new file mode 100644 index 0000000000..99c5b7dad7 --- /dev/null +++ b/packages/docs/src/components/supported-erc20/styles.module.css @@ -0,0 +1,57 @@ +.main { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + justify-items: stretch; +} + +.list { + height: 226px; + overflow: hidden; + overflow-y: scroll; + list-style: none; + padding: 0; + width: auto; + resize: vertical; + grid-row: 2; +} + +.list li { + margin: 0; + padding: 8px 15px; + background-color: var(--ifm-color-primary-lightest); + cursor: pointer; +} + +.list .selected { + background-color: var(--ifm-color-primary-lighter); +} + +.searchBar { + padding: 4px; + font-family: 'Roboto', sans-serif; + font-weight: 400; + font-size: 16px; + line-height: 26px; + border: 1px solid rgb(59, 69, 78, 0.2); + border-radius: 3px; + margin: 5px 0; +} + +.details { + grid-column: 2; + grid-row: 1 / 3; + padding: 20px; +} + +.details ul { + list-style: none; + padding: 0; +} + +.symbol { + background-color: var(--ifm-color-primary-lighter); + padding: 3px 8px; + border-radius: 4px; + margin-right: 7px; +} diff --git a/packages/docs/src/css/custom.css b/packages/docs/src/css/custom.css new file mode 100755 index 0000000000..b9063517a3 --- /dev/null +++ b/packages/docs/src/css/custom.css @@ -0,0 +1,106 @@ +/* * + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: rgb(33, 175, 144); + --ifm-color-primary-darker: rgb(31, 165, 136); + --ifm-color-primary-darkest: rgb(26, 136, 112); + --ifm-color-primary-light: rgb(70, 203, 174); + --ifm-color-primary-lighter: rgb(102, 212, 189); + --ifm-color-primary-lightest: rgb(146, 224, 208); + --ifm-code-font-size: 95%; + + --ifm-navbar-background-color: #00261b; + --ifm-navbar-link-color: #ffffff; + + --ifm-container-width: 1280px; +} + +.admonition a { + color: white; + text-decoration: underline; +} + +@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap'); + +body { + font-family: 'Roboto', sans-serif; + font-weight: 400; + font-size: 16px; + line-height: 26px; + text-rendering: optimizelegibility; + -moz-osx-font-smoothing: grayscale; + outline-color: rgb(59, 69, 78); + outline-style: none; + outline-width: 0px; + overflow-wrap: break-word; +} + +h2 { + position: relative; + margin: 73px 0px 24px; +} + +h2::before { + top: -32px; + left: 0px; + width: 100%; + height: 1px; + content: ' '; + position: absolute; + background-color: rgb(230, 236, 241); +} + +h1 .hash-link { + margin-left: -2rem; +} + +.navbar-sidebar__items .menu__link { + color: var(--ifm-navbar-link-color); +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} + +.docusaurus-highlight-code-line { + background-color: rgb(72, 77, 91); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); +} + +.navbar__toggle { + color: var(--ifm-navbar-link-color); +} + +/* The following lines are a hack to fix long lines of highlighted code breaking the page layout width */ +.prism-code { + background-color: rgb(41, 45, 62); +} +.prism-code code { + width: 0; +} + +.menu__link--sublist { + font-weight: 700; +} + +.navbar__brand { + min-width: 150px; +} + +.navbar .navbar__items { + flex: 4 0 0; +} + +.navbar__items.navbar__items--right { + flex: 1 0 0; +} diff --git a/packages/docs/src/pages/index.js b/packages/docs/src/pages/index.js new file mode 100755 index 0000000000..a08ad54fe7 --- /dev/null +++ b/packages/docs/src/pages/index.js @@ -0,0 +1,9 @@ +import React from 'react'; +import {Redirect} from '@docusaurus/router'; + + + +function Home() { + return ; +} +export default Home; diff --git a/packages/docs/src/pages/integration-options/index.js b/packages/docs/src/pages/integration-options/index.js new file mode 100644 index 0000000000..9d413d7e08 --- /dev/null +++ b/packages/docs/src/pages/integration-options/index.js @@ -0,0 +1,25 @@ +import React from 'react'; +import Layout from '@theme/Layout'; +import IntegrationOptions from '../../components/integration-options'; +import styles from './styles.module.css'; + +function Home() { + return ( + +
    +

    What is the best way to integrate with Request?

    +

    How to integrate Request?

    + +

    + The way you decide to interact with the network will determine the responsibility you have + over security aspects and the settings you can adjust. Depending on your need for + decentralization, you have three options: +

    + + +
    +
    + ); +} + +export default Home; diff --git a/packages/docs/src/pages/integration-options/styles.module.css b/packages/docs/src/pages/integration-options/styles.module.css new file mode 100644 index 0000000000..d40bda62b8 --- /dev/null +++ b/packages/docs/src/pages/integration-options/styles.module.css @@ -0,0 +1,9 @@ +h1 { + font-size: 3rem; + margin-bottom: 2rem; +} + +.container { + max-width: 960px; + margin: 3rem auto 4rem; +} diff --git a/packages/docs/src/pages/portal/index.js b/packages/docs/src/pages/portal/index.js new file mode 100644 index 0000000000..ad42c50036 --- /dev/null +++ b/packages/docs/src/pages/portal/index.js @@ -0,0 +1,33 @@ +import React from 'react'; +import Layout from '@theme/Layout'; +import RedocStandalone from '../../components/redoc'; +import styles from './styles.module.css'; + +class docApi extends React.Component { + render() { + return ( + +
    + +
    +
    + ); + } +} + +export default docApi; diff --git a/packages/docs/src/pages/portal/styles.module.css b/packages/docs/src/pages/portal/styles.module.css new file mode 100644 index 0000000000..f5bbf7e453 --- /dev/null +++ b/packages/docs/src/pages/portal/styles.module.css @@ -0,0 +1,30 @@ +table th, +table td { + border: none; +} + +.redoc h3 { + font-size: 17px; + margin: 17px 0; +} + +.redoc .menu-content { + padding-top: 20px; +} + +.redoc table { + display: table; +} + +.redoc table tr:nth-child(2n) { + background-color: inherit; +} + +.redoc pre { + background-color: inherit; +} + +.redoc code { + background-color: inherit; + color: inherit; +} diff --git a/packages/docs/src/pages/styles.module.css b/packages/docs/src/pages/styles.module.css new file mode 100755 index 0000000000..666feb6a17 --- /dev/null +++ b/packages/docs/src/pages/styles.module.css @@ -0,0 +1,23 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 966px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/packages/docs/src/pages/whitepaper/index.js b/packages/docs/src/pages/whitepaper/index.js new file mode 100644 index 0000000000..e7ceec6b9b --- /dev/null +++ b/packages/docs/src/pages/whitepaper/index.js @@ -0,0 +1,40 @@ +import React from 'react'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; +import styles from './styles.module.css'; + +const whitepapers = { + English: 'https://request.network/assets/pdf/request_whitepaper.pdf', + Bulgarian: 'https://request.network/assets/pdf/bulgarian_whitepaper.pdf', + Chinese: 'https://request.network/assets/pdf/chinese_whitepaper.pdf', + Dutch: 'https://request.network/assets/pdf/dutch_whitepaper.pdf', + French: 'https://request.network/assets/pdf/french_whitepaper.pdf', + German: 'https://request.network/assets/pdf/german_whitepaper.pdf', + Portuguese: 'https://request.network/assets/pdf/portuguese_whitepaper.pdf', + Romanian: 'https://request.network/assets/pdf/romanian_whitepaper.pdf', + Slovenian: 'https://request.network/assets/pdf/slovenian_whitepaper.pdf', + Spanish: 'https://request.network/assets/pdf/spanish_whitepaper.pdf', + Vietnamese: 'https://request.network/assets/pdf/vietnamese_whitepaper.pdf', +}; + +function Home() { + const context = useDocusaurusContext(); + const { siteConfig = {} } = context; + return ( + +
    +

    Whitepaper

    +

    Below you can find links for the whitepaper translated to different languages:

    +
      + {Object.entries(whitepapers).map(([lang, link]) => ( +
    • + {lang} +
    • + ))} +
    +
    +
    + ); +} + +export default Home; diff --git a/packages/docs/src/pages/whitepaper/styles.module.css b/packages/docs/src/pages/whitepaper/styles.module.css new file mode 100644 index 0000000000..8afdcb3998 --- /dev/null +++ b/packages/docs/src/pages/whitepaper/styles.module.css @@ -0,0 +1,16 @@ +.list { + font-size: 20px; + margin: 0; + list-style: none; + column-count: 2; + column-gap: 32px; + display: block; +} + +.list a { + display: block; + padding: 16px; + border: 1px solid rgb(230, 236, 241); + box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px; + margin: 0 0 21px; +} diff --git a/packages/docs/src/pages/whitepaper/whitepaper.md b/packages/docs/src/pages/whitepaper/whitepaper.md new file mode 100644 index 0000000000..1c8136c669 --- /dev/null +++ b/packages/docs/src/pages/whitepaper/whitepaper.md @@ -0,0 +1,23 @@ +# Whitepaper + +[**English**](https://request.network/assets/pdf/request_whitepaper.pdf) + +[**Bulgarian**](https://request.network/assets/pdf/bulgarian_whitepaper.pdf) + +[**Chinese**](https://request.network/assets/pdf/chinese_whitepaper.pdf) + +[**Dutch**](https://request.network/assets/pdf/dutch_whitepaper.pdf) + +[**French**](https://request.network/assets/pdf/french_whitepaper.pdf) + +[**German**](https://request.network/assets/pdf/german_whitepaper.pdf) + +[**Portuguese**](https://request.network/assets/pdf/portuguese_whitepaper.pdf) + +[**Romanian**](https://request.network/assets/pdf/romanian_whitepaper.pdf) + +[**Slovenian**](https://request.network/assets/pdf/slovenian_whitepaper.pdf) + +[**Spanish**](https://request.network/assets/pdf/spanish_whitepaper.pdf) + +[**Vietnamese**](https://request.network/assets/pdf/vietnamese_whitepaper.pdf) diff --git a/packages/docs/static/img/REQ-05-patterns-02.png b/packages/docs/static/img/REQ-05-patterns-02.png new file mode 100644 index 0000000000..12a827e893 Binary files /dev/null and b/packages/docs/static/img/REQ-05-patterns-02.png differ diff --git a/packages/docs/static/img/REQ-07-hands-02.png b/packages/docs/static/img/REQ-07-hands-02.png new file mode 100644 index 0000000000..17b24f9658 Binary files /dev/null and b/packages/docs/static/img/REQ-07-hands-02.png differ diff --git a/packages/docs/static/img/ReQ-01.png b/packages/docs/static/img/ReQ-01.png new file mode 100644 index 0000000000..d5da7ef1fb Binary files /dev/null and b/packages/docs/static/img/ReQ-01.png differ diff --git a/packages/docs/static/img/RequestProtocol/1-LayersPresentation.jpg b/packages/docs/static/img/RequestProtocol/1-LayersPresentation.jpg new file mode 100644 index 0000000000..5002ddd4d7 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/1-LayersPresentation.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/2-AdvancedRequestPresentation.jpg b/packages/docs/static/img/RequestProtocol/2-AdvancedRequestPresentation.jpg new file mode 100644 index 0000000000..0d74def21d Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/2-AdvancedRequestPresentation.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/2-Encryption.jpg b/packages/docs/static/img/RequestProtocol/2-Encryption.jpg new file mode 100644 index 0000000000..72d98461e3 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/2-Encryption.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/2-RequestPresentation.jpg b/packages/docs/static/img/RequestProtocol/2-RequestPresentation.jpg new file mode 100644 index 0000000000..20ac06ed72 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/2-RequestPresentation.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/3-AdvancedLogicFlow.jpg b/packages/docs/static/img/RequestProtocol/3-AdvancedLogicFlow.jpg new file mode 100644 index 0000000000..167ff83288 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/3-AdvancedLogicFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/3-DataAccessFlow.jpg b/packages/docs/static/img/RequestProtocol/3-DataAccessFlow.jpg new file mode 100644 index 0000000000..30b714a4e5 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/3-DataAccessFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/3-RequestLogicFlow.jpg b/packages/docs/static/img/RequestProtocol/3-RequestLogicFlow.jpg new file mode 100644 index 0000000000..720dd61797 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/3-RequestLogicFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/3-StorageFlow.jpg b/packages/docs/static/img/RequestProtocol/3-StorageFlow.jpg new file mode 100644 index 0000000000..7b4eaf852f Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/3-StorageFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/3-TransactionFlow.jpg b/packages/docs/static/img/RequestProtocol/3-TransactionFlow.jpg new file mode 100644 index 0000000000..639062a7f0 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/3-TransactionFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/3-TransactionFlowEncrypted.jpg b/packages/docs/static/img/RequestProtocol/3-TransactionFlowEncrypted.jpg new file mode 100644 index 0000000000..5569bfc30c Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/3-TransactionFlowEncrypted.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/4-DataAccessAndStorageFlow.jpg b/packages/docs/static/img/RequestProtocol/4-DataAccessAndStorageFlow.jpg new file mode 100644 index 0000000000..a879e0759e Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/4-DataAccessAndStorageFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/4-DataAccessFlow.jpg b/packages/docs/static/img/RequestProtocol/4-DataAccessFlow.jpg new file mode 100644 index 0000000000..e2d737513e Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/4-DataAccessFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/4-RequestLogicFlow.jpg b/packages/docs/static/img/RequestProtocol/4-RequestLogicFlow.jpg new file mode 100644 index 0000000000..9dde052ce9 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/4-RequestLogicFlow.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/4-RequestLogicFlowInvalid.jpg b/packages/docs/static/img/RequestProtocol/4-RequestLogicFlowInvalid.jpg new file mode 100644 index 0000000000..c93bff4423 Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/4-RequestLogicFlowInvalid.jpg differ diff --git a/packages/docs/static/img/RequestProtocol/4-TransactionFlow.jpg b/packages/docs/static/img/RequestProtocol/4-TransactionFlow.jpg new file mode 100644 index 0000000000..ef6542b3de Binary files /dev/null and b/packages/docs/static/img/RequestProtocol/4-TransactionFlow.jpg differ diff --git a/packages/docs/static/img/cropped-favicon-32x32.png b/packages/docs/static/img/cropped-favicon-32x32.png new file mode 100644 index 0000000000..6ed5e19c73 Binary files /dev/null and b/packages/docs/static/img/cropped-favicon-32x32.png differ diff --git a/packages/docs/static/img/info-icon.svg b/packages/docs/static/img/info-icon.svg new file mode 100644 index 0000000000..d76886480e --- /dev/null +++ b/packages/docs/static/img/info-icon.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/docs/static/img/logo.svg b/packages/docs/static/img/logo.svg new file mode 100644 index 0000000000..d615d1822e --- /dev/null +++ b/packages/docs/static/img/logo.svg @@ -0,0 +1 @@ +icon \ No newline at end of file diff --git a/packages/docs/static/img/portal-api-app.gif b/packages/docs/static/img/portal-api-app.gif new file mode 100755 index 0000000000..192f5028aa Binary files /dev/null and b/packages/docs/static/img/portal-api-app.gif differ diff --git a/packages/docs/static/img/portal-api-key.gif b/packages/docs/static/img/portal-api-key.gif new file mode 100644 index 0000000000..93e6f403e2 Binary files /dev/null and b/packages/docs/static/img/portal-api-key.gif differ diff --git a/packages/docs/static/img/request_docs_thumbnail.png b/packages/docs/static/img/request_docs_thumbnail.png new file mode 100644 index 0000000000..7d27d50258 Binary files /dev/null and b/packages/docs/static/img/request_docs_thumbnail.png differ diff --git a/packages/epk-decryption/.vscode/settings.json b/packages/epk-decryption/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/epk-decryption/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/epk-decryption/CHANGELOG.md b/packages/epk-decryption/CHANGELOG.md index 484fdbaf0b..99e5d40da1 100644 --- a/packages/epk-decryption/CHANGELOG.md +++ b/packages/epk-decryption/CHANGELOG.md @@ -3,6 +3,192 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.12](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.12) (2020-06-29) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.3.11](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.11) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.3.10](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.10) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.3.9](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.9) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.3.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.8) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.3.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.7) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.3.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.6) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/epk-decryption + + + + + +## [0.3.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.5) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/epk-decryption + + + + + +## [0.3.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.3...@requestnetwork/epk-decryption@0.3.4) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/epk-decryption + + + + + ## [0.3.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-decryption@0.3.2...@requestnetwork/epk-decryption@0.3.3) (2019-11-20) **Note:** Version bump only for package @requestnetwork/epk-decryption diff --git a/packages/epk-decryption/package.json b/packages/epk-decryption/package.json index e35f0a1259..0130a9d7d0 100644 --- a/packages/epk-decryption/package.json +++ b/packages/epk-decryption/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/epk-decryption", - "version": "0.3.3", + "version": "0.3.12", "publishConfig": { "access": "public" }, @@ -34,20 +34,20 @@ "build": "run-s build:commonjs build:umd", "build:commonjs": "tsc -b", "build:umd": "webpack", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0" + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0" }, "devDependencies": { "@types/chai": "4.1.7", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@typescript-eslint/parser": "1.2.0", "amd-loader": "0.0.8", "awesome-typescript-loader": "5.2.1", @@ -55,21 +55,21 @@ "chai-as-promised": "7.1.1", "duplicate-package-checker-webpack-plugin": "3.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", + "mocha": "6.2.2", "npm-run-all": "4.1.5", - "nyc": "13.2.0", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", "source-map-support": "0.5.13", "terser-webpack-plugin": "1.3.0", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2", "webpack": "4.38.0", - "webpack-bundle-analyzer": "3.3.2", - "webpack-cli": "3.3.6" + "webpack-bundle-analyzer": "3.6.0", + "webpack-cli": "3.3.10" } } diff --git a/packages/epk-signature/.vscode/settings.json b/packages/epk-signature/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/epk-signature/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/epk-signature/CHANGELOG.md b/packages/epk-signature/CHANGELOG.md index 5d069dc313..56f52a186c 100644 --- a/packages/epk-signature/CHANGELOG.md +++ b/packages/epk-signature/CHANGELOG.md @@ -3,6 +3,192 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.5.13](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.13) (2020-06-29) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.12](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.12) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.11](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.11) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.10](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.10) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.9](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.9) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.8) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.7) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/epk-signature + + + + + +## [0.5.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.6) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/epk-signature + + + + + +## [0.5.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.4...@requestnetwork/epk-signature@0.5.5) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/epk-signature + + + + + ## [0.5.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/epk-signature@0.5.3...@requestnetwork/epk-signature@0.5.4) (2019-11-20) **Note:** Version bump only for package @requestnetwork/epk-signature diff --git a/packages/epk-signature/package.json b/packages/epk-signature/package.json index 1e6e86910a..219051dee1 100644 --- a/packages/epk-signature/package.json +++ b/packages/epk-signature/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/epk-signature", - "version": "0.5.4", + "version": "0.5.13", "publishConfig": { "access": "public" }, @@ -34,19 +34,19 @@ "build": "run-s build:commonjs build:umd", "build:commonjs": "tsc -b", "build:umd": "webpack", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0" + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0" }, "devDependencies": { "@types/chai": "4.1.7", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@typescript-eslint/parser": "1.2.0", "amd-loader": "0.0.8", "awesome-typescript-loader": "5.2.1", @@ -54,22 +54,22 @@ "chai-as-promised": "7.1.1", "duplicate-package-checker-webpack-plugin": "3.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", + "mocha": "6.2.2", "npm-run-all": "4.1.5", - "nyc": "13.2.0", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", "source-map-support": "0.5.13", "terser-webpack-plugin": "1.3.0", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2", "webpack": "4.38.0", - "webpack-bundle-analyzer": "3.3.2", - "webpack-cli": "3.3.6" + "webpack-bundle-analyzer": "3.6.0", + "webpack-cli": "3.3.10" }, "gitHead": "6155223cfce769e48ccae480c510b35b4f54b4d0" } diff --git a/packages/ethereum-storage/.vscode/settings.json b/packages/ethereum-storage/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/ethereum-storage/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/ethereum-storage/CHANGELOG.md b/packages/ethereum-storage/CHANGELOG.md index 71f2389738..01e103afa7 100644 --- a/packages/ethereum-storage/CHANGELOG.md +++ b/packages/ethereum-storage/CHANGELOG.md @@ -3,6 +3,276 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.10.0) (2020-06-29) + + +### Bug Fixes + +* enhance node synchronization and storing of ignored data ([#205](https://github.com/RequestNetwork/requestNetwork/issues/205)) ([fb6add2](https://github.com/RequestNetwork/requestNetwork/commit/fb6add27b0507e5db3a19682dbcda90274ab19f1)) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) +* resubmit stuck transaction with more gas ([#239](https://github.com/RequestNetwork/requestNetwork/issues/239)) ([cf7f92e](https://github.com/RequestNetwork/requestNetwork/commit/cf7f92eb6ee9f0c5da427f37fa5f12f56812a221)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.9.0) (2020-05-04) + + +### Bug Fixes + +* enhance node synchronization and storing of ignored data ([#205](https://github.com/RequestNetwork/requestNetwork/issues/205)) ([fb6add2](https://github.com/RequestNetwork/requestNetwork/commit/fb6add27b0507e5db3a19682dbcda90274ab19f1)) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.8.0) (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.7.0) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.6.0) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.5.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.5.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.4.8) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/ethereum-storage + + + + + +## [0.4.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.4.7) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/ethereum-storage + + + + + +## [0.4.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.5...@requestnetwork/ethereum-storage@0.4.6) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/ethereum-storage + + + + + ## [0.4.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/ethereum-storage@0.4.4...@requestnetwork/ethereum-storage@0.4.5) (2019-11-20) **Note:** Version bump only for package @requestnetwork/ethereum-storage diff --git a/packages/ethereum-storage/README.md b/packages/ethereum-storage/README.md index c868ddd390..45ae0e6c4d 100644 --- a/packages/ethereum-storage/README.md +++ b/packages/ethereum-storage/README.md @@ -3,8 +3,6 @@ `@requestnetwork/ethereum-storage` is a package part of the [Request Network protocol](https://github.com/RequestNetwork/requestNetwork). It is an implementation of the Storage layer of Request Network protocol that uses IPFS to immutably store the data and uses the Ethereum network to persist the IPFS hash of the data and make them permanently available to everyone. -The package also stores the source and artifacts of the smart contract deployed on Ethereum. - To use Infura to connect to an Ethereum node, get an infura token on [infura.io](infura.io) and use as provider `"NETWORK_YOU_WANT.infura.io/v3/YOUR_INFURA_TOKEN"`. @@ -37,7 +35,12 @@ const ipfsGatewayConnection: StorageTypes.IIpfsGatewayConnection = { timeout: 1000, }; -const ethereumStorage = new EthereumStorage(ipfsGatewayConnection, web3Connection); +const ethereumStorage = new EthereumStorage( + // Give an external url of the storage (use to indicate where the buffer data are stored) + 'url.buffer.ethereum.storage', + ipfsGatewayConnection, + web3Connection, +); const data = 'Some data'; @@ -46,11 +49,13 @@ await ethereumStorage.append(data); ## Smart Contract -ethereum-storage source can be downloaded in order to deploy the smart contract on a local environment: +ethereum-storage uses smart contracts to store IPFS hashes. + +The smart contracts can be downloaded in order to deploy them on a local environment: ```bash git clone https://github.com/RequestNetwork/requestNetwork.git -cd requestNetwork/packages/ethereum-storage +cd requestNetwork/packages/smart-contracts yarn install yarn run build yarn run ganache @@ -68,7 +73,7 @@ There are 3 smart contracts: - `RequestOpenHashSubmitter` entry point to add hashes in `RequestHashStorage`. It gives the rules to get the right to submit hashes and collect the fees. This contract must be whitelisted in `RequestHashStorage`. The only condition for adding hash is to pay the fees. - `StorageFeeCollector` parent contract (not deployed) of `RequestOpenHashSubmitter`, computes the fees and send them to the burner. -## Configuring the provider using Truffle and the development network +#### Configuring the provider using Truffle and the development network When deploying the smart contracts for development you can manually set the provider host and port via env variables: diff --git a/packages/ethereum-storage/migrations/2_deploy_contracts.js b/packages/ethereum-storage/migrations/2_deploy_contracts.js deleted file mode 100644 index e77b61406c..0000000000 --- a/packages/ethereum-storage/migrations/2_deploy_contracts.js +++ /dev/null @@ -1,34 +0,0 @@ -const RequestHashStorage = artifacts.require('./RequestHashStorage.sol'); -const RequestOpenHashSubmitter = artifacts.require('./RequestOpenHashSubmitter.sol'); -const erc20 = artifacts.require('./TestERC20.sol'); - -const addressContractBurner = '0xfCb4393e7fAef06fAb01c00d67c1895545AfF3b8'; - -// Deploys, set up the contracts -module.exports = async function(deployer) { - try { - // Deploy the contract RequestHashStorage - await deployer.deploy(RequestHashStorage); - console.log('RequestHashStorage Contract deployed: ' + RequestHashStorage.address); - - // Deploy the contract RequestOpenHashSubmitter - await deployer.deploy( - RequestOpenHashSubmitter, - RequestHashStorage.address, - addressContractBurner, - ); - console.log('RequestOpenHashSubmitter Contract deployed: ' + RequestOpenHashSubmitter.address); - - // Whitelist the requestSubmitter in requestHashDeclaration - const instanceRequestHashStorage = await RequestHashStorage.deployed(); - instanceRequestHashStorage.addWhitelisted(RequestOpenHashSubmitter.address); - console.log('requestSubmitter Whitelisted in requestHashDeclaration'); - - // Deploy the ERC20 contract - await deployer.deploy(erc20, 1000); // 1000 initial supply - - console.log('Contracts initialized'); - } catch (e) { - console.error(e); - } -}; diff --git a/packages/ethereum-storage/package.json b/packages/ethereum-storage/package.json index 7a82b8c4f0..cbc0d75c69 100644 --- a/packages/ethereum-storage/package.json +++ b/packages/ethereum-storage/package.json @@ -1,14 +1,13 @@ { "name": "@requestnetwork/ethereum-storage", - "version": "0.4.5", + "version": "0.10.0", "publishConfig": { "access": "public" }, "description": "Request Network storage layer based on ethereum.", "keywords": [ "requestnetwork", - "ethereum-storage", - "smart-contracts" + "ethereum-storage" ], "repository": { "type": "git", @@ -22,81 +21,68 @@ "engines": { "node": ">=8.0.0" }, - "main": "dist/src/lib/index.js", - "types": "dist/src/lib/index.d.ts", + "main": "dist/src/index.js", + "types": "dist/src/index.d.ts", "directories": { - "lib": "src/lib", - "test": "test/lib" + "lib": "src", + "test": "test" }, "files": [ "dist" ], "scripts": { - "build:lib": "tsc -b", - "build:sol": "truffle compile --contracts_directory=./src", - "build": "yarn run build:lib && yarn run build:sol", - "clean:lib": "shx rm -rf dist", - "clean:sol": "shx rm -rf build", - "clean": "yarn run clean:lib && yarn run clean:sol", - "lint:lib": "tslint --project . && eslint \"src/lib/**/*.ts\"", - "lint:sol": "solium --dir src/contracts", - "lint": "yarn run lint:lib && yarn run lint:sol", + "build": "tsc -b", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", + "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "ganache": "ganache-cli -l 90000000 -p 8545 -m \"candy maple cake sugar pudding cream honey rich smooth crumble sweet treat\"", - "deploy": "truffle --contracts_directory=./src deploy", - "test:lib": "nyc mocha --timeout=5000 --require ts-node/register --require source-map-support/register \"test/lib/**/*.ts\"", - "test:lib:watch": "nyc mocha --watch --watch-extensions ts --timeout=5000 --require ts-node/register --require source-map-support/register \"test/lib/**/*.ts\"", - "test:sol": "truffle test --contracts_directory=./src test/contracts/*.js", - "test": "yarn run test:lib && yarn run test:sol", + "test": "nyc mocha --timeout=5000 --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "nyc mocha --watch --watch-extensions ts --timeout=5000 --require source-map-support/register \"test/**/*.ts\"", "init-ipfs": "node scripts/init-ipfs.js" }, "dependencies": { - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/smart-contracts": "0.9.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "bluebird": "3.5.3", "bn.js": "4.11.8", "form-data": "2.3.3", - "ipfs-unixfs": "0.1.16", + "ipfs-unixfs": "0.3.0", "keyv": "3.1.0", "node-fetch": "2.6.0", "shelljs": "0.8.3", - "sinon": "7.3.2", - "web3-eth": "1.0.0-beta.37", + "web3-eth": "1.2.0", "web3-utils": "1.2.1", "yargs": "12.0.5" }, "devDependencies": { - "@openzeppelin/contracts": "2.4.0", "@truffle/hdwallet-provider": "1.0.18", - "@types/bluebird": "3.5.27", + "@types/bluebird": "3.5.29", "@types/chai": "4.1.7", "@types/chai-as-promised": "7.1.0", "@types/chai-spies": "1.0.0", - "@types/fetch-mock": "7.3.1", + "@types/fetch-mock": "7.3.2", "@types/form-data": "2.2.1", "@types/keyv": "3.1.0", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", + "@types/sinon": "7.5.0", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-bignumber": "3.0.0", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "fetch-mock": "7.3.3", - "ganache-cli": "6.3.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", - "openzeppelin-solidity": "2.1.2", - "openzeppelin-test-helpers": "0.1.2", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", + "sinon": "7.5.0", "solium": "1.2.2", "source-map-support": "0.5.13", - "truffle": "5.0.3", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2", "web3-providers-http": "1.2.1" diff --git a/packages/ethereum-storage/src/lib/config.ts b/packages/ethereum-storage/src/config.ts similarity index 95% rename from packages/ethereum-storage/src/lib/config.ts rename to packages/ethereum-storage/src/config.ts index 609696813c..109cdb7534 100644 --- a/packages/ethereum-storage/src/lib/config.ts +++ b/packages/ethereum-storage/src/config.ts @@ -15,6 +15,7 @@ const config: any = { }, retryDelay: 0, safeGasPriceLimit: '200000000000', + transactionPollingTimeout: 300, }, ipfs: { defaultNode: { @@ -159,3 +160,10 @@ export function getIpfsExpectedBootstrapNodes(): string[] { export function getMaxIpfsReadRetry(): number { return config.ipfs.maxIpfsReadRetry; } + +/** + * Retrieve from config the amount of time to wait before a transaction is considered failed + */ +export function getTransactionPollingTimeout(): number { + return config.ethereum.transactionPollingTimeout; +} diff --git a/packages/ethereum-storage/src/lib/ethereum-blocks.ts b/packages/ethereum-storage/src/ethereum-blocks.ts similarity index 100% rename from packages/ethereum-storage/src/lib/ethereum-blocks.ts rename to packages/ethereum-storage/src/ethereum-blocks.ts diff --git a/packages/ethereum-storage/src/ethereum-entries-to-ipfs-content.ts b/packages/ethereum-storage/src/ethereum-entries-to-ipfs-content.ts new file mode 100644 index 0000000000..59e19f9b19 --- /dev/null +++ b/packages/ethereum-storage/src/ethereum-entries-to-ipfs-content.ts @@ -0,0 +1,239 @@ +import * as Bluebird from 'bluebird'; + +import { LogTypes, StorageTypes } from '@requestnetwork/types'; +import { + getMaxIpfsReadRetry, +} from './config'; + +import IgnoredDataIds from './ignored-dataIds'; +import IpfsConnectionError from './ipfs-connection-error'; +import IpfsManager from './ipfs-manager'; + +// rate of the size of the Header of a ipfs file regarding its content size +// used to estimate the size of a ipfs file from the content size +const SAFE_RATE_HEADER_SIZE: number = 0.3; +// max ipfs header size +const SAFE_MAX_HEADER_SIZE: number = 500; + +/** + * Verify the hashes are present on IPFS for the corresponding ethereum entry + * Filtered incorrect hashes + * @param ethereumEntries Ethereum entries from the smart contract + * @returns Filtered list of dataId with metadata + */ +export default async function EthereumEntriesToIpfsContent( + ethereumEntries: StorageTypes.IEthereumEntry[], + ipfsManager: IpfsManager, + ignoredDataIdsIndex: IgnoredDataIds, + logger: LogTypes.ILogger, + maxConcurrency: number, + ): Promise { + const totalCount: number = ethereumEntries.length; + let successCount: number = 0; + let successCountOnFirstTry: number = 0; + let ipfsConnectionErrorCount: number = 0; + let wrongFeesCount: number = 0; + let incorrectFileCount: number = 0; + + // Contains results from readHashOnIPFS function + // We store hashAndSize in this array in order to know which hashes have not been found on IPFS + let allIpfsContentOrErrors: Array<{ + ipfsContent: StorageTypes.IEntry | null; + entryWithError: StorageTypes.IEthereumEntry | null; + }>; + + // Final array of dataIds, content and meta + const finalIpfsContents: StorageTypes.IEntry[] = []; + let ethereumEntriesToProcess: StorageTypes.IEthereumEntry[] = ethereumEntries.slice(); + + // Try to read the hashes on IPFS + // The operation is done at least once and retried depending on the readOnIPFSRetry config + for (let tryIndex = 0; tryIndex < 1 + getMaxIpfsReadRetry(); tryIndex++) { + // Reset for each retry + ipfsConnectionErrorCount = 0; + + if (tryIndex > 0) { + logger.debug(`Retrying to read hashes on IPFS`, ['ipfs']); + } + + allIpfsContentOrErrors = await Bluebird.map( + ethereumEntriesToProcess, + // Read hash on IPFS and retrieve content corresponding to the hash + // Reject on error when no file is found on IPFS + // or when the declared size doesn't correspond to the size of the content stored on ipfs + async (ethereumEntry: StorageTypes.IEthereumEntry) => { + return getIpfsContent(ethereumEntry, tryIndex + 1, ipfsManager, logger); + }, + { + concurrency: maxConcurrency, + }, + ); + + // flush the list of entries to process + ethereumEntriesToProcess = []; + + // Store found hashes in entries + // The hashes to retry to read are the hashes where readHashOnIPFS returned null + for (const { ipfsContent, entryWithError } of allIpfsContentOrErrors) { + if (ipfsContent) { + // content found and not error + finalIpfsContents.push(ipfsContent); + } else if (entryWithError) { + const errorType = entryWithError.error!.type; + if (errorType === StorageTypes.ErrorEntries.INCORRECT_FILE) { + incorrectFileCount++; + // no retry needed, just store it + await ignoredDataIdsIndex.save(entryWithError); + } else if (errorType === StorageTypes.ErrorEntries.WRONG_FEES) { + wrongFeesCount++; + // no retry needed, just store it + await ignoredDataIdsIndex.save(entryWithError); + } else if (errorType === StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR) { + ipfsConnectionErrorCount++; + // push it for a retry + ethereumEntriesToProcess.push(entryWithError); + } else { + throw new Error(`Unexpected Error for the hash: ${entryWithError.hash}, ${entryWithError.error?.type}, ${entryWithError.error?.message}`); + } + } + } + + successCount = finalIpfsContents.length; + + logger.debug(`${successCount}/${totalCount} retrieved dataIds after try ${tryIndex + 1}`, ['ipfs']); + + if (tryIndex === 0) { + successCountOnFirstTry = successCount; + } + } + + // Save the entries not successfully retrieved after the retries + for (const remainingEntry of ethereumEntriesToProcess) { + // store the ipfs ignored after the retried + await ignoredDataIdsIndex.save(remainingEntry); + } + + // Clean the ignored dataIds + for (const ipfsContent of finalIpfsContents) { + // store the id successfully retrieved from the ignored ones + await ignoredDataIdsIndex.delete(ipfsContent.id); + } + + logger.info( + `getData on ${totalCount} events, ${successCount} retrieved (${successCount - + successCountOnFirstTry} after retries), ${ipfsConnectionErrorCount} not found, ${incorrectFileCount} incorrect files, ${wrongFeesCount} with wrong fees`, + ['metric', 'successfullyRetrieved'], + ); + + return finalIpfsContents; +} + +/** + * Tries to get the ipfs content or return the error + * @param ethereumEntry entry information to get the ipfs from + * @returns the ipfsContent and meta or the entry with the error + */ +async function getIpfsContent( + ethereumEntry: StorageTypes.IEthereumEntry, + tryIndex: number, + ipfsManager: IpfsManager, + logger: LogTypes.ILogger, +): Promise<{ + ipfsContent: StorageTypes.IEntry | null; + entryWithError: StorageTypes.IEthereumEntry | null; +}> { + // Check if the event log is incorrect + if ( + typeof ethereumEntry.hash === 'undefined' || + typeof ethereumEntry.feesParameters === 'undefined' + ) { + throw Error('The event log has no hash or feesParameters'); + } + if (typeof ethereumEntry.meta === 'undefined') { + throw Error('The event log has no metadata'); + } + + // Get content from ipfs and verify provided size is correct + let ipfsObject; + + // To limit the read response size, calculate a reasonable margin for the IPFS headers compared to the size stored on ethereum + const ipfsHeaderMargin = Math.max( + ethereumEntry.feesParameters.contentSize * SAFE_RATE_HEADER_SIZE, + SAFE_MAX_HEADER_SIZE, + ); + + try { + const startTime = Date.now(); + // Send ipfs request + ipfsObject = await ipfsManager.read( + ethereumEntry.hash, + Number(ethereumEntry.feesParameters.contentSize) + ipfsHeaderMargin, + ); + logger.debug( + `read ${ethereumEntry.hash}, try; ${tryIndex}. Took ${Date.now() - startTime} ms`, + ['ipfs'], + ); + } catch (error) { + const errorMessage = error.message || error; + + // Check the type of the error + if (error instanceof IpfsConnectionError) { + logger.info(`IPFS connection error when trying to fetch: ${ethereumEntry.hash}`, [ + 'ipfs', + ]); + logger.debug(`IPFS connection error : ${errorMessage}`, ['ipfs']); + // An ipfs connection error occurred (for example a timeout), therefore we would eventually retry to find the has + return { + entryWithError: { + ...ethereumEntry, + error: { message: errorMessage, type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR }, + }, + ipfsContent: null, + }; + } else { + logger.info(`Incorrect file for hash: ${ethereumEntry.hash}`, ['ipfs']); + + // No need to retry to find this hash + return { + entryWithError: { + ...ethereumEntry, + error: { message: errorMessage, type: StorageTypes.ErrorEntries.INCORRECT_FILE }, + }, + ipfsContent: null, + }; + } + } + + const contentSizeDeclared = ethereumEntry.feesParameters.contentSize; + + // Check if the declared size is higher or equal to the size of the actual file + // If the declared size is higher, it's not considered as a problem since it means the hash submitter has paid a bigger fee than he had to + if (!ipfsObject || ipfsObject.ipfsSize > contentSizeDeclared) { + logger.info(`Incorrect declared size for hash: ${ethereumEntry.hash}`, ['ipfs']); + + // No need to retry to find this hash + return { + entryWithError: { + ...ethereumEntry, + error: { message: `Incorrect declared size`, type: StorageTypes.ErrorEntries.WRONG_FEES }, + }, + ipfsContent: null, + }; + } + + // Get meta data from ethereum + const ethereumMetadata = ethereumEntry.meta; + + const ipfsContent = { + content: ipfsObject.content, + id: ethereumEntry.hash, + meta: { + ethereum: ethereumMetadata, + ipfs: { size: ipfsObject.ipfsSize }, + state: StorageTypes.ContentState.CONFIRMED, + storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, + timestamp: ethereumMetadata.blockTimestamp, + }, + }; + return { ipfsContent, entryWithError: null }; +} diff --git a/packages/ethereum-storage/src/lib/ethereum-metadata-cache.ts b/packages/ethereum-storage/src/ethereum-metadata-cache.ts similarity index 76% rename from packages/ethereum-storage/src/lib/ethereum-metadata-cache.ts rename to packages/ethereum-storage/src/ethereum-metadata-cache.ts index 8af318357f..2c8c834844 100644 --- a/packages/ethereum-storage/src/lib/ethereum-metadata-cache.ts +++ b/packages/ethereum-storage/src/ethereum-metadata-cache.ts @@ -16,6 +16,8 @@ export default class EthereumMetadataCache { */ public metadataCache: Keyv; + public listDataIds: Keyv; + /** * Manager for the storage smart contract * This attribute is used to get metadata in case they're not registered yet @@ -34,6 +36,11 @@ export default class EthereumMetadataCache { namespace: 'ethereumMetadata', store, }); + + this.listDataIds = new Keyv({ + namespace: 'listDataIds', + store, + }); } /** @@ -49,6 +56,7 @@ export default class EthereumMetadataCache { // PROT-503: We should ensure the corresponding metadata is the metadata of the first occurrence of the dataId if (!(await this.metadataCache.get(dataId))) { await this.metadataCache.set(dataId, meta); + await this.updateDataId(dataId); } } @@ -63,11 +71,43 @@ export default class EthereumMetadataCache { // If the metadata has not been saved in the cache yet // we get them with smartContractManager and save them let metadata: StorageTypes.IEthereumMetadata | undefined = await this.metadataCache.get(dataId); + if (!metadata) { metadata = await this.smartContractManager.getMetaFromEthereum(dataId); await this.metadataCache.set(dataId, metadata); + await this.updateDataId(dataId); } return metadata; } + + /** + * Get the list of data ids stored + * + * @returns the list of data ids stored + */ + public async getDataIds(): Promise { + const listDataIds: string[] | undefined = await this.listDataIds.get('list'); + if (!listDataIds) { + return []; + } + return listDataIds; + } + + /** + * Update the list of data ids stored + * + * @param dataId data id to add to the list + * @returns + */ + private async updateDataId(dataId: string): Promise { + let listDataIds: string[] | undefined = await this.listDataIds.get('list'); + if (!listDataIds) { + listDataIds = []; + } + if (!listDataIds.includes(dataId)) { + listDataIds.push(dataId); + await this.listDataIds.set('list', listDataIds); + } + } } diff --git a/packages/ethereum-storage/src/lib/ethereum-storage.ts b/packages/ethereum-storage/src/ethereum-storage.ts similarity index 59% rename from packages/ethereum-storage/src/lib/ethereum-storage.ts rename to packages/ethereum-storage/src/ethereum-storage.ts index b9e68ab274..42bac20261 100644 --- a/packages/ethereum-storage/src/lib/ethereum-storage.ts +++ b/packages/ethereum-storage/src/ethereum-storage.ts @@ -1,25 +1,17 @@ import { LogTypes, StorageTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import * as Bluebird from 'bluebird'; -import { - getIpfsExpectedBootstrapNodes, - getMaxConcurrency, - getMaxIpfsReadRetry, - getPinRequestConfig, -} from './config'; +import { EventEmitter } from 'events'; +import { getIpfsExpectedBootstrapNodes, getMaxConcurrency, getPinRequestConfig } from './config'; + +import ethereumEntriesToIpfsContent from './ethereum-entries-to-ipfs-content'; import EthereumMetadataCache from './ethereum-metadata-cache'; -import IpfsConnectionError from './ipfs-connection-error'; +import IgnoredDataIds from './ignored-dataIds'; import IpfsManager from './ipfs-manager'; import SmartContractManager from './smart-contract-manager'; import * as Keyv from 'keyv'; -// rate of the size of the Header of a ipfs file regarding its content size -// used to estimate the size of a ipfs file from the content size -const SAFE_RATE_HEADER_SIZE: number = 0.3; -// max ipfs header size -const SAFE_MAX_HEADER_SIZE: number = 500; - // time to wait before considering the web3 provider is not reachable const WEB3_PROVIDER_TIMEOUT: number = 10000; @@ -45,16 +37,23 @@ export default class EthereumStorage implements StorageTypes.IStorage { */ public ethereumMetadataCache: EthereumMetadataCache; + /** Data ids ignored by the node */ + public ignoredDataIds: IgnoredDataIds; + /** * Maximum number of concurrent calls */ public maxConcurrency: number; /** - * Number of times we retry to read hashes on IPFS - * Left public for testing purpose + * Timestamp of the dataId not mined on ethereum yet + */ + private buffer: { [id: string]: number | undefined }; + + /** + * Url where can be reached the data buffered by this storage */ - public maxIpfsReadRetry: number = getMaxIpfsReadRetry(); + private externalBufferUrl: string; /** * Logger instance @@ -71,6 +70,7 @@ export default class EthereumStorage implements StorageTypes.IStorage { * @param metadataStore a Keyv store to persist the metadata in ethereumMetadataCache */ public constructor( + externalBufferUrl: string, ipfsGatewayConnection?: StorageTypes.IIpfsGatewayConnection, web3Connection?: StorageTypes.IWeb3Connection, { @@ -102,6 +102,9 @@ export default class EthereumStorage implements StorageTypes.IStorage { this.smartContractManager, metadataStore, ); + this.ignoredDataIds = new IgnoredDataIds(metadataStore); + this.buffer = {}; + this.externalBufferUrl = externalBufferUrl; } /** @@ -172,7 +175,7 @@ export default class EthereumStorage implements StorageTypes.IStorage { * @param content Content to add into the storage * @returns Promise resolving id used to retrieve the content */ - public async append(content: string): Promise { + public async append(content: string): Promise { if (!this.isInitialized) { throw new Error('Ethereum storage must be initialized'); } @@ -182,7 +185,7 @@ export default class EthereumStorage implements StorageTypes.IStorage { } // Add content to IPFS and get the hash back - let ipfsHash; + let ipfsHash: string; try { ipfsHash = await this.ipfsManager.add(content); } catch (error) { @@ -190,38 +193,98 @@ export default class EthereumStorage implements StorageTypes.IStorage { } // Get content length from ipfs - let contentSize; + let contentSize: number; try { contentSize = await this.ipfsManager.getContentLength(ipfsHash); } catch (error) { throw Error(`Ipfs get length request error: ${error}`); } + const timestamp = Utils.getCurrentTimestampInSecond(); + const result: StorageTypes.IAppendResult = Object.assign(new EventEmitter(), { + content, + id: ipfsHash, + meta: { + ipfs: { size: contentSize }, + local: { location: this.externalBufferUrl }, + state: StorageTypes.ContentState.PENDING, + storageType: StorageTypes.StorageSystemType.LOCAL, + timestamp, + }, + }); + // store in the buffer the timestamp + this.buffer[ipfsHash] = timestamp; + const feesParameters: StorageTypes.IFeesParameters = { contentSize }; - // Add content hash to ethereum - let ethereumMetadata; + this.smartContractManager + .addHashAndSizeToEthereum(ipfsHash, feesParameters) + .then(async (ethereumMetadata: StorageTypes.IEthereumMetadata) => { + const resultAfterBroadcast: StorageTypes.IEntry = { + content, + id: ipfsHash, + meta: { + ethereum: ethereumMetadata, + ipfs: { size: contentSize }, + state: StorageTypes.ContentState.CONFIRMED, + storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, + timestamp: ethereumMetadata.blockTimestamp, + }, + }; + // Save the metadata of the new ipfsHash into the Ethereum metadata cache + await this.ethereumMetadataCache.saveDataIdMeta(ipfsHash, ethereumMetadata); + + result.emit('confirmed', resultAfterBroadcast); + }) + .catch(error => { + result.emit('error', error); + }); + + return result; + } + + /** + * Add the content to ipfs + * To be used only in case of persisting the hash on ethereum outside the storage + * + * @param content Content to add into the storage + * @returns Promise resolving id used to retrieve the content + */ + public async _ipfsAdd(data: string): Promise { + if (!this.isInitialized) { + throw new Error('Ethereum storage must be initialized'); + } + + if (!data) { + throw Error('No data provided'); + } + + // Add a small check to at least having JSON data added + try { + JSON.parse(data); + } catch (error) { + throw Error(`data not JSON parsable: ${error}`); + } + + // Add content to IPFS and get the hash back + let ipfsHash; try { - ethereumMetadata = await this.smartContractManager.addHashAndSizeToEthereum( - ipfsHash, - feesParameters, - ); + ipfsHash = await this.ipfsManager.add(data); } catch (error) { - throw Error(`Smart contract error: ${error}`); + throw Error(`Ipfs add request error: ${error}`); } - // Save the metadata of the new ipfsHash into the Ethereum metadata cache - await this.ethereumMetadataCache.saveDataIdMeta(ipfsHash, ethereumMetadata); + // Get content length from ipfs + let ipfsSize; + try { + ipfsSize = await this.ipfsManager.getContentLength(ipfsHash); + } catch (error) { + throw new Error(`Ipfs get length request error: ${error}`); + } return { - content, - id: ipfsHash, - meta: { - ethereum: ethereumMetadata, - ipfs: { size: contentSize }, - storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, - timestamp: ethereumMetadata.blockTimestamp, - }, + ipfsHash, + ipfsSize, }; } @@ -240,29 +303,51 @@ export default class EthereumStorage implements StorageTypes.IStorage { // Get Ethereum metadata let ethereumMetadata; + let bufferTimestamp: number | undefined; + let ipfsObject; try { + // Check if the data as been added on ethereum ethereumMetadata = await this.ethereumMetadataCache.getDataIdMeta(id); + + // Clear buffer if needed + if (this.buffer[id]) { + this.buffer[id] = undefined; + } } catch (error) { - throw Error(`Ethereum meta read request error: ${error}`); + // if not found, check the buffer + bufferTimestamp = this.buffer[id]; + if (!bufferTimestamp) { + throw Error('No content found from this id'); + } } // Send ipfs request - let ipfsObject; try { ipfsObject = await this.ipfsManager.read(id); } catch (error) { throw Error(`Ipfs read request error: ${error}`); } + const meta = ethereumMetadata + ? { + ethereum: ethereumMetadata, + ipfs: { size: ipfsObject.ipfsSize }, + state: StorageTypes.ContentState.CONFIRMED, + storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, + timestamp: ethereumMetadata.blockTimestamp, + } + : { + ipfs: { size: ipfsObject.ipfsSize }, + local: { location: this.externalBufferUrl }, + state: StorageTypes.ContentState.PENDING, + storageType: StorageTypes.StorageSystemType.LOCAL, + timestamp: bufferTimestamp || 0, + }; + return { content: ipfsObject.content, id, - meta: { - ethereum: ethereumMetadata, - ipfs: { size: ipfsObject.ipfsSize }, - storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, - timestamp: ethereumMetadata.blockTimestamp, - }, + meta, }; } @@ -306,6 +391,53 @@ export default class EthereumStorage implements StorageTypes.IStorage { return contentDataIdAndMeta; } + /** + * Try to get some previous ignored data + * + * @param options timestamp boundaries for the data retrieval + * @returns Promise resolving stored data + */ + public async getIgnoredData(): Promise { + if (!this.isInitialized) { + throw new Error('Ethereum storage must be initialized'); + } + this.logger.info('Getting some previous ignored dataIds', ['ethereum']); + + const ethereumEntries: StorageTypes.IEthereumEntry[] = await this.ignoredDataIds.getDataIdsToRetry(); + + // If no hash was found on ethereum, we return an empty list + if (!ethereumEntries.length) { + this.logger.info('No new data found.', ['ethereum']); + return []; + } + + this.logger.debug('Fetching data from IPFS and checking correctness', ['ipfs']); + + const entries = await ethereumEntriesToIpfsContent( + ethereumEntries, + this.ipfsManager, + this.ignoredDataIds, + this.logger, + this.maxConcurrency, + ); + + const ids = entries.map(entry => entry.id) || []; + // Pin data asynchronously + // tslint:disable-next-line:no-floating-promises + this.pinDataToIPFS(ids); + + // Save existing ethereum metadata to the ethereum metadata cache + for (const entry of entries) { + const ethereumMetadata = entry.meta.ethereum; + if (ethereumMetadata) { + // PROT-504: The saving of dataId's metadata should be encapsulated when retrieving dataId inside smart contract (getPastEvents) + await this.ethereumMetadataCache.saveDataIdMeta(entry.id, ethereumMetadata); + } + } + + return entries; + } + /** * Pin an array of IPFS hashes * @@ -335,6 +467,33 @@ export default class EthereumStorage implements StorageTypes.IStorage { } } + /** + * Get Information on the dataIds retrieved and ignored by the ethereum storage + * + * @param detailed if true get the list of the files hash + * @returns Promise resolving object with dataIds retrieved and ignored + */ + public async _getStatus(detailed: boolean = false): Promise { + const dataIds = await this.ethereumMetadataCache.getDataIds(); + const dataIdsWithReason = await this.ignoredDataIds.getDataIdsWithReasons(); + + const ethereum = this.smartContractManager.getConfig(); + const ipfs = await this.ipfsManager.getConfig(); + + return { + dataIds: { + count: dataIds.length, + values: detailed ? dataIds : undefined, + }, + ethereum, + ignoredDataIds: { + count: Object.keys(dataIdsWithReason).length, + values: detailed ? dataIdsWithReason : undefined, + }, + ipfs, + }; + } + /** * Get all dataId and the contents stored on the storage * @@ -364,7 +523,13 @@ export default class EthereumStorage implements StorageTypes.IStorage { this.logger.debug('Fetching data from IPFS and checking correctness', ['ipfs']); - const entries = await this.EthereumEntriesToEntries(ethereumEntries); + const entries = await ethereumEntriesToIpfsContent( + ethereumEntries, + this.ipfsManager, + this.ignoredDataIds, + this.logger, + this.maxConcurrency, + ); const ids = entries.map(entry => entry.id) || []; // Pin data asynchronously @@ -386,173 +551,6 @@ export default class EthereumStorage implements StorageTypes.IStorage { }; } - /** - * Verify the hashes are present on IPFS for the corresponding ethereum entry - * Filtered incorrect hashes - * @param ethereumEntries Ethereum entries from the smart contract - * @returns Filtered list of dataId with metadata - */ - private async EthereumEntriesToEntries( - ethereumEntries: StorageTypes.IEthereumEntry[], - ): Promise { - const totalCount: number = ethereumEntries.length; - let successCount: number = 0; - let successCountOnFirstTry: number = 0; - let ipfsConnectionErrorCount: number = 0; - let wrongFeesCount: number = 0; - let incorrectFileCount: number = 0; - - // Contains results from readHashOnIPFS function - // We store hashAndSize in this array in order to know which hashes have not been found on IPFS - let entriesAndEthereumEntriesToRetry: Array<{ - entry: StorageTypes.IEntry | null; - ethereumEntryToRetry: StorageTypes.IEthereumEntry | null; - }>; - - // Contains hashes we retry to read on IPFS - let ethereumEntriesToRetry: StorageTypes.IEthereumEntry[] = []; - - // Final array of dataIds and meta - const entries: StorageTypes.IEntry[] = []; - - // Try to read the hashes on IPFS - // The operation is done at least once and retried depending on the readOnIPFSRetry config - for (let tryIndex = 0; tryIndex < 1 + this.maxIpfsReadRetry; tryIndex++) { - // Reset for each retry - ipfsConnectionErrorCount = 0; - - if (tryIndex > 0) { - this.logger.debug(`Retrying to read hashes on IPFS`, ['ipfs']); - } - - entriesAndEthereumEntriesToRetry = await Bluebird.map( - ethereumEntries, - // Read hash on IPFS and retrieve content corresponding to the hash - // Reject on error when no file is found on IPFS - // or when the declared size doesn't correspond to the size of the content stored on ipfs - async (hashAndSize: StorageTypes.IEthereumEntry, currentIndex: number) => { - // Check if the event log is incorrect - if ( - typeof hashAndSize.hash === 'undefined' || - typeof hashAndSize.feesParameters === 'undefined' - ) { - throw Error('The event log has no hash or feesParameters'); - } - if (typeof hashAndSize.meta === 'undefined') { - throw Error('The event log has no metadata'); - } - - // Get content from ipfs and verify provided size is correct - let ipfsObject; - - const startTime = Date.now(); - - // To limit the read response size, calculate a reasonable margin for the IPFS headers compared to the size stored on ethereum - const ipfsHeaderMargin = Math.max( - hashAndSize.feesParameters.contentSize * SAFE_RATE_HEADER_SIZE, - SAFE_MAX_HEADER_SIZE, - ); - - try { - // Send ipfs request - ipfsObject = await this.ipfsManager.read( - hashAndSize.hash, - Number(hashAndSize.feesParameters.contentSize) + ipfsHeaderMargin, - ); - - this.logger.debug( - `[${successCount + currentIndex + 1}/${totalCount}] read ${ - hashAndSize.hash - }, try; ${tryIndex + 1}. Took ${Date.now() - startTime} ms`, - ['ipfs'], - ); - } catch (error) { - const errorMessage = error.message || error; - - // Check the type of the error - if (error instanceof IpfsConnectionError) { - this.logger.info(`IPFS connection error when trying to fetch: ${hashAndSize.hash}`, [ - 'ipfs', - ]); - ipfsConnectionErrorCount++; - this.logger.debug(`IPFS connection error : ${errorMessage}`, ['ipfs']); - - // An ipfs connection error occurred (for example a timeout), therefore we would eventually retry to find the hash - return { entry: null, ethereumEntryToRetry: hashAndSize }; - } else { - this.logger.info(`Incorrect file for hash: ${hashAndSize.hash}`, ['ipfs']); - incorrectFileCount++; - this.logger.debug(`Incorrect file error: ${errorMessage}`, ['ipfs']); - - // No need to retry to find this hash - return { entry: null, ethereumEntryToRetry: null }; - } - } - - const contentSizeDeclared = hashAndSize.feesParameters.contentSize; - - // Check if the declared size is higher or equal to the size of the actual file - // If the declared size is higher, it's not considered as a problem since it means the hash submitter has paid a bigger fee than he had to - if (!ipfsObject || ipfsObject.ipfsSize > contentSizeDeclared) { - this.logger.info(`Incorrect declared size for hash: ${hashAndSize.hash}`, ['ipfs']); - wrongFeesCount++; - - // No need to retry to find this hash - return { entry: null, ethereumEntryToRetry: null }; - } - - // Get meta data from ethereum - const ethereumMetadata = hashAndSize.meta; - - const entry = { - content: ipfsObject.content, - id: hashAndSize.hash, - meta: { - ethereum: ethereumMetadata, - ipfs: { size: ipfsObject.ipfsSize }, - storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, - timestamp: ethereumMetadata.blockTimestamp, - }, - }; - return { entry, ethereumEntryToRetry: null }; - }, - { - concurrency: this.maxConcurrency, - }, - ); - - // Store found hashes in entries - // The hashes to retry to read are the hashes where readHashOnIPFS returned null - entriesAndEthereumEntriesToRetry.forEach(({ entry, ethereumEntryToRetry }) => { - if (entry) { - entries.push(entry); - } else if (ethereumEntryToRetry) { - ethereumEntriesToRetry.push(ethereumEntryToRetry); - } - }); - - // Put the remaining hashes to retrieved in the queue for the next retry - ethereumEntries = ethereumEntriesToRetry; - ethereumEntriesToRetry = []; - - successCount = entries.length; - - this.logger.debug(`${successCount} retrieved dataIds after try ${tryIndex + 1}`, ['ipfs']); - - if (tryIndex === 0) { - successCountOnFirstTry = successCount; - } - } - - this.logger.info( - `getData on ${totalCount} events, ${successCount} retrieved (${successCount - - successCountOnFirstTry} after retries), ${ipfsConnectionErrorCount} not found, ${incorrectFileCount} incorrect files, ${wrongFeesCount} with wrong fees`, - ['metric', 'successfullyRetrieved'], - ); - - return entries; - } - /** * Verify the ipfs node (connectivity and network) * Check if the node is reachable and if the list of bootstrap nodes is correct diff --git a/packages/ethereum-storage/src/lib/ethereum-utils.ts b/packages/ethereum-storage/src/ethereum-utils.ts similarity index 100% rename from packages/ethereum-storage/src/lib/ethereum-utils.ts rename to packages/ethereum-storage/src/ethereum-utils.ts diff --git a/packages/ethereum-storage/src/lib/gas-price-definer.ts b/packages/ethereum-storage/src/gas-price-definer.ts similarity index 100% rename from packages/ethereum-storage/src/lib/gas-price-definer.ts rename to packages/ethereum-storage/src/gas-price-definer.ts diff --git a/packages/ethereum-storage/src/lib/gas-price-providers/etherchain-provider.ts b/packages/ethereum-storage/src/gas-price-providers/etherchain-provider.ts similarity index 100% rename from packages/ethereum-storage/src/lib/gas-price-providers/etherchain-provider.ts rename to packages/ethereum-storage/src/gas-price-providers/etherchain-provider.ts diff --git a/packages/ethereum-storage/src/lib/gas-price-providers/ethgasstation-provider.ts b/packages/ethereum-storage/src/gas-price-providers/ethgasstation-provider.ts similarity index 100% rename from packages/ethereum-storage/src/lib/gas-price-providers/ethgasstation-provider.ts rename to packages/ethereum-storage/src/gas-price-providers/ethgasstation-provider.ts diff --git a/packages/ethereum-storage/src/ignored-dataIds.ts b/packages/ethereum-storage/src/ignored-dataIds.ts new file mode 100644 index 0000000000..6c90e0036b --- /dev/null +++ b/packages/ethereum-storage/src/ignored-dataIds.ts @@ -0,0 +1,194 @@ +import * as Keyv from 'keyv'; + +import { StorageTypes } from '@requestnetwork/types'; + +/** + * Interval time between iteration for the retry + */ +const INTERVAL_RETRY_MS = 60000; // every minute + +/** + * Allows to save and retrieve the dataIds ignored with the reason + */ +export default class IgnoredDataIds { + /** + * Store the reason we ignored data ids in a dictionary + */ + public ignoredDataIds: Keyv; + + /** + * as KeyV don't allow to get the list of the keys, we need to store it manually + * TODO (PROT-1189): replace KeyV by a database service + */ + public listIgnoredDataIds: Keyv; + + /** + * Constructor + * @param store a Keyv store to persist the metadata + */ + public constructor(store?: Keyv.Store) { + this.ignoredDataIds = new Keyv({ + namespace: 'dataIdIgnored', + store, + }); + + this.listIgnoredDataIds = new Keyv({ + namespace: 'listIgnoredDataIds', + store, + }); + } + + /** + * Saves in the cache the reason to ignore the dataId + * @param dataId dataId + * @param reason reason we ignored the dataId + * @param toRetry will be retry later if true + */ + public async save( + entry: StorageTypes.IEthereumEntry, + ): Promise { + const previous = await this.ignoredDataIds.get(entry.hash); + + if (!previous) { + // add the dataId id if new in the store + await this.ignoredDataIds.set(entry.hash, { + entry, + iteration: 1, + lastTryTimestamp: Date.now(), + toRetry: entry.error?.type === StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }); + // update the list + await this.addToDataIdsList(entry.hash); + } else { + // if already in the store + if (previous.toRetry) { + // update it only if it was mean to be retry + await this.ignoredDataIds.set(entry.hash, { + entry, + iteration: previous.iteration as number + 1, + lastTryTimestamp: Date.now(), + toRetry: entry.error?.type === StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }); + } + } + } + + /** + * Removes the ignored dataId from the cache + * @param dataId dataId + */ + public async delete( + dataId: string, + ): Promise { + await this.ignoredDataIds.delete(dataId); + // update the list + await this.deleteFromDataIdsList(dataId); + } + + /** + * Retrieve reason from cache + * @param dataId dataId to get Ethereum metadata from + * @returns the reason or null + */ + public async getReason(dataId: string): Promise { + return (await this.ignoredDataIds.get(dataId))?.entry.error?.message; + } + + /** + * Get the list of data ids stored + * + * @returns the list of data ids stored + */ + public async getDataIds(): Promise { + const listDataId: string[] | undefined = await this.listIgnoredDataIds.get('list'); + return listDataId || []; + } + + /* + * Get the list of data ids that should be retry + * + * @returns the list of data ids + */ + public async getDataIdsToRetry(): Promise { + const listDataId: string[] | undefined = await this.listIgnoredDataIds.get('list'); + + const result: StorageTypes.IEthereumEntry[] = []; + + if (listDataId) { + for (const dataId of Array.from(listDataId)) { + const data: StorageTypes.IIgnoredDataId | undefined = await this.ignoredDataIds.get(dataId); + if (data && this.shouldRetry(data)) { + result.push(data.entry); + } + } + } + + return result; + } + + /** + * Get the list of data ids stored with reason + * + * @returns the list of data ids stored with reason + */ + public async getDataIdsWithReasons(): Promise { + const listDataId: string[] | undefined = await this.listIgnoredDataIds.get('list'); + + if (!listDataId) { + return {}; + } + const result: any = {}; + + for (const dataId of Array.from(listDataId)) { + result[dataId] = await this.ignoredDataIds.get(dataId); + } + + return result; + } + + /** + * Check if it is the time to retry the entry + * @param entry to check + * @returns true if it is time to retry + */ + private shouldRetry( + entry: StorageTypes.IIgnoredDataId, + ): boolean { + // The entry should be retry periodically in an exponential interval of time + // Every time we retry to exponentially increase the time of the next try + return entry.toRetry && (entry.lastTryTimestamp as number + Math.floor(Math.exp(entry.iteration)) * INTERVAL_RETRY_MS) <= Date.now(); + } + + /** + * Update the list of data ids stored with reason + * + * @param dataId data id to add to the list + * @returns + */ + private async addToDataIdsList(dataId: string): Promise { + let listDataIds: string[] | undefined = await this.listIgnoredDataIds.get('list'); + if (!listDataIds) { + listDataIds = []; + } + // update the list only if the dataId is not already stored + if (!listDataIds.includes(dataId)) { + listDataIds.push(dataId); + await this.listIgnoredDataIds.set('list', listDataIds); + } + } + + /** + * Update the list of data ids stored with reason + * + * @param dataId data id to add to the list + * @returns + */ + private async deleteFromDataIdsList(dataId: string): Promise { + let listDataIds: string[] | undefined = await this.listIgnoredDataIds.get('list'); + if (!listDataIds) { + return; + } + listDataIds = listDataIds.filter(e => e !== dataId); + await this.listIgnoredDataIds.set('list', listDataIds); + } +} diff --git a/packages/ethereum-storage/src/lib/index.ts b/packages/ethereum-storage/src/index.ts similarity index 100% rename from packages/ethereum-storage/src/lib/index.ts rename to packages/ethereum-storage/src/index.ts diff --git a/packages/ethereum-storage/src/lib/ipfs-connection-error.ts b/packages/ethereum-storage/src/ipfs-connection-error.ts similarity index 100% rename from packages/ethereum-storage/src/lib/ipfs-connection-error.ts rename to packages/ethereum-storage/src/ipfs-connection-error.ts diff --git a/packages/ethereum-storage/src/lib/ipfs-manager.ts b/packages/ethereum-storage/src/ipfs-manager.ts similarity index 97% rename from packages/ethereum-storage/src/lib/ipfs-manager.ts rename to packages/ethereum-storage/src/ipfs-manager.ts index ede3b14dba..91562d347c 100644 --- a/packages/ethereum-storage/src/lib/ipfs-manager.ts +++ b/packages/ethereum-storage/src/ipfs-manager.ts @@ -545,6 +545,23 @@ export default class IpfsManager { ); } + /** + * Gets current configuration + * + * @return the current configuration attributes + */ + public async getConfig(): Promise { + return { + delayBetweenRetries: this.errorHandlingConfig.delayBetweenRetries, + host: this.ipfsConnection.host, + id: JSON.parse(await this.getIpfsNodeId()), + maxRetries: this.errorHandlingConfig.maxRetries, + port: this.ipfsConnection.port, + protocol: this.ipfsConnection.protocol, + timeout: this.ipfsConnection.timeout, + }; + } + /** * Get the javascript network module used to send request to ipfs * @param protocol Protocol used to send ipfs requests diff --git a/packages/ethereum-storage/src/lib/smart-contract-manager.ts b/packages/ethereum-storage/src/smart-contract-manager.ts similarity index 85% rename from packages/ethereum-storage/src/lib/smart-contract-manager.ts rename to packages/ethereum-storage/src/smart-contract-manager.ts index a3d67ca914..d101d5e3c5 100644 --- a/packages/ethereum-storage/src/lib/smart-contract-manager.ts +++ b/packages/ethereum-storage/src/smart-contract-manager.ts @@ -1,8 +1,7 @@ +import * as SmartContracts from '@requestnetwork/smart-contracts'; import { LogTypes, StorageTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import * as Bluebird from 'bluebird'; -import * as artifactsRequestHashStorageUtils from './artifacts-request-hash-storage-utils'; -import * as artifactsRequestHashSubmitterUtils from './artifacts-request-hash-submitter-utils'; import * as config from './config'; import EthereumBlocks from './ethereum-blocks'; import EthereumUtils from './ethereum-utils'; @@ -23,6 +22,9 @@ const MORE_THAN_XXX_RESULTS_REGEX: RegExp = new RegExp( 'query returned more than [1-9][0-9]* results', ); +// String to match if the Web3 API throws "Transaction was not mined within XXX seconds" error +const TRANSACTION_POLLING_TIMEOUT: string = 'Transaction was not mined within'; + const LENGTH_BYTES32_STRING = 64; /** @@ -112,6 +114,9 @@ export default class SmartContractManager { throw Error(`Can't initialize web3-eth ${error}`); } + // Set the default transaction polling timeout to the value in our config + this.eth.transactionPollingTimeout = config.getTransactionPollingTimeout(); + // Checks if networkId is defined // If not defined we use default value from config this.networkName = @@ -124,23 +129,28 @@ export default class SmartContractManager { throw Error(`The network id ${web3Connection.networkId} doesn't exist`); } - this.hashStorageAddress = artifactsRequestHashStorageUtils.getAddress(this.networkName); - this.hashSubmitterAddress = artifactsRequestHashSubmitterUtils.getAddress(this.networkName); + this.hashStorageAddress = SmartContracts.requestHashStorageArtifact.getAddress( + this.networkName, + ); + + this.hashSubmitterAddress = SmartContracts.requestHashSubmitterArtifact.getAddress( + this.networkName, + ); // Initialize smart contract instance this.requestHashStorage = new this.eth.Contract( - artifactsRequestHashStorageUtils.getContractAbi(), + SmartContracts.requestHashStorageArtifact.getContractAbi(), this.hashStorageAddress, ); this.requestHashSubmitter = new this.eth.Contract( - artifactsRequestHashSubmitterUtils.getContractAbi(), + SmartContracts.requestHashSubmitterArtifact.getContractAbi(), this.hashSubmitterAddress, ); this.timeout = web3Connection.timeout || config.getDefaultEthereumProviderTimeout(); this.creationBlockNumberHashStorage = - artifactsRequestHashStorageUtils.getCreationBlockNumber(this.networkName) || 0; + SmartContracts.requestHashStorageArtifact.getCreationBlockNumber(this.networkName) || 0; this.ethereumBlocks = new EthereumBlocks( this.eth, @@ -240,6 +250,7 @@ export default class SmartContractManager { contentHash: string, feesParameters: StorageTypes.IFeesParameters, gasPrice?: number, + nonce?: number, ): Promise { // Get the account for the transaction const account = await this.getMainAccount(); @@ -278,16 +289,63 @@ export default class SmartContractManager { // When set to true, we use it to ignore next confirmation event function call let ethereumMetadataCreated: boolean = false; + // Keep the transaction hash for future needs + let transactionHash: string = ''; + this.requestHashSubmitter.methods .submitHash(contentHash, feesParametersAsBytes) .send({ from: account, gas: '100000', gasPrice: gasPriceToUse, + nonce, value: fee, }) - .on('error', (transactionError: string) => { - reject(Error(`Ethereum transaction error: ${transactionError}`)); + .on('transactionHash', (hash: any) => { + // Store the transaction hash in case we need it in the future + transactionHash = hash; + }) + .on('error', async (transactionError: string) => { + // If failed because of polling timeout, try to resubmit the transaction with more gas + if ( + transactionError.toString().includes(TRANSACTION_POLLING_TIMEOUT) && + transactionHash + ) { + // If we didn't set the nonce, find the current transaction nonce + if (!nonce) { + const tx = await this.eth.getTransaction(transactionHash); + nonce = tx.nonce; + } + + // Get the new gas price for the transaction + const newGasPrice = new bigNumber( + await gasPriceDefiner.getGasPrice(StorageTypes.GasPriceType.FAST, this.networkName), + ); + + // If the new gas price is higher than the previous, resubmit the transaction + if (newGasPrice.gt(new bigNumber(gasPriceToUse))) { + // Retry transaction with the new gas price and propagate back the result + try { + resolve( + await this.addHashAndSizeToEthereum( + contentHash, + feesParameters, + newGasPrice, + nonce, + ), + ); + } catch (error) { + reject(error); + } + } else { + // The transaction is stuck, but it doesn't seem to be a gas issue. Nothing better to do than to wait... + this.logger.warn( + `Transaction ${transactionHash} hasn't been mined for more than ${config.getTransactionPollingTimeout()} seconds. It may be stuck.`, + ); + } + } else { + reject(Error(`Ethereum transaction error: ${transactionError}`)); + } }) .on('confirmation', (confirmationNumber: number, receiptAfterConfirmation: any) => { if (!ethereumMetadataCreated) { @@ -428,6 +486,24 @@ export default class SmartContractManager { return eventsWithMetaData; } + /** + * Gets current configuration + * + * @return the current configuration attributes + */ + public getConfig(): any { + return { + creationBlockNumberHashStorage: this.creationBlockNumberHashStorage, + currentProvider: this.eth.currentProvider.host, + hashStorageAddress: this.hashStorageAddress, + hashSubmitterAddress: this.hashSubmitterAddress, + maxConcurrency: this.maxConcurrency, + maxRetries: this.maxRetries, + networkName: this.networkName, + retryDelay: this.retryDelay, + }; + } + /** * Get events inside storage smart contract for a specified block range * Some web3 providers, including Infura, send error if the past event number for a specific range is over 1000 diff --git a/packages/ethereum-storage/test/dataids-ignored.test.ts b/packages/ethereum-storage/test/dataids-ignored.test.ts new file mode 100644 index 0000000000..7e3308aee4 --- /dev/null +++ b/packages/ethereum-storage/test/dataids-ignored.test.ts @@ -0,0 +1,153 @@ +import 'mocha'; + +import { StorageTypes } from '@requestnetwork/types'; +import IgnoredDataIds from '../src/ignored-dataIds'; + +import { expect } from 'chai'; +import * as sinon from 'sinon'; + +const entry: StorageTypes.IEthereumEntry = { + error: { + message: 'this is a little test !', + type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }, + feesParameters: { contentSize: 3 }, + hash: 'QmNXA5DyFZkdf4XkUT81nmJSo3nS2bL25x7YepxeoDa6tY', + meta: {} as any, +}; +const entry2: StorageTypes.IEthereumEntry = { + error: { message: 'this is a second test !', type: StorageTypes.ErrorEntries.INCORRECT_FILE }, + feesParameters: { contentSize: 3 }, + hash: 'hash2', + meta: {} as any, +}; + +let ignoredDataIds: IgnoredDataIds; + +// tslint:disable:no-magic-numbers +// tslint:disable:no-unused-expression +describe('Ignored DataIds', () => { + beforeEach(() => { + ignoredDataIds = new IgnoredDataIds(); + }); + + describe('save', () => { + it('can save()', async () => { + await ignoredDataIds.save(entry); + expect(await ignoredDataIds.getReason(entry.hash)).to.be.equal(entry.error!.message); + }); + it('can save() something already saved that can be retried', async () => { + const clock = sinon.useFakeTimers(); + await ignoredDataIds.save(entry); + expect(await ignoredDataIds.getDataIdsWithReasons()).to.be.deep.equal({ + [entry.hash]: { + entry, + iteration: 1, + lastTryTimestamp: 0, + toRetry: true, + }, + }); + + clock.tick(10); + await ignoredDataIds.save(entry); + expect(await ignoredDataIds.getDataIdsWithReasons()).to.be.deep.equal({ + [entry.hash]: { + entry, + iteration: 2, + lastTryTimestamp: 10, + toRetry: true, + }, + }); + }); + it('can save() something already saved that cannot be retried', async () => { + const clock = sinon.useFakeTimers(); + await ignoredDataIds.save(entry2); + expect(await ignoredDataIds.getDataIdsWithReasons()).to.be.deep.equal({ + [entry2.hash]: { + entry: entry2, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + }); + + clock.tick(10); + + await ignoredDataIds.save(entry2); + expect(await ignoredDataIds.getDataIdsWithReasons()).to.be.deep.equal({ + [entry2.hash]: { + entry: entry2, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + }); + sinon.restore(); + }); + }); + + describe('getDataIdsWithReasons', () => { + it('can getDataIdsWithReasons()', async () => { + sinon.useFakeTimers(); + + await ignoredDataIds.save(entry); + await ignoredDataIds.save(entry2); + + expect(await ignoredDataIds.getDataIdsWithReasons()).to.be.deep.equal({ + [entry.hash]: { + entry, + iteration: 1, + lastTryTimestamp: 0, + toRetry: true, + }, + [entry2.hash]: { + entry: entry2, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + }); + sinon.restore(); + }); + it('can getDataIdsWithReasons() if empty', async () => { + expect(await ignoredDataIds.getDataIdsWithReasons()).to.be.deep.equal({}); + }); + }); + + describe('getDataIdsToRetry', () => { + it('can getDataIdsToRetry()', async () => { + const clock = sinon.useFakeTimers(); + await ignoredDataIds.save(entry); + expect(await ignoredDataIds.getDataIdsToRetry()).to.be.deep.equal([]); + + clock.tick(120001); + expect(await ignoredDataIds.getDataIdsToRetry()).to.be.deep.equal([entry]); + + sinon.restore(); + }); + }); + + describe('delete', () => { + it('can delete()', async () => { + await ignoredDataIds.save(entry); + expect(await ignoredDataIds.getReason(entry.hash)).to.be.equal(entry.error!.message); + expect(await ignoredDataIds.getDataIds()).to.be.deep.equal([entry.hash]); + + await ignoredDataIds.delete(entry.hash); + expect(await ignoredDataIds.getReason(entry.hash)).to.be.undefined; + expect(await ignoredDataIds.getDataIds()).to.be.deep.equal([]); + }); + }); + + describe('getDataIds', () => { + it('can getDataIds()', async () => { + await ignoredDataIds.save(entry); + await ignoredDataIds.save(entry2); + + expect(await ignoredDataIds.getDataIds()).to.be.deep.equal([entry.hash, entry2.hash]); + }); + it('can getDataIds() if empty', async () => { + expect(await ignoredDataIds.getDataIds()).to.be.deep.equal([]); + }); + }); +}); diff --git a/packages/ethereum-storage/test/lib/ethereum-blocks.test.ts b/packages/ethereum-storage/test/ethereum-blocks.test.ts similarity index 99% rename from packages/ethereum-storage/test/lib/ethereum-blocks.test.ts rename to packages/ethereum-storage/test/ethereum-blocks.test.ts index 713e3ca059..534636c294 100644 --- a/packages/ethereum-storage/test/lib/ethereum-blocks.test.ts +++ b/packages/ethereum-storage/test/ethereum-blocks.test.ts @@ -3,7 +3,7 @@ import 'mocha'; import * as chaiAsPromised from 'chai-as-promised'; import * as sinon from 'sinon'; -import EthereumBlocks from '../../src/lib/ethereum-blocks'; +import EthereumBlocks from '../src/ethereum-blocks'; const chai = require('chai'); const spies = require('chai-spies'); diff --git a/packages/ethereum-storage/test/ethereum-entries-to-ipfs-content.test.ts b/packages/ethereum-storage/test/ethereum-entries-to-ipfs-content.test.ts new file mode 100644 index 0000000000..bf876b887b --- /dev/null +++ b/packages/ethereum-storage/test/ethereum-entries-to-ipfs-content.test.ts @@ -0,0 +1,393 @@ +import 'mocha'; + +import * as sinon from 'sinon'; + +import { StorageTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +import ethereumEntriesToIpfsContent from '../src/ethereum-entries-to-ipfs-content'; +import IgnoredDataIndex from '../src/ignored-dataIds'; +import IpfsConnectionError from '../src/ipfs-connection-error'; + +// tslint:disable:no-magic-numbers + +// Extends chai for promises +chai.use(chaiAsPromised); +const expect = chai.expect; + +import spies = require('chai-spies'); +chai.use(spies); + +let ignoredDataIndex: IgnoredDataIndex; +let ipfsManager: any; + +// tslint:disable:no-unused-expression +describe('ethereum-entries-to-ipfs-content', () => { + beforeEach(async () => { + ignoredDataIndex = new IgnoredDataIndex(); + ipfsManager = {}; + }); + + it('can retry the right hashes', async () => { + sinon.useFakeTimers(); + + const connectionErrorSpy = chai.spy(() => { + throw new IpfsConnectionError(`Ipfs read request response error: test purpose`); + }); + const incorrectErrorSpy = chai.spy(() => { + throw new Error('Incorrect file test'); + }); + const biggerErrorSpy = chai.spy(() => ({ + content: 'bigger', + ipfsLinks: [], + ipfsSize: 5, + })); + const okSpy = chai.spy(() => ({ + content: 'ok', + ipfsLinks: [], + ipfsSize: 2, + })); + + ipfsManager.read = chai.spy( + async (hash: string): Promise => { + if (hash === 'hConnectionError') { + return connectionErrorSpy(); + } else if (hash === 'hIncorrectFile') { + return incorrectErrorSpy(); + } else if (hash === 'hBiggerFile') { + return biggerErrorSpy(); + } else { + return okSpy(); + } + }, + ); + + const ethereumEntriesToProcess: StorageTypes.IEthereumEntry[] = [ + { hash: 'hConnectionError', feesParameters: { contentSize: 3 }, meta: {} as any }, + { hash: 'hIncorrectFile', feesParameters: { contentSize: 3 }, meta: {} as any }, + { hash: 'hBiggerFile', feesParameters: { contentSize: 3 }, meta: {} as any }, + { hash: 'hOk', feesParameters: { contentSize: 3 }, meta: {} as any }, + ]; + const result = await ethereumEntriesToIpfsContent( + ethereumEntriesToProcess, + ipfsManager, + ignoredDataIndex, + new Utils.SimpleLogger(), + 5, + ); + + expect(result.length).to.equal(1); + expect(result[0]!.content).to.equal('ok'); + expect(result[0]!.id).to.equal('hOk'); + + const ignoredData = await ignoredDataIndex.getDataIdsWithReasons(); + + expect(ignoredData).to.deep.equal({ + hBiggerFile: { + entry: { + error: { + message: 'Incorrect declared size', + type: StorageTypes.ErrorEntries.WRONG_FEES, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hBiggerFile', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + hConnectionError: { + entry: { + error: { + message: 'Ipfs read request response error: test purpose', + type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hConnectionError', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: true, + }, + hIncorrectFile: { + entry: { + error: { + message: 'Incorrect file test', + type: StorageTypes.ErrorEntries.INCORRECT_FILE, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hIncorrectFile', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + }); + + expect(ipfsManager.read).to.have.been.called.exactly(5); + expect(connectionErrorSpy).to.have.been.called.twice; + expect(incorrectErrorSpy).to.have.been.called.once; + expect(biggerErrorSpy).to.have.been.called.once; + expect(okSpy).to.have.been.called.once; + + sinon.restore(); + }); + + it('can retry right hashes but find it after the retry', async () => { + sinon.useFakeTimers(); + + const connectionErrorSpy = chai.spy(() => { + throw new IpfsConnectionError(`Ipfs read request response error: test purpose`); + }); + const incorrectErrorSpy = chai.spy(() => { + throw new Error('Incorrect file test'); + }); + const biggerErrorSpy = chai.spy(() => ({ + content: 'bigger', + ipfsLinks: [], + ipfsSize: 5, + })); + const okSpy = chai.spy(() => ({ + content: 'ok', + ipfsLinks: [], + ipfsSize: 2, + })); + + let tryCount = 0; + ipfsManager.read = chai.spy( + async (hash: string): Promise => { + if (hash === 'hConnectionError' && tryCount === 0) { + tryCount++; + return connectionErrorSpy(); + } else if (hash === 'hIncorrectFile') { + return incorrectErrorSpy(); + } else if (hash === 'hBiggerFile') { + return biggerErrorSpy(); + } else { + return okSpy(); + } + }, + ); + + const ethereumEntriesToProcess: StorageTypes.IEthereumEntry[] = [ + { hash: 'hConnectionError', feesParameters: { contentSize: 3 }, meta: {} as any }, + { hash: 'hIncorrectFile', feesParameters: { contentSize: 3 }, meta: {} as any }, + { hash: 'hBiggerFile', feesParameters: { contentSize: 3 }, meta: {} as any }, + { hash: 'hOk', feesParameters: { contentSize: 3 }, meta: {} as any }, + ]; + const result = await ethereumEntriesToIpfsContent( + ethereumEntriesToProcess, + ipfsManager, + ignoredDataIndex, + new Utils.SimpleLogger(), + 5, + ); + + expect(result.length).to.equal(2); + expect(result[0]!.content).to.equal('ok'); + expect(result[0]!.id).to.equal('hOk'); + expect(result[1]!.content).to.equal('ok'); + expect(result[1]!.id).to.equal('hConnectionError'); + + const ignoredData = await ignoredDataIndex.getDataIdsWithReasons(); + + expect(ignoredData).to.deep.equal({ + hBiggerFile: { + entry: { + error: { + message: 'Incorrect declared size', + type: StorageTypes.ErrorEntries.WRONG_FEES, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hBiggerFile', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + hIncorrectFile: { + entry: { + error: { + message: 'Incorrect file test', + type: StorageTypes.ErrorEntries.INCORRECT_FILE, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hIncorrectFile', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: false, + }, + }); + + expect(ipfsManager.read).to.have.been.called.exactly(5); + expect(connectionErrorSpy).to.have.been.called.once; + expect(incorrectErrorSpy).to.have.been.called.once; + expect(biggerErrorSpy).to.have.been.called.once; + expect(okSpy).to.have.been.called.twice; + + sinon.restore(); + }); + + it('can store hash as ignored then remove it', async () => { + sinon.useFakeTimers(); + + ipfsManager.read = chai.spy(() => { + throw new IpfsConnectionError(`Ipfs read request response error: test purpose`); + }); + + const ethereumEntriesToProcess: StorageTypes.IEthereumEntry[] = [ + { hash: 'hConnectionError', feesParameters: { contentSize: 3 }, meta: {} as any }, + ]; + let result = await ethereumEntriesToIpfsContent( + ethereumEntriesToProcess, + ipfsManager, + ignoredDataIndex, + new Utils.SimpleLogger(), + 5, + ); + + expect(result.length).to.equal(0); + + let ignoredData = await ignoredDataIndex.getDataIdsWithReasons(); + + expect(ignoredData).to.deep.equal({ + hConnectionError: { + entry: { + error: { + message: 'Ipfs read request response error: test purpose', + type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hConnectionError', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: true, + }, + }); + + expect(ipfsManager.read).to.have.been.called.twice; + + // Then we find it: + ipfsManager.read = chai.spy( + async (_hash: string): Promise => ({ + content: 'ok', + ipfsLinks: [], + ipfsSize: 2, + }), + ); + result = await ethereumEntriesToIpfsContent( + ethereumEntriesToProcess, + ipfsManager, + ignoredDataIndex, + new Utils.SimpleLogger(), + 5, + ); + expect(result.length).to.equal(1); + expect(result[0]!.content).to.equal('ok'); + expect(result[0]!.id).to.equal('hConnectionError'); + + ignoredData = await ignoredDataIndex.getDataIdsWithReasons(); + + expect(ignoredData).to.deep.equal({}); + + sinon.restore(); + }); + + it('can store hash as ignored it twice', async () => { + const clock = sinon.useFakeTimers(); + + ipfsManager.read = chai.spy(() => { + throw new IpfsConnectionError(`Ipfs read request response error: test purpose`); + }); + + const ethereumEntriesToProcess: StorageTypes.IEthereumEntry[] = [ + { hash: 'hConnectionError', feesParameters: { contentSize: 3 }, meta: {} as any }, + ]; + let result = await ethereumEntriesToIpfsContent( + ethereumEntriesToProcess, + ipfsManager, + ignoredDataIndex, + new Utils.SimpleLogger(), + 5, + ); + expect(result.length).to.equal(0); + + let ignoredData = await ignoredDataIndex.getDataIdsWithReasons(); + + expect(ignoredData).to.deep.equal({ + hConnectionError: { + entry: { + error: { + message: 'Ipfs read request response error: test purpose', + type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hConnectionError', + meta: {}, + }, + iteration: 1, + lastTryTimestamp: 0, + toRetry: true, + }, + }); + + expect(ipfsManager.read).to.have.been.called.twice; + + clock.tick(100); + result = await ethereumEntriesToIpfsContent( + ethereumEntriesToProcess, + ipfsManager, + ignoredDataIndex, + new Utils.SimpleLogger(), + 5, + ); + expect(result.length).to.equal(0); + + ignoredData = await ignoredDataIndex.getDataIdsWithReasons(); + + expect(ignoredData).to.deep.equal({ + hConnectionError: { + entry: { + error: { + message: 'Ipfs read request response error: test purpose', + type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hConnectionError', + meta: {}, + }, + iteration: 2, + lastTryTimestamp: 100, + toRetry: true, + }, + }); + + sinon.restore(); + }); +}); diff --git a/packages/ethereum-storage/test/lib/ethereum-metadata-cache.ts b/packages/ethereum-storage/test/ethereum-metadata-cache.ts similarity index 97% rename from packages/ethereum-storage/test/lib/ethereum-metadata-cache.ts rename to packages/ethereum-storage/test/ethereum-metadata-cache.ts index 2fd51710f1..a71a1c1348 100644 --- a/packages/ethereum-storage/test/lib/ethereum-metadata-cache.ts +++ b/packages/ethereum-storage/test/ethereum-metadata-cache.ts @@ -4,8 +4,8 @@ import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import { StorageTypes } from '@requestnetwork/types'; -import EthereumMetadataCache from '../../src/lib/ethereum-metadata-cache'; -import SmartContractManager from '../../src/lib/smart-contract-manager'; +import EthereumMetadataCache from '../src/ethereum-metadata-cache'; +import SmartContractManager from '../src/smart-contract-manager'; const spies = require('chai-spies'); chai.use(spies); diff --git a/packages/ethereum-storage/test/lib/ethereum-storage.test.ts b/packages/ethereum-storage/test/ethereum-storage.test.ts similarity index 84% rename from packages/ethereum-storage/test/lib/ethereum-storage.test.ts rename to packages/ethereum-storage/test/ethereum-storage.test.ts index 5e8250f4f8..bfba2cbea0 100644 --- a/packages/ethereum-storage/test/lib/ethereum-storage.test.ts +++ b/packages/ethereum-storage/test/ethereum-storage.test.ts @@ -1,15 +1,16 @@ import 'mocha'; +import * as sinon from 'sinon'; + +import * as SmartContracts from '@requestnetwork/smart-contracts'; import { StorageTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; +import { EventEmitter } from 'events'; -import EthereumStorage from '../../src/lib/ethereum-storage'; -import IpfsConnectionError from '../../src/lib/ipfs-connection-error'; - -import * as artifactsRequestHashStorageUtils from '../../src/lib/artifacts-request-hash-storage-utils'; -import * as artifactsRequestHashSubmitterUtils from '../../src/lib/artifacts-request-hash-submitter-utils'; -import { IEthereumEntriesWithLastTimestamp } from '@requestnetwork/types/src/storage-types'; +import EthereumStorage from '../src/ethereum-storage'; +import IpfsConnectionError from '../src/ipfs-connection-error'; // tslint:disable:no-magic-numbers @@ -56,8 +57,8 @@ const web3Eth = require('web3-eth'); const eth = new web3Eth(provider); const contractHashSubmitter = new eth.Contract( - artifactsRequestHashSubmitterUtils.getContractAbi(), - artifactsRequestHashSubmitterUtils.getAddress('private'), + SmartContracts.requestHashSubmitterArtifact.getContractAbi(), + SmartContracts.requestHashSubmitterArtifact.getAddress('private'), ); const addressRequestHashSubmitter = contractHashSubmitter._address; @@ -77,7 +78,7 @@ const realSize2Bytes32Hex = web3Utils.padLeft(web3Utils.toHex(realSize2), 64); const fakeSize2 = 0; const fakeSize2Bytes32Hex = web3Utils.padLeft(web3Utils.toHex(fakeSize2), 64); -// Define a mock for getPastEvents to be independant of the state of ganache instance +// Define a mock for getPastEvents to be independent of the state of ganache instance const pastEventsMock = [ { blockNumber: 1, @@ -137,6 +138,7 @@ describe('EthereumStorage', () => { describe('initialize', () => { it('cannot use functions when not initialized', async () => { const ethereumStorageNotInitialized: EthereumStorage = new EthereumStorage( + 'localhost', ipfsGatewayConnection, web3Connection, ); @@ -153,6 +155,7 @@ describe('EthereumStorage', () => { it('cannot initialize if ipfs node not reachable', async () => { const ethereumStorageNotInitialized: EthereumStorage = new EthereumStorage( + 'localhost', invalidHostIpfsGatewayConnection, web3Connection, ); @@ -162,6 +165,7 @@ describe('EthereumStorage', () => { }); it('cannot initialize if ipfs node not in the right network', async () => { const ethereumStorageWithIpfsBootstrapNodesWrong: EthereumStorage = new EthereumStorage( + 'localhost', ipfsGatewayConnection, web3Connection, ); @@ -177,6 +181,7 @@ describe('EthereumStorage', () => { }); it('cannot initialize if ethereum node not reachable', async () => { const ethereumStorageNotInitialized: EthereumStorage = new EthereumStorage( + 'localhost', ipfsGatewayConnection, invalidHostWeb3Connection, ); @@ -187,6 +192,7 @@ describe('EthereumStorage', () => { it('cannot initialize if ethereum node not listening', async () => { const ethereumStorageNotInitialized: EthereumStorage = new EthereumStorage( + 'localhost', ipfsGatewayConnection, web3Connection, ); @@ -199,6 +205,7 @@ describe('EthereumStorage', () => { }); it('cannot initialize if contracts are not deployed', async () => { const ethereumStorageNotInitialized: EthereumStorage = new EthereumStorage( + 'localhost', ipfsGatewayConnection, web3Connection, ); @@ -208,11 +215,11 @@ describe('EthereumStorage', () => { // Initialize smart contract instance ethereumStorageNotInitialized.smartContractManager.requestHashStorage = new eth.Contract( - artifactsRequestHashStorageUtils.getContractAbi(), + SmartContracts.requestHashStorageArtifact.getContractAbi(), invalidHashStorageAddress, ); ethereumStorageNotInitialized.smartContractManager.requestHashSubmitter = new eth.Contract( - artifactsRequestHashSubmitterUtils.getContractAbi(), + SmartContracts.requestHashSubmitterArtifact.getContractAbi(), invalidHashSubmitterAddress, ); @@ -224,7 +231,7 @@ describe('EthereumStorage', () => { describe('append/read/getData', () => { beforeEach(async () => { - ethereumStorage = new EthereumStorage(ipfsGatewayConnection, web3Connection); + ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); await ethereumStorage.initialize(); ethereumStorage.smartContractManager.requestHashStorage.getPastEvents = getPastEventsMock; @@ -250,36 +257,25 @@ describe('EthereumStorage', () => { }); it('allows to append a file', async () => { + sinon.useFakeTimers(); + const timestamp = Utils.getCurrentTimestampInSecond(); const result = await ethereumStorage.append(content1); - if (!result.meta.ethereum) { - assert.fail('result.meta.ethereum does not exist'); - return; - } - - const resultExpected: StorageTypes.IEntry = { + const resultExpected: StorageTypes.IAppendResult = Object.assign(new EventEmitter(), { content: content1, id: hash1, meta: { - ethereum: { - blockConfirmation: 10, - blockNumber: 10, - blockTimestamp: 1545816416, - cost: '110', - fee: '100', - gasFee: '10', - networkName: 'private', - smartContractAddress: '0x345ca3e014aaf5dca488057592ee47305d9b3e10', - transactionHash: '0x7c45c575a54893dc8dc7230e3044e1de5c8714cd0a1374cf3a66378c639627a3', - }, ipfs: { size: realSize1, }, - storageType: StorageTypes.StorageSystemType.ETHEREUM_IPFS, - timestamp: 1545816416, + local: { location: 'localhost' }, + state: StorageTypes.ContentState.PENDING, + storageType: StorageTypes.StorageSystemType.LOCAL, + timestamp, }, - }; + }); assert.deepEqual(result, resultExpected); + sinon.restore(); }); it('cannot append if ipfs add fail', async () => { @@ -291,18 +287,23 @@ describe('EthereumStorage', () => { ); }); - it('throws when append and addHashAndSizeToEthereum throws', async () => { + it('throws when append and addHashAndSizeToEthereum throws', done => { ethereumStorage.smartContractManager.addHashAndSizeToEthereum = async (): Promise< StorageTypes.IEthereumMetadata > => { throw Error('fake error'); }; - try { - await ethereumStorage.append(content1); - assert.fail('result.meta.ethereum does not exist'); - } catch (e) { - assert.equal(e.message, 'Smart contract error: Error: fake error'); - } + + ethereumStorage.append(content1).then(result => { + result + .on('confirmed', () => { + assert.fail('addHashAndSizeToEthereum must have thrown'); + }) + .on('error', error => { + expect(error.message).to.equal('fake error'); + done(); + }); + }); }); it(`allows to save dataId's Ethereum metadata into the metadata cache when append is called`, async () => { await expect(ethereumStorage.ethereumMetadataCache.metadataCache.get(hash1)).to.eventually.be @@ -340,10 +341,14 @@ describe('EthereumStorage', () => { const result2 = await ethereumStorage.append(content1); - await assert.notDeepEqual(result1, result2); - await expect( - ethereumStorage.ethereumMetadataCache.metadataCache.get(hash1), - ).to.eventually.deep.equal(result1.meta.ethereum); + result1.on('confirmed', resultConfirmed1 => { + result2.on('confirmed', async resultConfirmed2 => { + await assert.notDeepEqual(resultConfirmed1, resultConfirmed2); + await expect( + ethereumStorage.ethereumMetadataCache.metadataCache.get(hash1), + ).to.eventually.deep.equal(resultConfirmed1.meta.ethereum); + }); + }); }); it('allows to read a file', async () => { @@ -390,7 +395,7 @@ describe('EthereumStorage', () => { throw Error('expected error'); }; await expect(ethereumStorage.read(content1)).to.eventually.rejectedWith( - `Ethereum meta read request error: Error: expected error`, + `No content found from this id`, ); }); @@ -529,7 +534,7 @@ describe('EthereumStorage', () => { // We want to force the retrieval of metadata with getPastEvents function ethereumStorage.ethereumMetadataCache.saveDataIdMeta = async (_dataId, _meta) => {}; ethereumStorage.smartContractManager.getEntriesFromEthereum = async (): Promise< - IEthereumEntriesWithLastTimestamp + StorageTypes.IEthereumEntriesWithLastTimestamp > => { return { ethereumEntries: [ @@ -610,7 +615,7 @@ describe('EthereumStorage', () => { // Test with no meta ethereumStorage.smartContractManager.getEntriesFromEthereum = (): Promise< - IEthereumEntriesWithLastTimestamp + StorageTypes.IEthereumEntriesWithLastTimestamp > => { return Promise.resolve({ ethereumEntries: [ @@ -685,7 +690,7 @@ describe('EthereumStorage', () => { it('allows to read hash on IPFS with retries', async () => { // Mock to test IPFS read retry ethereumStorage.smartContractManager.getEntriesFromEthereum = (): Promise< - IEthereumEntriesWithLastTimestamp + StorageTypes.IEthereumEntriesWithLastTimestamp > => { return Promise.resolve({ ethereumEntries: [ @@ -732,8 +737,6 @@ describe('EthereumStorage', () => { // Store how many time we tried to read a specific hash const hashTryCount: any = {}; - ethereumStorage.maxIpfsReadRetry = 4; - // This mock simulates ipfsManager.read() when we try to read the hash on IPFS differente times ethereumStorage.ipfsManager.read = async (hash: string) => { hashTryCount[hash] ? hashTryCount[hash]++ : (hashTryCount[hash] = 1); @@ -793,8 +796,8 @@ describe('EthereumStorage', () => { '0x2': 1, '0x3': 1, '0x4': 2, - '0x5': 3, - '0x6': 5, + '0x5': 2, + '0x6': 2, }); }); @@ -805,4 +808,91 @@ describe('EthereumStorage', () => { assert.isNumber(result.lastTimestamp); }); }); + + describe('getIgnoredData', () => { + it('cannot get ignored data if not initialized', async () => { + ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); + await expect(ethereumStorage.getIgnoredData()).to.eventually.rejectedWith( + 'Ethereum storage must be initialized', + ); + }); + it('can get ignored data', async () => { + ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); + await ethereumStorage.initialize(); + + ethereumStorage.ignoredDataIds.getDataIdsToRetry = async (): Promise< + StorageTypes.IEthereumEntry[] + > => [ + { + error: { + message: 'Ipfs read request response error: test purpose', + type: StorageTypes.ErrorEntries.IPFS_CONNECTION_ERROR, + }, + feesParameters: { + contentSize: 3, + }, + hash: 'hConnectionError', + meta: { blockTimestamp: 123 } as any, + }, + ]; + + ethereumStorage.ipfsManager.read = chai.spy( + async (_hash: string): Promise => ({ + content: 'ok', + ipfsLinks: [], + ipfsSize: 2, + }), + ); + + const entries = await ethereumStorage.getIgnoredData(); + expect(entries.length, 'config wrong').to.equal(1); + expect(entries[0], 'config wrong').to.deep.equal({ + content: 'ok', + id: 'hConnectionError', + meta: { + ethereum: { + blockTimestamp: 123, + }, + ipfs: { + size: 2, + }, + state: 'confirmed', + storageType: 'ethereumIpfs', + timestamp: 123, + }, + }); + }); + it('can get ignored data even if empty', async () => { + ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); + await ethereumStorage.initialize(); + + const entries = await ethereumStorage.getIgnoredData(); + expect(entries.length, 'config wrong').to.be.equal(0); + }); + }); + + describe('_getStatus()', () => { + it('can get status', async () => { + ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); + await ethereumStorage.initialize(); + await ethereumStorage.append(content1); + await ethereumStorage.getData(); + + const status = await ethereumStorage._getStatus(); + expect(status.dataIds.count, 'config wrong').to.gte(0); + expect(status.ignoredDataIds.count, 'config wrong').to.gte(0); + expect(status.ethereum, 'config wrong').to.deep.equal({ + creationBlockNumberHashStorage: 0, + currentProvider: 'http://localhost:8545', + hashStorageAddress: '0x345ca3e014aaf5dca488057592ee47305d9b3e10', + hashSubmitterAddress: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf', + maxConcurrency: 5, + maxRetries: undefined, + networkName: 'private', + retryDelay: undefined, + }); + // tslint:disable-next-line:no-unused-expression + expect(status.ipfs, 'config wrong').to.exist; + }); + }); }); diff --git a/packages/ethereum-storage/test/lib/ethereum-utils.test.ts b/packages/ethereum-storage/test/ethereum-utils.test.ts similarity index 93% rename from packages/ethereum-storage/test/lib/ethereum-utils.test.ts rename to packages/ethereum-storage/test/ethereum-utils.test.ts index 059542c6b6..bfd2b6080b 100644 --- a/packages/ethereum-storage/test/lib/ethereum-utils.test.ts +++ b/packages/ethereum-storage/test/ethereum-utils.test.ts @@ -2,8 +2,8 @@ import { expect } from 'chai'; import 'mocha'; import { StorageTypes } from '@requestnetwork/types'; -import { getSafeGasPriceLimit } from '../../src/lib/config'; -import EthereumUtils from '../../src/lib/ethereum-utils'; +import { getSafeGasPriceLimit } from '../src/config'; +import EthereumUtils from '../src/ethereum-utils'; const bigNumber: any = require('bn.js'); diff --git a/packages/ethereum-storage/test/lib/gas-price-definer.test.ts b/packages/ethereum-storage/test/gas-price-definer.test.ts similarity index 95% rename from packages/ethereum-storage/test/lib/gas-price-definer.test.ts rename to packages/ethereum-storage/test/gas-price-definer.test.ts index 0578105031..cdf853f05a 100644 --- a/packages/ethereum-storage/test/lib/gas-price-definer.test.ts +++ b/packages/ethereum-storage/test/gas-price-definer.test.ts @@ -1,8 +1,8 @@ import { StorageTypes } from '@requestnetwork/types'; -import EthereumUtils from '../../src/lib/ethereum-utils'; +import EthereumUtils from '../src/ethereum-utils'; -import * as config from '../../src/lib/config'; -import GasPriceDefiner from '../../src/lib/gas-price-definer'; +import * as config from '../src/config'; +import GasPriceDefiner from '../src/gas-price-definer'; import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; diff --git a/packages/ethereum-storage/test/lib/gas-price-providers/etherchain-provider.test.ts b/packages/ethereum-storage/test/gas-price-providers/etherchain-provider.test.ts similarity index 92% rename from packages/ethereum-storage/test/lib/gas-price-providers/etherchain-provider.test.ts rename to packages/ethereum-storage/test/gas-price-providers/etherchain-provider.test.ts index 83003b42af..8ea9df6647 100644 --- a/packages/ethereum-storage/test/lib/gas-price-providers/etherchain-provider.test.ts +++ b/packages/ethereum-storage/test/gas-price-providers/etherchain-provider.test.ts @@ -1,5 +1,5 @@ import { StorageTypes } from '@requestnetwork/types'; -import EtherchainProvider from '../../../src/lib/gas-price-providers/etherchain-provider'; +import EtherchainProvider from '../../src/gas-price-providers/etherchain-provider'; import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; @@ -53,21 +53,15 @@ describe('EtherchainProvider', () => { // Test with each gas price type await expect( etherchainProvider.getGasPrice(StorageTypes.GasPriceType.SAFELOW), - ).to.eventually.eql( - new bigNumber(1000000000), - ); + ).to.eventually.eql(new bigNumber(1000000000)); await expect( etherchainProvider.getGasPrice(StorageTypes.GasPriceType.STANDARD), - ).to.eventually.eql( - new bigNumber(3500000000), - ); + ).to.eventually.eql(new bigNumber(3500000000)); await expect( etherchainProvider.getGasPrice(StorageTypes.GasPriceType.FAST), - ).to.eventually.eql( - new bigNumber(7000000000), - ); + ).to.eventually.eql(new bigNumber(7000000000)); }); it('throws when API is not available', async () => { diff --git a/packages/ethereum-storage/test/lib/gas-price-providers/ethgasstation-provider.test.ts b/packages/ethereum-storage/test/gas-price-providers/ethgasstation-provider.test.ts similarity index 97% rename from packages/ethereum-storage/test/lib/gas-price-providers/ethgasstation-provider.test.ts rename to packages/ethereum-storage/test/gas-price-providers/ethgasstation-provider.test.ts index 6e37d8eada..e9452916fa 100644 --- a/packages/ethereum-storage/test/lib/gas-price-providers/ethgasstation-provider.test.ts +++ b/packages/ethereum-storage/test/gas-price-providers/ethgasstation-provider.test.ts @@ -1,5 +1,5 @@ import { StorageTypes } from '@requestnetwork/types'; -import EthGasStationProvider from '../../../src/lib/gas-price-providers/ethgasstation-provider'; +import EthGasStationProvider from '../../src/gas-price-providers/ethgasstation-provider'; import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; diff --git a/packages/ethereum-storage/test/lib/ipfs-manager.test.ts b/packages/ethereum-storage/test/ipfs-manager.test.ts similarity index 99% rename from packages/ethereum-storage/test/lib/ipfs-manager.test.ts rename to packages/ethereum-storage/test/ipfs-manager.test.ts index 10e8b149f2..a6140b17d9 100644 --- a/packages/ethereum-storage/test/lib/ipfs-manager.test.ts +++ b/packages/ethereum-storage/test/ipfs-manager.test.ts @@ -5,7 +5,7 @@ import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import { EventEmitter } from 'events'; import * as http from 'http'; -import IpfsManager from '../../src/lib/ipfs-manager'; +import IpfsManager from '../src/ipfs-manager'; const spies = require('chai-spies'); diff --git a/packages/ethereum-storage/test/lib/smartcontract-manager.test.ts b/packages/ethereum-storage/test/smartcontract-manager.test.ts similarity index 93% rename from packages/ethereum-storage/test/lib/smartcontract-manager.test.ts rename to packages/ethereum-storage/test/smartcontract-manager.test.ts index af2bb7ce14..6bb3cf7950 100644 --- a/packages/ethereum-storage/test/lib/smartcontract-manager.test.ts +++ b/packages/ethereum-storage/test/smartcontract-manager.test.ts @@ -1,19 +1,18 @@ import 'mocha'; +import * as SmartContracts from '@requestnetwork/smart-contracts'; import { StorageTypes } from '@requestnetwork/types'; import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; -import EthereumBlocks from '../../src/lib/ethereum-blocks'; -import SmartContractManager from '../../src/lib/smart-contract-manager'; - -import * as artifactsRequestHashStorageUtils from '../../src/lib/artifacts-request-hash-storage-utils'; -import * as artifactsRequestHashSubmitterUtils from '../../src/lib/artifacts-request-hash-submitter-utils'; +import EthereumBlocks from '../src/ethereum-blocks'; +import SmartContractManager from '../src/smart-contract-manager'; // tslint:disable:no-magic-numbers // Extends chai for promises chai.use(chaiAsPromised); const assert = chai.assert; +const expect = chai.expect; const web3HttpProvider = require('web3-providers-http'); @@ -63,13 +62,13 @@ const otherSize = 100000; const otherSizeBytes32Hex = web3Utils.padLeft(web3Utils.toHex(otherSize), 64); const contractHashStorage = new eth.Contract( - artifactsRequestHashStorageUtils.getContractAbi(), - artifactsRequestHashStorageUtils.getAddress('private'), + SmartContracts.requestHashStorageArtifact.getContractAbi(), + SmartContracts.requestHashStorageArtifact.getAddress('private'), ); const contractHashSubmitter = new eth.Contract( - artifactsRequestHashSubmitterUtils.getContractAbi(), - artifactsRequestHashSubmitterUtils.getAddress('private'), + SmartContracts.requestHashSubmitterArtifact.getContractAbi(), + SmartContracts.requestHashSubmitterArtifact.getAddress('private'), ); const addressRequestHashSubmitter = contractHashSubmitter._address; @@ -197,6 +196,19 @@ describe('SmartContractManager', () => { smartContractManager.ethereumBlocks.maxRetries = 0; }); + it('can get config', async () => { + expect(smartContractManager.getConfig(), 'config wrong').to.deep.equal({ + creationBlockNumberHashStorage: 0, + currentProvider: 'http://localhost:8545', + hashStorageAddress: '0x345ca3e014aaf5dca488057592ee47305d9b3e10', + hashSubmitterAddress: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf', + maxConcurrency: Number.MAX_SAFE_INTEGER, + maxRetries: undefined, + networkName: 'private', + retryDelay: undefined, + }); + }); + it('getMainAccount should return the main account', async () => { const accounts = await eth.getAccounts(); const mainAccount = await smartContractManager.getMainAccount(); @@ -406,7 +418,7 @@ describe('SmartContractManager', () => { it('getAddress in artifactsRequestHashStorageUtils with a invalid host network should throw an error', async () => { assert.throws( - () => artifactsRequestHashStorageUtils.getAddress('nonexistent'), + () => SmartContracts.requestHashStorageArtifact.getAddress('nonexistent'), Error, 'No deployment for network', ); @@ -414,14 +426,14 @@ describe('SmartContractManager', () => { it('getAddress in artifactsRequestHashSubmitterUtils with a invalid host network should throw an error', async () => { assert.throws( - () => artifactsRequestHashSubmitterUtils.getAddress('nonexistent'), + () => SmartContracts.requestHashSubmitterArtifact.getAddress('nonexistent'), Error, 'No deployment for network', ); }); it('getCreationBlockNumber in artifactsRequestHashSubmitterUtils', async () => { - assert.equal(artifactsRequestHashSubmitterUtils.getCreationBlockNumber('private'), 1); + assert.equal(SmartContracts.requestHashSubmitterArtifact.getCreationBlockNumber('private'), 1); }); it('allows to getMetaFromEthereum() a hash', async () => { @@ -442,12 +454,10 @@ describe('SmartContractManager', () => { }); it('allows to getMetaFromEthereum() a hash not indexed', async () => { - try { - await smartContractManager.getMetaFromEthereum('empty'); - assert.fail('must have exception'); - } catch (e) { - assert.equal(e.message, 'contentHash not indexed on ethereum'); - } + await expect( + smartContractManager.getMetaFromEthereum('empty'), + 'should throw', + ).to.eventually.be.rejectedWith('contentHash not indexed on ethereum'); }); it('badly formatted events from web3 should throw an error', async () => { @@ -603,13 +613,13 @@ describe('SmartContractManager', () => { smartContractManager.eth.net.isListening = () => new Promise( (resolve, _reject): void => { - setTimeout(() => resolve(true), 20000); + setTimeout(() => resolve(true), 300); }, ); // Timeout is lower to not reach the mocha test timeout await assert.isRejected( - smartContractManager.checkWeb3ProviderConnection(1000), + smartContractManager.checkWeb3ProviderConnection(100), Error, 'The Web3 provider is not reachable, did you use the correct protocol (http/https)?', ); diff --git a/packages/ethereum-storage/tsconfig.json b/packages/ethereum-storage/tsconfig.json index 3c7594cfc5..5c52df843a 100644 --- a/packages/ethereum-storage/tsconfig.json +++ b/packages/ethereum-storage/tsconfig.json @@ -5,12 +5,6 @@ "rootDir": ".", "resolveJsonModule": true }, - "include": ["./src/lib/**/*", "./artifacts/**/*"], - "references": [{ "path": "../types" }, { "path": "../utils" }], - "files": [ - "./artifacts/RequestHashStorage/0.1.0.json", - "./artifacts/RequestHashStorage/artifacts.json", - "./artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/0.1.0.json", - "./artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json" - ] + "include": ["./src/**/*"], + "references": [{ "path": "../types" }, { "path": "../utils" }, { "path": "../smart-contracts" }] } diff --git a/packages/ethereum-storage/tslint.json b/packages/ethereum-storage/tslint.json index 86908345b4..0946f20963 100644 --- a/packages/ethereum-storage/tslint.json +++ b/packages/ethereum-storage/tslint.json @@ -1,6 +1,3 @@ { - "extends": "../../tslint.json", - "linterOptions": { - "exclude": ["artifacts/**/*"] - } + "extends": "../../tslint.json" } diff --git a/packages/integration-test/.vscode/settings.json b/packages/integration-test/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/integration-test/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/integration-test/CHANGELOG.md b/packages/integration-test/CHANGELOG.md index 5f2f0e5bf4..feb7e8f023 100644 --- a/packages/integration-test/CHANGELOG.md +++ b/packages/integration-test/CHANGELOG.md @@ -3,6 +3,419 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.15.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.15.0) (2020-06-29) + + +### Features + +* add the identity ethereumSmartContract to the request logic ([#218](https://github.com/RequestNetwork/requestNetwork/issues/218)) ([66d97e0](https://github.com/RequestNetwork/requestNetwork/commit/66d97e00dee7305088cb94a0edf542fe4d0bbd56)) +* remove hash in encrypted transaction ([#232](https://github.com/RequestNetwork/requestNetwork/issues/232)) ([d58f101](https://github.com/RequestNetwork/requestNetwork/commit/d58f101f9f76e408671dd1edb0d67863d1c8abd5)) +* replace symmetric encryption algorithm by aes-256-gcm ([#233](https://github.com/RequestNetwork/requestNetwork/issues/233)) ([969bebe](https://github.com/RequestNetwork/requestNetwork/commit/969bebeb99b4bc2fdd31405a162934cfdff6db05)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* fix nightly test ([#145](https://github.com/RequestNetwork/requestNetwork/issues/145)) ([8377143](https://github.com/RequestNetwork/requestNetwork/commit/83771435234a2f7f00d3ac072911a6ec918007f4)) +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.14.0) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* fix nightly test ([#145](https://github.com/RequestNetwork/requestNetwork/issues/145)) ([8377143](https://github.com/RequestNetwork/requestNetwork/commit/83771435234a2f7f00d3ac072911a6ec918007f4)) +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.13.0) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* fix nightly test ([#145](https://github.com/RequestNetwork/requestNetwork/issues/145)) ([8377143](https://github.com/RequestNetwork/requestNetwork/commit/83771435234a2f7f00d3ac072911a6ec918007f4)) +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.12.0) (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* fix nightly test ([#145](https://github.com/RequestNetwork/requestNetwork/issues/145)) ([8377143](https://github.com/RequestNetwork/requestNetwork/commit/83771435234a2f7f00d3ac072911a6ec918007f4)) +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.11.0) (2020-03-23) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* fix nightly test ([#145](https://github.com/RequestNetwork/requestNetwork/issues/145)) ([8377143](https://github.com/RequestNetwork/requestNetwork/commit/83771435234a2f7f00d3ac072911a6ec918007f4)) +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.10.0) (2020-02-20) + + +### Bug Fixes + +* fix nightly test ([#145](https://github.com/RequestNetwork/requestNetwork/issues/145)) ([8377143](https://github.com/RequestNetwork/requestNetwork/commit/83771435234a2f7f00d3ac072911a6ec918007f4)) +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.9.0) (2020-01-16) + + +### Bug Fixes + +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.8.0) (2019-12-18) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + +# [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.6.0...@requestnetwork/integration-test@0.7.0) (2019-12-04) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) + + + + + # [0.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/integration-test@0.5.0...@requestnetwork/integration-test@0.6.0) (2019-11-20) diff --git a/packages/integration-test/package.json b/packages/integration-test/package.json index 885db8ebc2..691bef6566 100644 --- a/packages/integration-test/package.json +++ b/packages/integration-test/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/integration-test", - "version": "0.6.0", + "version": "0.15.0", "private": true, "description": "Integration tests for the request system.", "keywords": [ @@ -27,36 +27,40 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"test/**/*.ts\"", "lint-staged": "lint-staged", "test": "run-s test:node test:layers", - "test:scheduled": "run-s test:erc20", - "test:layers": "mocha --timeout=10000 --require ts-node/register \"test/layers.test.ts\"", - "test:node": "mocha --timeout=10000 --require ts-node/register \"test/node-client.test.ts\"", - "test:erc20": "mocha --timeout=10000 --require ts-node/register \"test/erc20.test.ts\"" + "test:scheduled": "run-s test:erc20 test:btc", + "test:layers": "mocha --timeout=10000 --extension ts --require ts-node/register \"test/layers.test.ts\"", + "test:node": "mocha --timeout=10000 --extension ts --require ts-node/register \"test/node-client.test.ts\"", + "test:erc20": "mocha --timeout=10000 --extension ts --require ts-node/register \"test/scheduled/erc20.test.ts\"", + "test:btc": "mocha --timeout=10000 --extension ts --require ts-node/register \"test/scheduled/btc.test.ts\"" }, "devDependencies": { - "@requestnetwork/advanced-logic": "0.6.0", - "@requestnetwork/data-access": "0.5.2", - "@requestnetwork/epk-decryption": "0.3.3", - "@requestnetwork/epk-signature": "0.5.4", - "@requestnetwork/ethereum-storage": "0.4.5", - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/request-client.js": "0.9.0", - "@requestnetwork/request-logic": "0.8.0", - "@requestnetwork/transaction-manager": "0.8.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/advanced-logic": "0.15.0", + "@requestnetwork/data-access": "0.11.0", + "@requestnetwork/epk-decryption": "0.3.12", + "@requestnetwork/epk-signature": "0.5.13", + "@requestnetwork/ethereum-storage": "0.10.0", + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/payment-detection": "0.18.0", + "@requestnetwork/request-client.js": "0.18.0", + "@requestnetwork/request-logic": "0.14.0", + "@requestnetwork/transaction-manager": "0.14.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "@truffle/hdwallet-provider": "1.0.18", "@types/chai": "4.1.7", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "chai": "4.2.0", + "chai-as-promised": "7.1.1", "lint-staged": "8.1.3", - "mocha": "5.2.0", + "mocha": "6.2.2", "npm-run-all": "4.1.5", - "prettier": "1.16.4", - "ts-node": "8.5.2", + "prettier": "1.19.1", + "sinon": "8.1.1", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2", "web3-eth": "1.0.0-beta.37" diff --git a/packages/integration-test/test/layers.test.ts b/packages/integration-test/test/layers.test.ts index 88fc376de5..cafadf77eb 100644 --- a/packages/integration-test/test/layers.test.ts +++ b/packages/integration-test/test/layers.test.ts @@ -30,6 +30,8 @@ let encryptionDataPayee: any; let payerSignatureInfo: SignatureTypes.ISignatureParameters; let payerIdentity: IdentityTypes.IIdentity; let encryptionDataPayer: any; +let decryptionProvider: any; +let signatureProvider: any; let dataAccess: DataAccessTypes.IDataAccess; @@ -47,7 +49,7 @@ describe('Request system', () => { networkId: StorageTypes.EthereumNetwork.PRIVATE, web3Provider: provider, }; - const ethereumStorage = new EthereumStorage(ipfsGatewayConnection, web3Connection); + const ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); // Data access setup dataAccess = new DataAccess(ethereumStorage); @@ -71,7 +73,7 @@ describe('Request system', () => { value: '0x5aeda56215b167893e80b4fe645ba6d5bab767de', }; - const signatureProvider = new EthereumPrivateKeySignatureProvider(payeeSignatureInfo); + signatureProvider = new EthereumPrivateKeySignatureProvider(payeeSignatureInfo); signatureProvider.addSignatureParameters(payerSignatureInfo); encryptionDataPayee = { @@ -112,7 +114,7 @@ describe('Request system', () => { }; // Decryption provider setup - const decryptionProvider = new EthereumPrivateKeyDecryptionProvider( + decryptionProvider = new EthereumPrivateKeyDecryptionProvider( encryptionDataPayee.decryptionParams, ); decryptionProvider.addDecryptionParameters(encryptionDataPayer.decryptionParams); @@ -153,10 +155,7 @@ describe('Request system', () => { payer, }; - const topics = [ - Utils.crypto.normalizeKeccak256Hash(payeeIdentity), - Utils.crypto.normalizeKeccak256Hash(payer), - ]; + const topics = [payeeIdentity, payer]; const resultCreation = await requestLogic.createRequest( requestCreationHash, @@ -175,6 +174,123 @@ describe('Request system', () => { assert.exists(request); }); + it('can create a request with smart contract as payer', async () => { + const contentDataExtensionData = advancedLogic.extensions.contentData.createCreationAction({ + content: { this: 'could', be: 'an', invoice: true }, + }); + + const payer = { + network: 'private', + type: IdentityTypes.TYPE.ETHEREUM_SMART_CONTRACT, + value: '0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6', + }; + + const requestCreationHash: RequestLogicTypes.ICreateParameters = { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: '100000000000', + extensionsData: [contentDataExtensionData], + payee: payeeIdentity, + payer, + }; + + const topics = [payeeIdentity, payer]; + + const resultCreation = await requestLogic.createRequest( + requestCreationHash, + payeeIdentity, + topics, + ); + + assert.exists(resultCreation); + + // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' + const requestIdLength = 66; + assert.equal(resultCreation.result.requestId.length, requestIdLength); + + const request = await requestLogic.getRequestFromId(resultCreation.result.requestId); + + assert.exists(request); + }); + + it('can create a request with cache', async () => { + const ipfsGatewayConnection: StorageTypes.IIpfsGatewayConnection = { + host: 'localhost', + port: 5001, + protocol: StorageTypes.IpfsGatewayProtocol.HTTP, + timeout: 10000, + }; + const web3Connection: StorageTypes.IWeb3Connection = { + networkId: StorageTypes.EthereumNetwork.PRIVATE, + web3Provider: provider, + }; + const ethereumStorage = new EthereumStorage('localhost', ipfsGatewayConnection, web3Connection); + + // Data access setup + dataAccess = new DataAccess(ethereumStorage); + await dataAccess.initialize(); + + // Transaction manager setup + const transactionManager = new TransactionManager(dataAccess, decryptionProvider); + + // Advanced Logic setup + advancedLogic = new AdvancedLogic(); + + // Logic setup + requestLogic = new RequestLogic(transactionManager, signatureProvider, advancedLogic); + + const contentDataExtensionData = advancedLogic.extensions.contentData.createCreationAction({ + content: { this: 'could', be: 'an', invoice: true }, + }); + + const payer = { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: '0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6', + }; + + const requestCreationHash: RequestLogicTypes.ICreateParameters = { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: '100000000000', + extensionsData: [contentDataExtensionData], + payee: payeeIdentity, + payer, + }; + + const topics = [payeeIdentity, payer]; + + const resultCreation = await requestLogic.createRequest( + requestCreationHash, + payeeIdentity, + topics, + ); + + assert.exists(resultCreation); + assert.equal( + resultCreation.meta.transactionManagerMeta.dataAccessMeta.storageMeta.storageType, + StorageTypes.StorageSystemType.LOCAL, + ); + + // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' + const requestIdLength = 66; + assert.equal(resultCreation.result.requestId.length, requestIdLength); + + // wait a bit + // tslint:disable-next-line:no-magic-numbers + await new Promise((r: any): any => setTimeout(r, 2000)); + + const request = await requestLogic.getRequestFromId(resultCreation.result.requestId); + assert.exists(request); + assert.equal( + request.meta.transactionManagerMeta.dataAccessMeta.storageMeta[0].storageType, + StorageTypes.StorageSystemType.ETHEREUM_IPFS, + ); + }); + it('can create a request BTC with payment network', async () => { const contentDataExtensionData = advancedLogic.extensions.contentData.createCreationAction({ content: { this: 'could', be: 'an', invoice: true }, @@ -194,6 +310,7 @@ describe('Request system', () => { const requestCreationHash: RequestLogicTypes.ICreateParameters = { currency: { + network: 'testnet', type: RequestLogicTypes.CURRENCY.BTC, value: 'BTC', }, @@ -203,10 +320,7 @@ describe('Request system', () => { payer, }; - const topics = [ - Utils.crypto.normalizeKeccak256Hash(payeeIdentity), - Utils.crypto.normalizeKeccak256Hash(payer), - ]; + const topics = [payeeIdentity, payer]; const resultCreation = await requestLogic.createRequest( requestCreationHash, @@ -232,7 +346,7 @@ describe('Request system', () => { type: RequestLogicTypes.CURRENCY.BTC, value: 'BTC', }, - expectedAmount: '100000000000', + expectedAmount: '200000000000', payee: payeeIdentity, payer: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, @@ -241,7 +355,7 @@ describe('Request system', () => { timestamp: Utils.getCurrentTimestampInSecond(), }; // create a unique topic just to not have collisions in tests - const topics1 = [Utils.crypto.normalizeKeccak256Hash(request1CreationHash)]; + const topics1 = [request1CreationHash]; const resultCreation1 = await requestLogic.createRequest( request1CreationHash, payeeIdentity, @@ -275,6 +389,11 @@ describe('Request system', () => { deltaAmount: '10000000000', requestId: requestId1, }; + + // wait a bit + // tslint:disable-next-line:no-magic-numbers + await new Promise((r: any): any => setTimeout(r, 1000)); + const resultReduce1 = await requestLogic.reduceExpectedAmountRequest( request1ReduceHash, payeeIdentity, @@ -282,6 +401,10 @@ describe('Request system', () => { const timestampReduce1 = resultReduce1.meta.transactionManagerMeta.dataAccessMeta.storageMeta.timestamp; + // wait a bit + // tslint:disable-next-line:no-magic-numbers + await new Promise((r: any): any => setTimeout(r, 1100)); + // cancel request const request1CancelHash: RequestLogicTypes.ICancelParameters = { requestId: requestId1, @@ -292,17 +415,17 @@ describe('Request system', () => { assert.equal(fromTopic.result.requests.length, 2); let request1 = fromTopic.result.requests[0]; const request2 = fromTopic.result.requests[1]; - assert.equal(request1.requestId, requestId1); - assert.equal(request2.requestId, requestId2); + assert.equal(request1.request!.requestId, requestId1); + assert.equal(request2.request!.requestId, requestId2); const fromTopicSecondSearch = await requestLogic.getRequestsByTopic(topics1[0], { from: timestampReduce1, }); assert.equal(fromTopicSecondSearch.result.requests.length, 1); request1 = fromTopicSecondSearch.result.requests[0]; - assert.equal(request1.requestId, requestId1); - assert.equal(request1.state, RequestLogicTypes.STATE.CANCELED); - assert.equal(request1.expectedAmount, '90000000000'); + assert.equal(request1.request!.requestId, requestId1); + assert.equal(request1.request!.state, RequestLogicTypes.STATE.CANCELED); + assert.equal(request1.request!.expectedAmount, '190000000000'); }); it('can create and update an encrypted request', async () => { @@ -321,10 +444,7 @@ describe('Request system', () => { payer: payerIdentity, }; - const topics = [ - Utils.crypto.normalizeKeccak256Hash(payeeIdentity), - Utils.crypto.normalizeKeccak256Hash(payerIdentity), - ]; + const topics = [payeeIdentity, payerIdentity]; const resultCreation = await requestLogic.createEncryptedRequest( requestCreationHash, @@ -342,10 +462,11 @@ describe('Request system', () => { const request = await requestLogic.getRequestFromId(resultCreation.result.requestId); assert.exists(request.result); - assert.equal(request.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); - assert.exists(request.result.request); - assert.equal(request.result.request!.expectedAmount, '12345678987654321'); - assert.equal(request.result.request!.state, 'created'); + assert.equal(request.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); + assert.isNull(request.result.request); + assert.exists(request.result.pending); + assert.equal(request.result.pending!.expectedAmount, '12345678987654321'); + assert.equal(request.result.pending!.state, RequestLogicTypes.STATE.CREATED); // reduce the expected amount by payee const resultReduce = await requestLogic.reduceExpectedAmountRequest( @@ -353,7 +474,7 @@ describe('Request system', () => { payeeIdentity, ); - assert.equal(resultReduce.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(resultReduce.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); assert.isUndefined(resultReduce.result); const requestAfterReduce = await requestLogic.getRequestFromId(resultCreation.result.requestId); @@ -361,19 +482,22 @@ describe('Request system', () => { assert.exists(requestAfterReduce.result); assert.equal( requestAfterReduce.meta.transactionManagerMeta.encryptionMethod, - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); assert.exists(requestAfterReduce.result.request); - assert.equal(requestAfterReduce.result.request!.expectedAmount, '12345678000000000'); + assert.equal(requestAfterReduce.result.request!.expectedAmount, '12345678987654321'); assert.equal(requestAfterReduce.result.request!.state, 'created'); + assert.exists(requestAfterReduce.result.pending); + assert.equal(requestAfterReduce.result.pending!.expectedAmount, '12345678000000000'); + // accept the request by payer const resultAccept = await requestLogic.acceptRequest( { requestId: resultCreation.result.requestId }, payerIdentity, ); - assert.equal(resultAccept.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(resultAccept.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); assert.isUndefined(resultAccept.result); const requestAfterAccept = await requestLogic.getRequestFromId(resultCreation.result.requestId); @@ -381,10 +505,13 @@ describe('Request system', () => { assert.exists(requestAfterAccept.result); assert.equal( requestAfterAccept.meta.transactionManagerMeta.encryptionMethod, - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); assert.exists(requestAfterAccept.result.request); - assert.equal(requestAfterAccept.result.request!.state, 'accepted'); + assert.equal(requestAfterAccept.result.request!.state, RequestLogicTypes.STATE.CREATED); + + assert.exists(requestAfterAccept.result.pending); + assert.equal(requestAfterAccept.result.pending!.state, RequestLogicTypes.STATE.ACCEPTED); // increase amount of the request by payer const resultIncrease = await requestLogic.increaseExpectedAmountRequest( @@ -392,7 +519,7 @@ describe('Request system', () => { payerIdentity, ); - assert.equal(resultIncrease.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(resultIncrease.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); assert.isUndefined(resultIncrease.result); const requestAfterIncrease = await requestLogic.getRequestFromId( @@ -402,10 +529,13 @@ describe('Request system', () => { assert.exists(requestAfterIncrease.result); assert.equal( requestAfterIncrease.meta.transactionManagerMeta.encryptionMethod, - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); assert.exists(requestAfterIncrease.result.request); - assert.equal(requestAfterIncrease.result.request!.expectedAmount, '12345678000000111'); + assert.equal(requestAfterIncrease.result.request!.expectedAmount, '12345678000000000'); + + assert.exists(requestAfterIncrease.result.pending); + assert.equal(requestAfterIncrease.result.pending!.expectedAmount, '12345678000000111'); // cancel the request by payee const resultCancel = await requestLogic.cancelRequest( @@ -413,7 +543,7 @@ describe('Request system', () => { payeeIdentity, ); - assert.equal(resultCancel.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(resultCancel.meta.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); assert.isUndefined(resultCancel.result); const requestAfterCancel = await requestLogic.getRequestFromId(resultCreation.result.requestId); @@ -421,10 +551,13 @@ describe('Request system', () => { assert.exists(requestAfterCancel.result); assert.equal( requestAfterCancel.meta.transactionManagerMeta.encryptionMethod, - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); assert.exists(requestAfterCancel.result.request); - assert.equal(requestAfterCancel.result.request!.state, 'canceled'); + assert.equal(requestAfterCancel.result.request!.state, RequestLogicTypes.STATE.ACCEPTED); + + assert.exists(requestAfterCancel.result.pending); + assert.equal(requestAfterCancel.result.pending!.state, RequestLogicTypes.STATE.CANCELED); // check that the data are encrypted: const dataAccessData = await dataAccess.getTransactionsByChannelId( @@ -435,7 +568,6 @@ describe('Request system', () => { assert.exists(dataAccessData.result.transactions[0].transaction.encryptedData); assert.exists(dataAccessData.result.transactions[0].transaction.encryptionMethod); - assert.exists(dataAccessData.result.transactions[0].transaction.hash); assert.exists(dataAccessData.result.transactions[0].transaction.keys); assert.exists( dataAccessData.result.transactions[0].transaction.keys![ @@ -450,19 +582,15 @@ describe('Request system', () => { assert.isUndefined(dataAccessData.result.transactions[0].transaction.data); assert.exists(dataAccessData.result.transactions[1].transaction.encryptedData); - assert.exists(dataAccessData.result.transactions[1].transaction.hash); assert.isUndefined(dataAccessData.result.transactions[1].transaction.data); assert.exists(dataAccessData.result.transactions[2].transaction.encryptedData); - assert.exists(dataAccessData.result.transactions[2].transaction.hash); assert.isUndefined(dataAccessData.result.transactions[2].transaction.data); assert.exists(dataAccessData.result.transactions[3].transaction.encryptedData); - assert.exists(dataAccessData.result.transactions[3].transaction.hash); assert.isUndefined(dataAccessData.result.transactions[3].transaction.data); assert.exists(dataAccessData.result.transactions[4].transaction.encryptedData); - assert.exists(dataAccessData.result.transactions[4].transaction.hash); assert.isUndefined(dataAccessData.result.transactions[4].transaction.data); }); }); diff --git a/packages/integration-test/test/node-client.test.ts b/packages/integration-test/test/node-client.test.ts index 44aedf42fa..60f244877b 100644 --- a/packages/integration-test/test/node-client.test.ts +++ b/packages/integration-test/test/node-client.test.ts @@ -2,17 +2,22 @@ import { EthereumPrivateKeyDecryptionProvider } from '@requestnetwork/epk-decryp import { EthereumPrivateKeySignatureProvider } from '@requestnetwork/epk-signature'; import MultiFormat from '@requestnetwork/multi-format'; import { Request, RequestNetwork, Types } from '@requestnetwork/request-client.js'; +import { IdentityTypes, PaymentTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; -import { assert } from 'chai'; -import 'mocha'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; -const payeeIdentity: Types.Identity.IIdentity = { - type: Types.Identity.TYPE.ETHEREUM_ADDRESS, +chai.use(chaiAsPromised); +const expect = chai.expect; +const assert = chai.assert; + +const payeeIdentity: IdentityTypes.IIdentity = { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x627306090abab3a6e1400e9345bc60c78a8bef57', }; -const payerIdentity: Types.Identity.IIdentity = { - type: Types.Identity.TYPE.ETHEREUM_ADDRESS, +const payerIdentity: IdentityTypes.IIdentity = { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0xf17f52151ebef6c7334fad080c5704d77216b732', }; @@ -80,24 +85,37 @@ describe('Request client using a request node', () => { // Get the data let requestData = request.getData(); assert.equal(requestData.expectedAmount, '1000'); - assert.equal(requestData.balance, null); + assert.equal(requestData.state, Types.RequestLogic.STATE.PENDING); + assert.isNull(requestData.balance); assert.exists(requestData.meta); + assert.equal(requestData.pending!.state, Types.RequestLogic.STATE.CREATED); + + requestData = await request.waitForConfirmation(); + assert.equal(requestData.state, Types.RequestLogic.STATE.CREATED); + assert.isNull(requestData.pending); // Reduce the amount and get the data - await request.reduceExpectedAmountRequest('200', payeeIdentity); - requestData = request.getData(); - assert.equal(requestData.expectedAmount, '800'); - assert.equal(requestData.balance, null); + requestData = await request.reduceExpectedAmountRequest('200', payeeIdentity); + assert.equal(requestData.expectedAmount, '1000'); + assert.equal(requestData.state, Types.RequestLogic.STATE.CREATED); + assert.isNull(requestData.balance); assert.exists(requestData.meta); + assert.equal(requestData.pending!.expectedAmount, '800'); + + requestData = await new Promise((resolve): any => requestData.on('confirmed', resolve)); + assert.equal(requestData.expectedAmount, '800'); + assert.isNull(requestData.pending); }); it('can create a request with declarative payment network and content data', async () => { const requestNetwork = new RequestNetwork({ signatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: { - paymentInstruction: 'Arbitrary payment instruction', + paymentInfo: { + paymentInstruction: 'Arbitrary payment instruction', + }, }, }; @@ -120,18 +138,26 @@ describe('Request client using a request node', () => { // Get the data let requestData = request.getData(); assert.equal(requestData.expectedAmount, '1000'); - assert.exists(requestData.balance); + assert.equal(requestData.state, Types.RequestLogic.STATE.PENDING); + assert.isNull(requestData.balance); + assert.exists(requestData.meta); + assert.equal(requestData.pending!.state, Types.RequestLogic.STATE.CREATED); - // @ts-ignore - assert.equal(requestData.balance.balance, '0'); + const extension = requestData.extensions[PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE]; + assert.exists(extension); + assert.equal(extension.events[0].name, 'create'); + assert.deepEqual(extension.events[0].parameters, paymentNetwork.parameters); - assert.exists(requestData.meta); + requestData = await request.waitForConfirmation(); + assert.equal(requestData.state, Types.RequestLogic.STATE.CREATED); + assert.isNull(requestData.pending); requestData = await request.declareSentPayment('100', 'bank transfer initiated', payerIdentity); assert.exists(requestData.balance); + assert.equal(requestData.balance!.balance, '0'); - // @ts-ignore - assert.equal(requestData.balance.balance, '0'); + requestData = await new Promise((resolve): any => requestData.on('confirmed', resolve)); + assert.equal(requestData.balance!.balance, '0'); requestData = await request.declareReceivedPayment( '100', @@ -139,9 +165,10 @@ describe('Request client using a request node', () => { payeeIdentity, ); assert.exists(requestData.balance); + assert.equal(requestData.balance!.balance, '0'); - // @ts-ignore - assert.equal(requestData.balance.balance, '100'); + requestData = await new Promise((resolve): any => requestData.on('confirmed', resolve)); + assert.equal(requestData.balance!.balance, '100'); }); it('can create requests and get them fromIdentity and with time boundaries', async () => { @@ -171,6 +198,7 @@ describe('Request client using a request node', () => { expectedAmount: '1000', payee: payeeIdentity, payer: payerIdentity, + timestamp: Utils.getCurrentTimestampInSecond(), }; const request2: Request = await requestNetwork.createRequest({ @@ -179,9 +207,13 @@ describe('Request client using a request node', () => { topics: topicsRequest1and2, }); - // reduce request 1 + // wait 1,5 sec and store the timestamp + // tslint:disable:no-magic-numbers + // tslint:disable-next-line:typedef + await new Promise(r => setTimeout(r, 1500)); const timestampBeforeReduce = Utils.getCurrentTimestampInSecond(); + // reduce request 1 await request1.reduceExpectedAmountRequest('10000000', payeeIdentity); // cancel request 1 @@ -212,13 +244,70 @@ describe('Request client using a request node', () => { assert.equal(requestData1.expectedAmount, '90000000'); }); + it('can create requests and get them fromIdentity with smart contract identity', async () => { + const requestNetwork = new RequestNetwork({ signatureProvider }); + + const payerSmartContract = { + network: 'private', + type: IdentityTypes.TYPE.ETHEREUM_SMART_CONTRACT, + value: '0xf17f52151ebef6c7334fad080c5704d77216b732', + }; + + const timestampCreation = Utils.getCurrentTimestampInSecond(); + + // create request 1 + const topicsRequest1and2: string[] = [ + MultiFormat.serialize(Utils.crypto.normalizeKeccak256Hash(timestampCreation)), + ]; + const request1: Request = await requestNetwork.createRequest({ + requestInfo: { + currency: 'BTC', + expectedAmount: '100000000', + payee: payeeIdentity, + payer: payerSmartContract, + timestamp: Utils.getCurrentTimestampInSecond(), + }, + signer: payeeIdentity, + topics: topicsRequest1and2, + }); + + // create request 2 to be sure it is not found when search with smart contract identity + await requestNetwork.createRequest({ + requestInfo: { + currency: 'BTC', + expectedAmount: '1000', + payee: payeeIdentity, + payer: payerIdentity, + timestamp: Utils.getCurrentTimestampInSecond(), + }, + signer: payeeIdentity, + topics: topicsRequest1and2, + }); + + // wait 1,5 sec and store the timestamp + // tslint:disable:no-magic-numbers + // tslint:disable-next-line:typedef + await new Promise(r => setTimeout(r, 1500)); + + // get requests with boundaries + const requests = await requestNetwork.fromIdentity(payerSmartContract, { + from: timestampCreation, + }); + assert.equal(requests.length, 1); + assert.equal(requests[0].requestId, request1.requestId); + }); + it('can create an encrypted request and get it back unencrypted', async () => { const requestNetwork = new RequestNetwork({ signatureProvider, decryptionProvider }); + const timestamp = Date.now(); // Create an encrypted request const request = await requestNetwork._createEncryptedRequest( { - requestInfo: requestCreationHashBTC, + requestInfo: { + ...requestCreationHashBTC, + ...{ timestamp }, + }, signer: payeeIdentity, }, [encryptionData.encryptionParams], @@ -230,32 +319,38 @@ describe('Request client using a request node', () => { // Get the data const requestData = request.getData(); + assert.exists(requestData); assert.equal(requestData.expectedAmount, '1000'); - assert.equal(requestData.balance, null); + assert.equal(requestData.state, Types.RequestLogic.STATE.PENDING); + assert.isNull(requestData.balance); assert.exists(requestData.meta); - assert.equal(requestData.meta!.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(requestData.pending!.state, Types.RequestLogic.STATE.CREATED); + assert.equal(requestData.meta!.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); // Fetch the created request by its id const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); // Verify that the request values are correct assert.instanceOf(fetchedRequest, Request); - assert.deepEqual(request, fetchedRequest); const fetchedRequestData = fetchedRequest.getData(); assert.equal(requestData.expectedAmount, fetchedRequestData.expectedAmount); - assert.equal(requestData.balance, null); + assert.isNull(requestData.balance); assert.exists(requestData.meta); - assert.equal(requestData.meta!.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(requestData.meta!.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); }); it('can create an encrypted request, modify it and get it back unencrypted', async () => { const requestNetwork = new RequestNetwork({ signatureProvider, decryptionProvider }); + const timestamp = Date.now(); // Create an encrypted request const request = await requestNetwork._createEncryptedRequest( { - requestInfo: requestCreationHashBTC, + requestInfo: { + ...requestCreationHashBTC, + ...{ timestamp }, + }, signer: payeeIdentity, }, [encryptionData.encryptionParams], @@ -268,9 +363,13 @@ describe('Request client using a request node', () => { // Get the data const requestData = request.getData(); assert.equal(requestData.expectedAmount, '1000'); - assert.equal(requestData.balance, null); + assert.equal(requestData.state, Types.RequestLogic.STATE.PENDING); + assert.isNull(requestData.balance); assert.exists(requestData.meta); - assert.equal(requestData.meta!.transactionManagerMeta.encryptionMethod, 'ecies-aes256-cbc'); + assert.equal(requestData.pending!.state, Types.RequestLogic.STATE.CREATED); + assert.equal(requestData.meta!.transactionManagerMeta.encryptionMethod, 'ecies-aes256-gcm'); + + await new Promise((resolve): any => request.on('confirmed', resolve)); // Fetch the created request by its id const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); @@ -286,7 +385,7 @@ describe('Request client using a request node', () => { assert.exists(fetchedRequestData.meta); assert.equal( fetchedRequestData.meta!.transactionManagerMeta.encryptionMethod, - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); assert.equal(fetchedRequestData.state, Types.RequestLogic.STATE.CREATED); @@ -315,76 +414,87 @@ describe('Request client using a request node', () => { await fetchedRequest.refresh(); assert.equal(fetchedRequest.getData().expectedAmount, '0'); }); -}); -it('create an encrypted and unencrypted request with the same content', async () => { - const requestNetwork = new RequestNetwork({ signatureProvider, decryptionProvider }); + it('create an encrypted and unencrypted request with the same content', async () => { + const requestNetwork = new RequestNetwork({ signatureProvider, decryptionProvider }); - // Create an encrypted request - const encryptedRequest = await requestNetwork._createEncryptedRequest( - { - requestInfo: requestCreationHashBTC, - signer: payeeIdentity, - }, - [encryptionData.encryptionParams], - ); + const timestamp = Date.now(); - // Create a plain request - const plainRequest = await requestNetwork.createRequest({ - requestInfo: requestCreationHashBTC, - signer: payeeIdentity, - }); + // Create an encrypted request + const encryptedRequest = await requestNetwork._createEncryptedRequest( + { + requestInfo: { + ...requestCreationHashBTC, + ...{ timestamp }, + }, + signer: payeeIdentity, + }, + [encryptionData.encryptionParams], + ); - assert.notEqual(encryptedRequest.requestId, plainRequest.requestId); + // Create a plain request + const plainRequest = await requestNetwork.createRequest({ + requestInfo: { + ...requestCreationHashBTC, + ...{ timestamp }, + }, + signer: payeeIdentity, + }); + assert.equal(encryptedRequest.requestId, plainRequest.requestId); - const encryptedRequestData = encryptedRequest.getData(); - const plainRequestData = plainRequest.getData(); + const encryptedRequestData = encryptedRequest.getData(); + const plainRequestData = plainRequest.getData(); - assert.notDeepEqual(encryptedRequestData, plainRequestData); + assert.notDeepEqual(encryptedRequestData, plainRequestData); - assert.equal( - encryptedRequestData.meta!.transactionManagerMeta.encryptionMethod, - 'ecies-aes256-cbc', - ); + assert.equal( + plainRequestData.meta!.transactionManagerMeta!.encryptionMethod, + 'ecies-aes256-gcm', + ); + assert.isNull(plainRequestData.meta!.transactionManagerMeta.ignoredTransactions![0]); + assert.equal( + plainRequestData.meta!.transactionManagerMeta.ignoredTransactions![1].reason, + 'Clear transactions are not allowed in encrypted channel', + ); + }); - assert.notExists(plainRequestData.meta!.transactionManagerMeta.encryptionMethod); -}); + it('cannot decrypt a request with the wrong decryption provider', async () => { + const timestamp = Date.now(); + const myRandomTopic = `topic ${Utils.getCurrentTimestampInSecond()}`; + const requestNetwork = new RequestNetwork({ + decryptionProvider, + signatureProvider, + }); -it('cannot decrypt a request with the wrong decryption provider', async () => { - const requestNetwork = new RequestNetwork({ - decryptionProvider, - signatureProvider, - }); + const badRequestNetwork = new RequestNetwork({ + decryptionProvider: wrongDecryptionProvider, + signatureProvider, + }); - const badRequestNetwork = new RequestNetwork({ - decryptionProvider: wrongDecryptionProvider, - signatureProvider, - }); + const request = await requestNetwork._createEncryptedRequest( + { + requestInfo: { + ...requestCreationHashBTC, + ...{ timestamp }, + }, + signer: payeeIdentity, + topics: [myRandomTopic], + }, + [encryptionData.encryptionParams], + ); - const request = await requestNetwork._createEncryptedRequest( - { - requestInfo: requestCreationHashBTC, - signer: payeeIdentity, - topics: ['my nice topic'], - }, - [encryptionData.encryptionParams], - ); - - let error = ''; - try { - await badRequestNetwork.fromRequestId(request.requestId); - } catch (e) { - error = e.message; - } - assert.include(error, 'No request found for the id:'); - - const requests = await badRequestNetwork.fromTopic('my nice topic'); - assert.isEmpty(requests); + // console.log(JSON.stringify(await badRequestNetwork.fromRequestId(request.requestId))); + expect(badRequestNetwork.fromRequestId(request.requestId)).to.eventually.rejectedWith( + 'Invalid transaction(s) found: [', + ); + const requests = await badRequestNetwork.fromTopic(myRandomTopic); + assert.isEmpty(requests); + }); }); describe('ERC20 localhost request creation and detection test', () => { - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED, parameters: { paymentAddress: '0xf17f52151EbEF6C7334FAD080c5704D77216b732', }, @@ -399,8 +509,8 @@ describe('ERC20 localhost request creation and detection test', () => { value: contractAddress, }, expectedAmount: '10', - payee: payerIdentity, - payer: payeeIdentity, + payee: payeeIdentity, + payer: payerIdentity, }; it('can create an ERC20 request on localhost and detect the payment using address based detection', async () => { @@ -417,10 +527,15 @@ describe('ERC20 localhost request creation and detection test', () => { assert.exists(request.requestId); // Get the data - const requestData = request.getData(); + let requestData = request.getData(); assert.equal(requestData.expectedAmount, '10'); - assert.notEqual(requestData.balance, null); - assert.equal(requestData.balance!.balance, '10'); + assert.equal(requestData.state, Types.RequestLogic.STATE.PENDING); + assert.isNull(requestData.balance); assert.exists(requestData.meta); + assert.equal(requestData.pending!.state, Types.RequestLogic.STATE.CREATED); + + requestData = await new Promise((resolve): any => request.on('confirmed', resolve)); + assert.equal(requestData.state, Types.RequestLogic.STATE.CREATED); + assert.isNull(requestData.pending); }); }); diff --git a/packages/integration-test/test/scheduled/btc-test-data.ts b/packages/integration-test/test/scheduled/btc-test-data.ts new file mode 100644 index 0000000000..20470ddee7 --- /dev/null +++ b/packages/integration-test/test/scheduled/btc-test-data.ts @@ -0,0 +1,48 @@ +import { IdentityTypes, RequestLogicTypes, SignatureTypes } from '@requestnetwork/types'; + +const arbitraryTimestamp = 1549953337; + +export const payee = { + identity: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: '0x627306090abab3a6e1400e9345bc60c78a8bef57', + }, + signatureParams: { + method: SignatureTypes.METHOD.ECDSA, + privateKey: '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3', + }, +}; + +export const payer = { + identity: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: '0xf17f52151ebef6c7334fad080c5704d77216b732', + }, + signatureParams: { + method: SignatureTypes.METHOD.ECDSA, + privateKey: '0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f', + }, +}; + +export const testnetRequestData = { + currency: { + network: 'testnet', + type: RequestLogicTypes.CURRENCY.BTC, + value: 'BTC', + }, + expectedAmount: '100000000000', + payee: payee.identity, + payer: payer.identity, + timestamp: arbitraryTimestamp, +}; + +export const requestData = { + currency: { + type: RequestLogicTypes.CURRENCY.BTC, + value: 'BTC', + }, + expectedAmount: '100000000000', + payee: payee.identity, + payer: payer.identity, + timestamp: arbitraryTimestamp, +}; diff --git a/packages/integration-test/test/scheduled/btc.test.ts b/packages/integration-test/test/scheduled/btc.test.ts new file mode 100644 index 0000000000..3709a00b74 --- /dev/null +++ b/packages/integration-test/test/scheduled/btc.test.ts @@ -0,0 +1,61 @@ +/* eslint-disable spellcheck/spell-checker */ +import { EthereumPrivateKeySignatureProvider } from '@requestnetwork/epk-signature'; +import { RequestNetwork } from '@requestnetwork/request-client.js'; +import { PaymentTypes, SignatureTypes } from '@requestnetwork/types'; +import * as chai from 'chai'; +import { payee, requestData, testnetRequestData } from './btc-test-data'; + +const expect = chai.expect; + +const signatureProvider = new EthereumPrivateKeySignatureProvider({ + method: SignatureTypes.METHOD.ECDSA, + privateKey: '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3', +}); +signatureProvider.addSignatureParameters({ + method: SignatureTypes.METHOD.ECDSA, + privateKey: '0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f', +}); +describe('BTC detection test-suite', () => { + // TODO - PROT-1162: unskip with a long term solution (only failing for testnet) + it.skip('Can create a BTC testnet payment provider request and detect the payment', async () => { + const requestNetwork = new RequestNetwork({ signatureProvider }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + parameters: { + paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', + }, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: testnetRequestData, + signer: payee.identity, + }); + + // tslint:disable-next-line:no-magic-numbers + await new Promise((resolve: any): any => setTimeout(resolve, 1500)); + expect((await request.refresh()).balance?.balance).to.be.equal('50500000'); + }); + + it('Can create a BTC mainnet payment provider request and detect the payment', async () => { + const requestNetwork = new RequestNetwork({ signatureProvider }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, + parameters: { + paymentAddress: '1FersucwSqufU26w9GrGz9M3KcwuNmy6a9', + }, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: requestData, + signer: payee.identity, + }); + + // tslint:disable-next-line:no-magic-numbers + await new Promise((resolve: any): any => setTimeout(resolve, 1500)); + expect((await request.refresh()).balance?.balance).to.be.equal('666743'); + }); +}); diff --git a/packages/integration-test/test/erc20-mainnet-test-data.ts b/packages/integration-test/test/scheduled/erc20-mainnet-test-data.ts similarity index 100% rename from packages/integration-test/test/erc20-mainnet-test-data.ts rename to packages/integration-test/test/scheduled/erc20-mainnet-test-data.ts diff --git a/packages/integration-test/test/erc20.test.ts b/packages/integration-test/test/scheduled/erc20.test.ts similarity index 56% rename from packages/integration-test/test/erc20.test.ts rename to packages/integration-test/test/scheduled/erc20.test.ts index a1757c33cf..e682cb2e56 100644 --- a/packages/integration-test/test/erc20.test.ts +++ b/packages/integration-test/test/scheduled/erc20.test.ts @@ -1,8 +1,14 @@ // tslint:disable: no-magic-numbers // tslint:disable: no-invalid-this -import ERC20AddressedBased from '@requestnetwork/request-client.js/src/api/payment-network/erc20/address-based'; -import ERC20InfoRetriever from '@requestnetwork/request-client.js/src/api/payment-network/erc20/info-retriever'; -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { Erc20PaymentNetwork } from '@requestnetwork/payment-detection'; +import ERC20AddressBasedInfoRetriever from '@requestnetwork/payment-detection/src/erc20/address-based-info-retriever'; + +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; import { account, tokens } from './erc20-mainnet-test-data'; import { expect } from 'chai'; @@ -27,29 +33,36 @@ const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { }, }; -describe('ERC20 detection test-suite', function(): void { - this.timeout(10000); - +describe('ERC20 detection test-suite', () => { describe('check mainnet payment detection', () => { - Object.entries(tokens).forEach(([symbol, { address, amount, decimals }]) => { + Object.entries(tokens).forEach(([symbol, { address, amount }]) => { it(`can detect the balance of ${symbol}`, async () => { - const balanceObject = await ERC20InfoRetriever(address, account, 'mainnet'); + const infoRetriever = new ERC20AddressBasedInfoRetriever( + address, + account, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'mainnet', + ); + const events = await infoRetriever.getTransferEvents(); - expect(balanceObject.decimals).to.be.equal(decimals); // if this assert fails it means this address received another transaction - expect(balanceObject.tokenEvents).to.have.lengthOf(1); - const event = balanceObject.tokenEvents[0]; - delete event.from; - expect(event).to.deep.equal({ - to: account, - value: amount, - }); + expect(events).to.have.lengthOf(1); + const event = events[0]; + expect(event.name).to.equal('payment'); + expect(event.amount).to.equal(amount); + expect(event.timestamp).to.be.a('number'); + expect(event.parameters!.to).to.equal(account); + expect(event.parameters!.from).to.be.a('string'); + expect(event.parameters!.block).to.be.a('number'); + expect(event.parameters!.txHash).to.be.a('string'); }); }); }); it('can getBalance on a mainnet request', async () => { - const erc20AddressedBased = new ERC20AddressedBased({ advancedLogic: mockAdvancedLogic }); + const erc20AddressedBased = new Erc20PaymentNetwork.AddressBased({ + advancedLogic: mockAdvancedLogic, + }); const mockRequest = { creator: { type: '', value: '0x2' }, currency: { @@ -82,12 +95,13 @@ describe('ERC20 detection test-suite', function(): void { expect(balance.balance).to.be.equal('510000000000000000'); expect(balance.events).to.have.lengthOf(1); expect(balance.events[0].name).to.be.equal('payment'); - expect(balance.events[0].parameters.to).to.be.equal( + expect(balance.events[0].parameters!.to).to.be.equal( '0x6A08D2C8f251AF1f17B5943f7f7Bb7078c50e29A', ); - expect(balance.events[0].parameters.from).to.be.equal( + expect(balance.events[0].parameters!.from).to.be.equal( '0x708416775B69E3D3d6c634FfdF91778A161d30Bd', ); - expect(balance.events[0].parameters.value).to.be.equal('510000000000000000'); + expect(balance.events[0].amount).to.be.equal('510000000000000000'); + expect(balance.events[0].timestamp).to.be.a('number'); }); }); diff --git a/packages/multi-format/.vscode/settings.json b/packages/multi-format/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/multi-format/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/multi-format/CHANGELOG.md b/packages/multi-format/CHANGELOG.md index 2892f9facb..9b72153139 100644 --- a/packages/multi-format/CHANGELOG.md +++ b/packages/multi-format/CHANGELOG.md @@ -3,6 +3,197 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.3.0) (2020-06-29) + + +### Features + +* replace symmetric encryption algorithm by aes-256-gcm ([#233](https://github.com/RequestNetwork/requestNetwork/issues/233)) ([969bebe](https://github.com/RequestNetwork/requestNetwork/commit/969bebeb99b4bc2fdd31405a162934cfdff6db05)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.2.9](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.9) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.2.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.8) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.2.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.7) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.2.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.6) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.2.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.5) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.2.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.4) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/multi-format + + + + + +## [0.2.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.3) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/multi-format + + + + + +## [0.2.2](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.1...@requestnetwork/multi-format@0.2.2) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/multi-format + + + + + ## [0.2.1](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/multi-format@0.2.0...@requestnetwork/multi-format@0.2.1) (2019-11-20) **Note:** Version bump only for package @requestnetwork/multi-format diff --git a/packages/multi-format/package.json b/packages/multi-format/package.json index 58fed4eae4..7c4591662c 100644 --- a/packages/multi-format/package.json +++ b/packages/multi-format/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/multi-format", - "version": "0.2.1", + "version": "0.3.0", "publishConfig": { "access": "public" }, @@ -32,33 +32,35 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "prepare": "yarn run build", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/types": "0.9.0" + "@requestnetwork/types": "0.17.0" }, "devDependencies": { "@types/chai": "4.1.7", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", + "@types/sinon": "7.5.0", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", - "sinon": "7.3.2", + "sinon": "7.5.0", "source-map-support": "0.5.13", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" } diff --git a/packages/multi-format/src/encryption/aes256-gcm-format.ts b/packages/multi-format/src/encryption/aes256-gcm-format.ts new file mode 100644 index 0000000000..939ceb5e82 --- /dev/null +++ b/packages/multi-format/src/encryption/aes256-gcm-format.ts @@ -0,0 +1,12 @@ +import { EncryptionTypes, MultiFormatTypes } from '@requestnetwork/types'; + +import SerializableMultiFormat from '../serializable-multi-format'; + +/** + * Class to serialize and deserialize multi-format AES-256-GCM encrypted data + */ +export default class Aes256GcmMultiFormat extends SerializableMultiFormat { + constructor() { + super(MultiFormatTypes.prefix.AES256_GCM_ENCRYPTED, EncryptionTypes.METHOD.AES256_GCM); + } +} diff --git a/packages/multi-format/src/encryption/encryption-format.ts b/packages/multi-format/src/encryption/encryption-format.ts index cf7d1545ff..92e92b160e 100644 --- a/packages/multi-format/src/encryption/encryption-format.ts +++ b/packages/multi-format/src/encryption/encryption-format.ts @@ -1,7 +1,8 @@ import GroupMultiFormat from '../group-multi-format'; import Aes256Cbc from './aes256-cbc-format'; +import Aes256Gcm from './aes256-gcm-format'; import Ecies from './ecies-format'; // group all the multi-format concerning the encryption -const group = new GroupMultiFormat([new Aes256Cbc(), new Ecies()]); +const group = new GroupMultiFormat([new Aes256Cbc(), new Ecies(), new Aes256Gcm()]); export default group; diff --git a/packages/payment-detection/.lintstagedrc.json b/packages/payment-detection/.lintstagedrc.json new file mode 100644 index 0000000000..09124f6125 --- /dev/null +++ b/packages/payment-detection/.lintstagedrc.json @@ -0,0 +1,3 @@ +{ + "src/**/*.ts": ["tslint --project . --fix", "prettier --single-quote --write", "git add"] +} diff --git a/packages/payment-detection/.nycrc b/packages/payment-detection/.nycrc new file mode 100644 index 0000000000..8f593ebeb3 --- /dev/null +++ b/packages/payment-detection/.nycrc @@ -0,0 +1,19 @@ +{ + "extension": [ + ".ts" + ], + "include": [ + "src/*.ts", + "src/**/*.ts" + ], + "require": [ + "ts-node/register" + ], + "reporter": [ + "text-summary", + "json", + "html" + ], + "sourceMap":true, + "all": true +} diff --git a/packages/payment-detection/.vscode/settings.json b/packages/payment-detection/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/payment-detection/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/payment-detection/CHANGELOG.md b/packages/payment-detection/CHANGELOG.md new file mode 100644 index 0000000000..127b0c9f11 --- /dev/null +++ b/packages/payment-detection/CHANGELOG.md @@ -0,0 +1,197 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.18.0 (2020-06-29) + + +### Features + +* amount are only number or string ([#223](https://github.com/RequestNetwork/requestNetwork/issues/223)) ([7a35bde](https://github.com/RequestNetwork/requestNetwork/commit/7a35bde63f78b9305819a80e97022fca7e9494d2)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* add request custom etherscan key ([#152](https://github.com/RequestNetwork/requestNetwork/issues/152)) ([5b74d0e](https://github.com/RequestNetwork/requestNetwork/commit/5b74d0efe7c38e1e995ac0af34e4a0c9ecf712fd)) + + +### Features + +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.17.0 (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* add request custom etherscan key ([#152](https://github.com/RequestNetwork/requestNetwork/issues/152)) ([5b74d0e](https://github.com/RequestNetwork/requestNetwork/commit/5b74d0efe7c38e1e995ac0af34e4a0c9ecf712fd)) + + +### Features + +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* add request custom etherscan key ([#152](https://github.com/RequestNetwork/requestNetwork/issues/152)) ([5b74d0e](https://github.com/RequestNetwork/requestNetwork/commit/5b74d0efe7c38e1e995ac0af34e4a0c9ecf712fd)) + + +### Features + +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* add request custom etherscan key ([#152](https://github.com/RequestNetwork/requestNetwork/issues/152)) ([5b74d0e](https://github.com/RequestNetwork/requestNetwork/commit/5b74d0efe7c38e1e995ac0af34e4a0c9ecf712fd)) + + +### Features + +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.14.0 (2020-03-23) + + +### Bug Fixes + +* add request custom etherscan key ([#152](https://github.com/RequestNetwork/requestNetwork/issues/152)) ([5b74d0e](https://github.com/RequestNetwork/requestNetwork/commit/5b74d0efe7c38e1e995ac0af34e4a0c9ecf712fd)) + + +### Features + +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) diff --git a/packages/payment-detection/README.md b/packages/payment-detection/README.md new file mode 100644 index 0000000000..98dc6c2268 --- /dev/null +++ b/packages/payment-detection/README.md @@ -0,0 +1,22 @@ +# @requestnetwork/payment-detection + +`@requestnetwork/payment-detection` is a typescript library part of the [Request Network protocol](https://github.com/RequestNetwork/requestNetwork). +It contains client-side payment detection for all supported payment networks. + +### Payment and Refund detections + +If a payment network has been given to the request, the payment detection can be done. + +From the information provided in payment network, the library will feed the property `balance` of the request with: + +- `balance`: the sum of the amount of all payments minus the sum of amount of all refunds +- `events`: all the payments and refunds events with the amount, timestamp etc... + +The payment networks available are: + +- `Types.Payment.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED` ('pn-bitcoin-address-based'): handle Bitcoin payments associated to a BTC address to the request, every transaction hitting this address will be consider as a payment. Optionally, the payer can provide a BTC address for the refunds. Note that **the addresses must be used only for one and only one request** otherwise one transaction will be considered as a payment for more than one request. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0.md)) +- `Types.Payment.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED` ('pn-testnet-bitcoin-address-based'): Same as previous but for the bitcoin testnet (for test purpose) +- `Types.Payment.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED`('pn-erc20-address-based'): Same as `BITCOIN_ADDRESS_BASED`, for ERC20 payments. +- `Types.Payment.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT`('pn-erc20-proxy-contract'): uses an intermediary contract to document which request is being paid, through the `PaymentReference`. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0.md)) +- `Types.Payment.PAYMENT_NETWORK_ID.ETH_INPUT_DATA`('pn-eth-input-data'): uses the transaction input data to pass the `PaymentReference`. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-eth-input-data-0.1.0.md)) +- `Types.Payment.PAYMENT_NETWORK_ID.DECLARATIVE`('pn-any-declarative'): a manual alternative, where payer can declare a payment sent, and payee can declare it received, working for any currency. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-any-declarative-0.1.0.md)) diff --git a/packages/payment-detection/package.json b/packages/payment-detection/package.json new file mode 100644 index 0000000000..7c019adf90 --- /dev/null +++ b/packages/payment-detection/package.json @@ -0,0 +1,74 @@ +{ + "name": "@requestnetwork/payment-detection", + "version": "0.18.0", + "publishConfig": { + "access": "public" + }, + "description": "Payment detection using ethers.", + "keywords": [ + "requestnetwork" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RequestNetwork/requestNetwork.git" + }, + "homepage": "https://github.com/RequestNetwork/requestNetwork/tree/master/packages/payment-detection#readme", + "bugs": { + "url": "https://github.com/RequestNetwork/requestNetwork/issues" + }, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "directories": { + "lib": "src", + "test": "test" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc -b", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", + "lint": "tslint --project . && eslint \"src/**/*.ts\"", + "lint-staged": "lint-staged", + "prepare": "yarn run build", + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" + }, + "dependencies": { + "@requestnetwork/smart-contracts": "0.9.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", + "axios": "0.19.0", + "bn.js": "4.11.8", + "ethers": "4.0.45", + "node-fetch": "2.6.0", + "satoshi-bitcoin": "1.0.4" + }, + "devDependencies": { + "@types/bn.js": "4.11.5", + "@types/chai": "4.1.7", + "@types/mocha": "5.2.7", + "@types/sinon": "7.5.0", + "@typescript-eslint/parser": "1.2.0", + "chai": "4.2.0", + "chai-as-promised": "7.1.1", + "chai-spies": "1.0.0", + "eslint": "5.13.0", + "eslint-plugin-spellcheck": "0.0.14", + "eslint-plugin-typescript": "0.14.0", + "lint-staged": "8.1.3", + "mocha": "6.2.2", + "nyc": "15.0.0", + "prettier": "1.16.4", + "shx": "0.3.2", + "sinon": "7.5.0", + "source-map-support": "0.5.13", + "ts-node": "8.6.2", + "tslint": "5.12.1", + "typescript": "3.7.2" + } +} diff --git a/packages/payment-detection/src/balance-error.ts b/packages/payment-detection/src/balance-error.ts new file mode 100644 index 0000000000..9865fdfd49 --- /dev/null +++ b/packages/payment-detection/src/balance-error.ts @@ -0,0 +1,22 @@ +import { PaymentTypes } from '@requestnetwork/types'; + +/** + * Creates the object IBalanceWithEvents from the error code and error message + * + * @param message error message + * @param code error code + * @returns The object IBalanceWithEvents to return + */ +export default function getBalanceErrorObject( + message: string, + code?: PaymentTypes.BALANCE_ERROR_CODE, +): PaymentTypes.IBalanceWithEvents { + return { + balance: null, + error: { + code: code || PaymentTypes.BALANCE_ERROR_CODE.UNKNOWN, + message, + }, + events: [], + }; +} diff --git a/packages/request-client.js/src/api/payment-network/btc/address-based.ts b/packages/payment-detection/src/btc/address-based.ts similarity index 65% rename from packages/request-client.js/src/api/payment-network/btc/address-based.ts rename to packages/payment-detection/src/btc/address-based.ts index dc42d78e4d..8d312f6153 100644 --- a/packages/request-client.js/src/api/payment-network/btc/address-based.ts +++ b/packages/payment-detection/src/btc/address-based.ts @@ -1,5 +1,6 @@ -import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import * as Types from '../../../types'; +import { ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; + +import getBalanceErrorObject from '../balance-error'; import DefaultBitcoinDetectionProvider from './default-bitcoin-detection-provider'; const bigNumber: any = require('bn.js'); @@ -8,14 +9,14 @@ const bigNumber: any = require('bn.js'); */ export default class PaymentNetworkBTCAddressBased { private extension: ExtensionTypes.PnAddressBased.IAddressBased; - private bitcoinDetectionProvider: Types.IBitcoinDetectionProvider; + private bitcoinDetectionProvider: PaymentTypes.IBitcoinDetectionProvider; /** * @param extension The advanced logic payment network extensions */ public constructor( extension: ExtensionTypes.PnAddressBased.IAddressBased, - bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider, + bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider, ) { this.extension = extension; this.bitcoinDetectionProvider = @@ -77,44 +78,51 @@ export default class PaymentNetworkBTCAddressBased { request: RequestLogicTypes.IRequest, paymentNetworkId: ExtensionTypes.ID, networkId: number, - ): Promise { + ): Promise { if (!request.extensions[paymentNetworkId]) { - throw new Error(`The request do not have the extension : ̀${paymentNetworkId}`); + return getBalanceErrorObject( + `The request does not have the extension: ${paymentNetworkId}`, + PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION, + ); } const paymentAddress = request.extensions[paymentNetworkId].values.paymentAddress; const refundAddress = request.extensions[paymentNetworkId].values.refundAddress; - let payments: Types.IBalanceWithEvents = { balance: '0', events: [] }; - if (paymentAddress) { - payments = await this.extractBalanceAndEvents( - paymentAddress, - Types.EVENTS_NAMES.PAYMENT, - networkId, - ); - } + try { + let payments: PaymentTypes.BTCBalanceWithEvents = { balance: '0', events: [] }; + if (paymentAddress) { + payments = await this.extractBalanceAndEvents( + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + networkId, + ); + } - let refunds: Types.IBalanceWithEvents = { balance: '0', events: [] }; - if (refundAddress) { - refunds = await this.extractBalanceAndEvents( - refundAddress, - Types.EVENTS_NAMES.REFUND, - networkId, - ); - } + let refunds: PaymentTypes.BTCBalanceWithEvents = { balance: '0', events: [] }; + if (refundAddress) { + refunds = await this.extractBalanceAndEvents( + refundAddress, + PaymentTypes.EVENTS_NAMES.REFUND, + networkId, + ); + } - const balance: string = new bigNumber(new bigNumber(payments.balance || 0)) - .sub(new bigNumber(refunds.balance || 0)) - .toString(); + const balance: string = new bigNumber(new bigNumber(payments.balance || 0)) + .sub(new bigNumber(refunds.balance || 0)) + .toString(); - const events: Types.IPaymentNetworkEvent[] = [...payments.events, ...refunds.events].sort( - (a: Types.IPaymentNetworkEvent, b: Types.IPaymentNetworkEvent) => - a.parameters.timestamp - b.parameters.timestamp, - ); + const events: PaymentTypes.BTCPaymentNetworkEvent[] = [ + ...payments.events, + ...refunds.events, + ].sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0)); - return { - balance, - events, - }; + return { + balance, + events, + }; + } catch (error) { + return getBalanceErrorObject(error.message); + } } /** @@ -127,9 +135,9 @@ export default class PaymentNetworkBTCAddressBased { */ private async extractBalanceAndEvents( address: string, - eventName: Types.EVENTS_NAMES, + eventName: PaymentTypes.EVENTS_NAMES, networkId: number, - ): Promise { + ): Promise { return this.bitcoinDetectionProvider.getAddressBalanceWithEvents(networkId, address, eventName); } } diff --git a/packages/request-client.js/src/api/payment-network/btc/default-bitcoin-detection-provider.ts b/packages/payment-detection/src/btc/default-bitcoin-detection-provider.ts similarity index 77% rename from packages/request-client.js/src/api/payment-network/btc/default-bitcoin-detection-provider.ts rename to packages/payment-detection/src/btc/default-bitcoin-detection-provider.ts index 150329ffc8..073dbf0a8d 100644 --- a/packages/request-client.js/src/api/payment-network/btc/default-bitcoin-detection-provider.ts +++ b/packages/payment-detection/src/btc/default-bitcoin-detection-provider.ts @@ -1,4 +1,4 @@ -import * as Types from '../../../types'; +import { PaymentTypes } from '@requestnetwork/types'; import BlockchainInfo from './default-providers/blockchain-info'; import BlockcypherCom from './default-providers/blockcypher-com'; @@ -8,8 +8,9 @@ import ChainSo from './default-providers/chain-so'; /** * The default Bitcoin detection provider give access to the bitcoin blockchain through several external API's */ -export default class DefaultBitcoinDetectionProvider implements Types.IBitcoinDetectionProvider { - public providers: Types.IBitcoinDetectionProvider[]; +export default class DefaultBitcoinDetectionProvider + implements PaymentTypes.IBitcoinDetectionProvider { + public providers: PaymentTypes.IBitcoinDetectionProvider[]; /** * Creates an instance of DefaultBitcoinDetectionProvider @@ -34,8 +35,8 @@ export default class DefaultBitcoinDetectionProvider implements Types.IBitcoinDe public async getAddressBalanceWithEvents( bitcoinNetworkId: number, address: string, - eventName: Types.EVENTS_NAMES, - ): Promise { + eventName: PaymentTypes.EVENTS_NAMES, + ): Promise { if (this.providers.length < 2) { throw new Error('At least two bitcoin providers are needed'); } @@ -84,24 +85,26 @@ export default class DefaultBitcoinDetectionProvider implements Types.IBitcoinDe * @returns Object containing IBalanceWithEvents and the count */ private getMostCommonBalance( - array: Types.IBalanceWithEvents[], - ): { count: number; value: Types.IBalanceWithEvents } | undefined { + array: PaymentTypes.BTCBalanceWithEvents[], + ): { count: number; value: PaymentTypes.BTCBalanceWithEvents } | undefined { // Reduce the array to an object indexed by balance with the count const duplicatesWithCount: { - [key: string]: { count: number; value: Types.IBalanceWithEvents }; + [key: string]: { count: number; value: PaymentTypes.BTCBalanceWithEvents }; } = array .filter(info => info.balance !== '-1') .reduce( ( accumulator: { - [key: string]: { count: number; value: Types.IBalanceWithEvents }; + [key: string]: { count: number; value: PaymentTypes.BTCBalanceWithEvents }; }, - elem: Types.IBalanceWithEvents, + elem: PaymentTypes.BTCBalanceWithEvents, ) => { - if (!accumulator[elem.balance]) { - accumulator[elem.balance] = { count: 0, value: elem }; + if (elem.balance !== null) { + if (!accumulator[elem.balance]) { + accumulator[elem.balance] = { count: 0, value: elem }; + } + accumulator[elem.balance].count++; } - accumulator[elem.balance].count++; return accumulator; }, {}, diff --git a/packages/request-client.js/src/api/payment-network/btc/default-providers/blockchain-info.ts b/packages/payment-detection/src/btc/default-providers/blockchain-info.ts similarity index 88% rename from packages/request-client.js/src/api/payment-network/btc/default-providers/blockchain-info.ts rename to packages/payment-detection/src/btc/default-providers/blockchain-info.ts index a25edd525d..09a79d0b15 100644 --- a/packages/request-client.js/src/api/payment-network/btc/default-providers/blockchain-info.ts +++ b/packages/payment-detection/src/btc/default-providers/blockchain-info.ts @@ -1,6 +1,7 @@ +import { PaymentTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import fetch from 'node-fetch'; -import * as Types from '../../../../types'; + const bigNumber: any = require('bn.js'); /* eslint-disable spellcheck/spell-checker */ @@ -17,7 +18,7 @@ const TXS_PER_PAGE = 50; /** * The Bitcoin Info retriever give access to the bitcoin blockchain through the api of blockchain.info */ -export default class BlockchainInfo implements Types.IBitcoinDetectionProvider { +export default class BlockchainInfo implements PaymentTypes.IBitcoinDetectionProvider { /** * Gets BTC address info using blockchain.info public API * @@ -29,8 +30,8 @@ export default class BlockchainInfo implements Types.IBitcoinDetectionProvider { public async getAddressBalanceWithEvents( bitcoinNetworkId: number, address: string, - eventName: Types.EVENTS_NAMES, - ): Promise { + eventName: PaymentTypes.EVENTS_NAMES, + ): Promise { const blockchainInfoUrl = this.getBlockchainInfoUrl(bitcoinNetworkId); const queryUrl = `${blockchainInfoUrl}/rawaddr/${address}?cors=true`; @@ -87,11 +88,14 @@ export default class BlockchainInfo implements Types.IBitcoinDetectionProvider { * @param eventName Indicates if it is an address for payment or refund * @returns Balance with events */ - public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents { + public parse( + addressInfo: any, + eventName: PaymentTypes.EVENTS_NAMES, + ): PaymentTypes.BTCBalanceWithEvents { const address = addressInfo.address; const balance = new bigNumber(addressInfo.total_received).toString(); - const events: Types.IPaymentNetworkEvent[] = addressInfo.txs + const events: PaymentTypes.BTCPaymentNetworkEvent[] = addressInfo.txs // exclude the transactions coming from the same address .filter((tx: any) => { const selfInputs = tx.inputs.filter( @@ -112,14 +116,14 @@ export default class BlockchainInfo implements Types.IBitcoinDetectionProvider { }, []) .filter((output: any) => output.output.addr === address) .map( - (output: any): Types.IPaymentNetworkEvent => ({ + (output: any): PaymentTypes.BTCPaymentNetworkEvent => ({ + amount: output.output.value.toString(), name: eventName, parameters: { - amount: output.output.value.toString(), block: output.blockHeight, - timestamp: output.timestamp, txHash: output.txHash, }, + timestamp: output.timestamp, }), ); diff --git a/packages/request-client.js/src/api/payment-network/btc/default-providers/blockcypher-com.ts b/packages/payment-detection/src/btc/default-providers/blockcypher-com.ts similarity index 84% rename from packages/request-client.js/src/api/payment-network/btc/default-providers/blockcypher-com.ts rename to packages/payment-detection/src/btc/default-providers/blockcypher-com.ts index 8c6c3d461a..44808293ce 100644 --- a/packages/request-client.js/src/api/payment-network/btc/default-providers/blockcypher-com.ts +++ b/packages/payment-detection/src/btc/default-providers/blockcypher-com.ts @@ -1,6 +1,6 @@ +import { PaymentTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import fetch from 'node-fetch'; -import * as Types from '../../../../types'; const bigNumber: any = require('bn.js'); /* eslint-disable spellcheck/spell-checker */ @@ -14,7 +14,7 @@ const BLOCKCYPHER_REQUEST_RETRY_DELAY = 100; /** * The Bitcoin Info retriever give access to the bitcoin blockchain through the api of blockcypher.com */ -export default class BlockcypherCom implements Types.IBitcoinDetectionProvider { +export default class BlockcypherCom implements PaymentTypes.IBitcoinDetectionProvider { /** * Gets BTC address info using blockcypher.com public API * @@ -26,8 +26,8 @@ export default class BlockcypherCom implements Types.IBitcoinDetectionProvider { public async getAddressBalanceWithEvents( bitcoinNetworkId: number, address: string, - eventName: Types.EVENTS_NAMES, - ): Promise { + eventName: PaymentTypes.EVENTS_NAMES, + ): Promise { const baseUrl = this.getBaseUrl(bitcoinNetworkId); const queryUrl = `${baseUrl}/addrs/${address}`; try { @@ -57,7 +57,10 @@ export default class BlockcypherCom implements Types.IBitcoinDetectionProvider { * @param eventName Indicates if it is an address for payment or refund * @returns Balance with events */ - public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents { + public parse( + addressInfo: any, + eventName: PaymentTypes.EVENTS_NAMES, + ): PaymentTypes.BTCBalanceWithEvents { const balance = new bigNumber(addressInfo.total_received).toString(); // Retrieves all the transaction hash of the transactions having as input the current address @@ -65,20 +68,20 @@ export default class BlockcypherCom implements Types.IBitcoinDetectionProvider { .filter((tx: any) => tx.tx_output_n === -1) .map((tx: any) => tx.tx_hash); - const events: Types.IPaymentNetworkEvent[] = addressInfo.txrefs + const events: PaymentTypes.BTCPaymentNetworkEvent[] = addressInfo.txrefs // keep only the transaction with this address as output .filter((tx: any) => tx.tx_input_n === -1) // exclude the transactions coming from the same address .filter((tx: any) => !inputTxHashes.includes(tx.tx_hash)) .map( - (tx: any): Types.IPaymentNetworkEvent => ({ + (tx: any): PaymentTypes.BTCPaymentNetworkEvent => ({ + amount: tx.value.toString(), name: eventName, parameters: { - amount: tx.value.toString(), block: tx.block_height, - // timestamp - not given by this API txHash: tx.tx_hash, }, + // timestamp - not given by this API }), ); diff --git a/packages/request-client.js/src/api/payment-network/btc/default-providers/blockstream-info.ts b/packages/payment-detection/src/btc/default-providers/blockstream-info.ts similarity index 85% rename from packages/request-client.js/src/api/payment-network/btc/default-providers/blockstream-info.ts rename to packages/payment-detection/src/btc/default-providers/blockstream-info.ts index 5cab6f864a..484b8b7a1d 100644 --- a/packages/request-client.js/src/api/payment-network/btc/default-providers/blockstream-info.ts +++ b/packages/payment-detection/src/btc/default-providers/blockstream-info.ts @@ -1,6 +1,6 @@ +import { PaymentTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import fetch from 'node-fetch'; -import * as Types from '../../../../types'; const bigNumber: any = require('bn.js'); /* eslint-disable spellcheck/spell-checker */ @@ -17,7 +17,7 @@ const TXS_PER_PAGE = 25; /** * The Bitcoin Info retriever give access to the bitcoin blockchain through the api of blockstream.info */ -export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider { +export default class BlockstreamInfo implements PaymentTypes.IBitcoinDetectionProvider { /** * Gets BTC address info using blockstream.info public API * @@ -29,8 +29,8 @@ export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider public async getAddressBalanceWithEvents( bitcoinNetworkId: number, address: string, - eventName: Types.EVENTS_NAMES, - ): Promise { + eventName: PaymentTypes.EVENTS_NAMES, + ): Promise { const baseUrl = this.getBaseUrl(bitcoinNetworkId); const queryUrl = `${baseUrl}/address/${address}/txs`; try { @@ -87,8 +87,11 @@ export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider * @param eventName Indicates if it is an address for payment or refund * @returns Balance with events */ - public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents { - const events: Types.IPaymentNetworkEvent[] = addressInfo.txs + public parse( + addressInfo: any, + eventName: PaymentTypes.EVENTS_NAMES, + ): PaymentTypes.BTCBalanceWithEvents { + const events: PaymentTypes.BTCPaymentNetworkEvent[] = addressInfo.txs // exclude the transactions coming from the same address .filter((tx: any) => { const autoVin = tx.vin.filter( @@ -109,20 +112,20 @@ export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider }, []) .filter((output: any) => output.output.scriptpubkey_address === addressInfo.address) .map( - (output: any): Types.IPaymentNetworkEvent => ({ + (output: any): PaymentTypes.BTCPaymentNetworkEvent => ({ + amount: output.output.value.toString(), name: eventName, parameters: { - amount: output.output.value.toString(), block: output.blockHeight, - timestamp: output.timestamp, txHash: output.txHash, }, + timestamp: output.timestamp, }), ); const balance: string = events - .reduce((balanceAccumulator: any, event: Types.IPaymentNetworkEvent) => { - return balanceAccumulator.add(new bigNumber(event.parameters.amount)); + .reduce((balanceAccumulator: any, event: PaymentTypes.BTCPaymentNetworkEvent) => { + return balanceAccumulator.add(new bigNumber(event.amount)); }, new bigNumber('0')) .toString(); diff --git a/packages/request-client.js/src/api/payment-network/btc/default-providers/chain-so.ts b/packages/payment-detection/src/btc/default-providers/chain-so.ts similarity index 79% rename from packages/request-client.js/src/api/payment-network/btc/default-providers/chain-so.ts rename to packages/payment-detection/src/btc/default-providers/chain-so.ts index d646eb4336..e6259ebb2b 100644 --- a/packages/request-client.js/src/api/payment-network/btc/default-providers/chain-so.ts +++ b/packages/payment-detection/src/btc/default-providers/chain-so.ts @@ -1,6 +1,6 @@ +import { PaymentTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import fetch from 'node-fetch'; -import * as Types from '../../../../types'; const converterBTC = require('satoshi-bitcoin'); const bigNumber: any = require('bn.js'); @@ -15,7 +15,7 @@ const CHAINSO_REQUEST_RETRY_DELAY = 100; /** * The Bitcoin Info retriever give access to the bitcoin blockchain through the api of chain.so */ -export default class ChainSo implements Types.IBitcoinDetectionProvider { +export default class ChainSo implements PaymentTypes.IBitcoinDetectionProvider { /** * Gets BTC address info using chain.so public API * @@ -27,10 +27,10 @@ export default class ChainSo implements Types.IBitcoinDetectionProvider { public async getAddressBalanceWithEvents( bitcoinNetworkId: number, address: string, - eventName: Types.EVENTS_NAMES, - ): Promise { + eventName: PaymentTypes.EVENTS_NAMES, + ): Promise { const baseUrl = this.getBaseUrl(bitcoinNetworkId); - const queryUrl = `${baseUrl}/${address}`; + const queryUrl = `${baseUrl}${address}`; try { const res = await Utils.retry(async () => fetch(queryUrl), { @@ -63,28 +63,31 @@ export default class ChainSo implements Types.IBitcoinDetectionProvider { * @param eventName Indicates if it is an address for payment or refund * @returns Balance with events */ - public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents { - const events: Types.IPaymentNetworkEvent[] = addressInfo.data.txs + public parse( + addressInfo: any, + eventName: PaymentTypes.EVENTS_NAMES, + ): PaymentTypes.BTCBalanceWithEvents { + const events: PaymentTypes.BTCPaymentNetworkEvent[] = addressInfo.data.txs // keep only the transaction with value incoming to the address .filter((tx: any) => tx.incoming !== undefined) // delete transactions that are from this address .filter((tx: any) => tx.outgoing === undefined) .map( - (tx: any): Types.IPaymentNetworkEvent => ({ + (tx: any): PaymentTypes.BTCPaymentNetworkEvent => ({ + amount: converterBTC.toSatoshi(tx.incoming.value).toString(), name: eventName, parameters: { - amount: converterBTC.toSatoshi(tx.incoming.value).toString(), block: tx.block_no, - timestamp: tx.time, txHash: tx.txid, }, + timestamp: tx.time, }), ); // Compute the balance making the sum of all the transactions amount const balance: string = events - .reduce((balanceAccumulator: any, event: Types.IPaymentNetworkEvent) => { - return balanceAccumulator.add(new bigNumber(event.parameters.amount)); + .reduce((balanceAccumulator: any, event: PaymentTypes.BTCPaymentNetworkEvent) => { + return balanceAccumulator.add(new bigNumber(event.amount)); }, new bigNumber('0')) .toString(); diff --git a/packages/payment-detection/src/btc/index.ts b/packages/payment-detection/src/btc/index.ts new file mode 100644 index 0000000000..70ab0c51aa --- /dev/null +++ b/packages/payment-detection/src/btc/index.ts @@ -0,0 +1,23 @@ +import BtcMainnetPaymentNetwork from './mainnet-address-based'; +import BtcTestnetPaymentNetwork from './testnet-address-based'; + +import DefaultBitcoinDetectionProvider from './default-bitcoin-detection-provider'; + +import BlockchainInfoProvider from './default-providers/blockchain-info'; +import BlockcypherComProvider from './default-providers/blockcypher-com'; +import BlockStreamInfoProvider from './default-providers/blockstream-info'; +import ChainSoProvider from './default-providers/chain-so'; + +const providers = { + BlockStreamInfoProvider, + BlockchainInfoProvider, + BlockcypherComProvider, + ChainSoProvider, +}; + +export { + DefaultBitcoinDetectionProvider, + BtcMainnetPaymentNetwork, + BtcTestnetPaymentNetwork, + providers as Providers, +}; diff --git a/packages/request-client.js/src/api/payment-network/btc/mainnet-address-based.ts b/packages/payment-detection/src/btc/mainnet-address-based.ts similarity index 79% rename from packages/request-client.js/src/api/payment-network/btc/mainnet-address-based.ts rename to packages/payment-detection/src/btc/mainnet-address-based.ts index f012cd2f27..7154a44824 100644 --- a/packages/request-client.js/src/api/payment-network/btc/mainnet-address-based.ts +++ b/packages/payment-detection/src/btc/mainnet-address-based.ts @@ -1,5 +1,9 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import * as Types from '../../../types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; import BTCAddressBased from './address-based'; @@ -12,7 +16,8 @@ const MAINNET_BITCOIN_NETWORK_ID = 0; * * @class PaymentNetworkBTCAddressBased */ -export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetwork { +export default class PaymentNetworkBTCAddressBased + implements PaymentTypes.IPaymentNetwork { private btcAddressBased: BTCAddressBased; /** @@ -23,7 +28,7 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw bitcoinDetectionProvider, }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic; - bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider; + bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider; }) { this.btcAddressBased = new BTCAddressBased( advancedLogic.extensions.addressBasedBtc, @@ -38,9 +43,9 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw * * @returns the extensions data object */ - public createExtensionsDataForCreation( + public async createExtensionsDataForCreation( paymentNetworkCreationParameters: ExtensionTypes.PnAddressBased.ICreationParameters, - ): ExtensionTypes.IAction { + ): Promise { return this.btcAddressBased.createExtensionsDataForCreation(paymentNetworkCreationParameters); } @@ -73,10 +78,12 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw /** * Gets the balance and the payment/refund events * - * @param the request to check + * @param request request to check * @returns the balance and the payment/refund events */ - public async getBalance(request: RequestLogicTypes.IRequest): Promise { + public async getBalance( + request: RequestLogicTypes.IRequest, + ): Promise { return this.btcAddressBased.getBalance( request, PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, diff --git a/packages/request-client.js/src/api/payment-network/btc/testnet-address-based.ts b/packages/payment-detection/src/btc/testnet-address-based.ts similarity index 80% rename from packages/request-client.js/src/api/payment-network/btc/testnet-address-based.ts rename to packages/payment-detection/src/btc/testnet-address-based.ts index c88290698a..7b69ea724a 100644 --- a/packages/request-client.js/src/api/payment-network/btc/testnet-address-based.ts +++ b/packages/payment-detection/src/btc/testnet-address-based.ts @@ -1,5 +1,9 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import * as Types from '../../../types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; import BTCAddressBased from './address-based'; @@ -12,7 +16,8 @@ const TESTNET_BITCOIN_NETWORK_ID = 3; * * @class PaymentNetworkBTCAddressBased */ -export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetwork { +export default class PaymentNetworkBTCAddressBased + implements PaymentTypes.IPaymentNetwork { private btcAddressBased: BTCAddressBased; /** @@ -23,7 +28,7 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw bitcoinDetectionProvider, }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic; - bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider; + bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider; }) { this.btcAddressBased = new BTCAddressBased( advancedLogic.extensions.addressBasedTestnetBtc, @@ -38,9 +43,9 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw * * @returns the extensions data object */ - public createExtensionsDataForCreation( + public async createExtensionsDataForCreation( paymentNetworkCreationParameters: ExtensionTypes.PnAddressBased.ICreationParameters, - ): ExtensionTypes.IAction { + ): Promise { return this.btcAddressBased.createExtensionsDataForCreation(paymentNetworkCreationParameters); } @@ -76,7 +81,9 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw * @param the request to check * @returns the balance and the payment/refund events */ - public async getBalance(request: RequestLogicTypes.IRequest): Promise { + public async getBalance( + request: RequestLogicTypes.IRequest, + ): Promise { return this.btcAddressBased.getBalance( request, PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED, diff --git a/packages/request-client.js/src/api/payment-network/declarative.ts b/packages/payment-detection/src/declarative.ts similarity index 78% rename from packages/request-client.js/src/api/payment-network/declarative.ts rename to packages/payment-detection/src/declarative.ts index f06e5abe1b..ab63b636a0 100644 --- a/packages/request-client.js/src/api/payment-network/declarative.ts +++ b/packages/payment-detection/src/declarative.ts @@ -1,5 +1,9 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import * as Types from '../../types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; const bigNumber: any = require('bn.js'); /** @@ -7,7 +11,8 @@ const bigNumber: any = require('bn.js'); * * @class PaymentNetworkDeclarative */ -export default class PaymentNetworkDeclarative implements Types.IPaymentNetwork { +export default class PaymentNetworkDeclarative + implements PaymentTypes.IPaymentNetwork { private extension: ExtensionTypes.PnAnyDeclarative.IAnyDeclarative; public constructor({ advancedLogic }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic }) { @@ -21,9 +26,9 @@ export default class PaymentNetworkDeclarative implements Types.IPaymentNetwork * @param paymentNetworkCreationParameters Parameters to create the extension * @returns The extensionData object */ - public createExtensionsDataForCreation( + public async createExtensionsDataForCreation( paymentNetworkCreationParameters: ExtensionTypes.PnAnyDeclarative.ICreationParameters, - ): ExtensionTypes.IAction { + ): Promise { return this.extension.createCreationAction({ paymentInfo: paymentNetworkCreationParameters.paymentInfo, refundInfo: paymentNetworkCreationParameters.refundInfo, @@ -126,28 +131,41 @@ export default class PaymentNetworkDeclarative implements Types.IPaymentNetwork * @param request the request to check * @returns the balance and the payment/refund events */ - public async getBalance(request: RequestLogicTypes.IRequest): Promise { + public async getBalance( + request: RequestLogicTypes.IRequest, + ): Promise { let balance = new bigNumber(0); - const events: Types.IPaymentNetworkEvent[] = []; + const events: PaymentTypes.DeclarativePaymentNetworkEvent[] = []; // For each extension data related to the declarative payment network, // we check if the data is a declared received payment or refund and we modify the balance // Received payment increase the balance and received refund decrease the balance - request.extensions[Types.PAYMENT_NETWORK_ID.DECLARATIVE].events.forEach(data => { + request.extensions[PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE].events.forEach(data => { + const parameters = data.parameters; if (data.name === ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_RECEIVED_PAYMENT) { - const parameters = data.parameters; - parameters.timestamp = data.timestamp; - // Declared received payments from payee is added to the balance - balance = balance.add(new bigNumber(data.parameters.amount)); - events.push({ name: Types.EVENTS_NAMES.PAYMENT, parameters }); + balance = balance.add(new bigNumber(parameters.amount)); + events.push({ + amount: parameters.amount, + name: PaymentTypes.EVENTS_NAMES.PAYMENT, + parameters: { + note: parameters.note, + }, + timestamp: data.timestamp, + }); } else if (data.name === ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_RECEIVED_REFUND) { - const parameters = data.parameters; parameters.timestamp = data.timestamp; // The balance is subtracted from declared received refunds from payer - balance = balance.sub(new bigNumber(data.parameters.amount)); - events.push({ name: Types.EVENTS_NAMES.REFUND, parameters }); + balance = balance.sub(new bigNumber(parameters.amount)); + events.push({ + amount: parameters.amount, + name: PaymentTypes.EVENTS_NAMES.REFUND, + parameters: { + note: parameters.note, + }, + timestamp: data.timestamp, + }); } }); diff --git a/packages/payment-detection/src/erc20/address-based-info-retriever.ts b/packages/payment-detection/src/erc20/address-based-info-retriever.ts new file mode 100644 index 0000000000..f14d50fd27 --- /dev/null +++ b/packages/payment-detection/src/erc20/address-based-info-retriever.ts @@ -0,0 +1,111 @@ +import { PaymentTypes } from '@requestnetwork/types'; +import { ethers } from 'ethers'; + +// The ERC20 smart contract ABI fragment containing decimals property and Transfer event +const erc20BalanceOfAbiFragment = [ + // decimals property + { + constant: true, + inputs: [], + name: 'decimals', + outputs: [ + { + name: '', + type: 'uint8', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + // Transfer events + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'from', + type: 'address', + }, + { + indexed: true, + name: 'to', + type: 'address', + }, + { + indexed: false, + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, +]; + +/** + * Retrieves a list of transfer events for an address + */ +export default class ERC20InfoRetriever + implements PaymentTypes.IPaymentNetworkInfoRetriever { + /** + * @param tokenContractAddress The address of the ERC20 contract + * @param address Address of the balance we want to check + * @param eventName Indicate if it is an address for payment or refund + * @param network The Ethereum network to use + */ + constructor( + private tokenContractAddress: string, + private toAddress: string, + private eventName: PaymentTypes.EVENTS_NAMES, + private network: string, + ) {} + + /** + * Retrieves transfer events for the current contract, address and network. + */ + public async getTransferEvents(): Promise { + // Creates a local or default provider + const provider = + this.network === 'private' + ? new ethers.providers.JsonRpcProvider() + : ethers.getDefaultProvider(this.network); + + // Setup the ERC20 contract interface + const contract = new ethers.Contract( + this.tokenContractAddress, + erc20BalanceOfAbiFragment, + provider, + ); + + // Create a filter to find all the Transfer logs for the toAddress + const filter = contract.filters.Transfer(null, this.toAddress) as ethers.providers.Filter; + filter.fromBlock = 0; + filter.toBlock = 'latest'; + + // Get the event logs + const logs = await provider.getLogs(filter); + + // Clean up the Transfer logs data + const eventPromises = logs.map(async log => { + if (!log.blockNumber) { + throw new Error('Block number not found'); + } + const block = await provider.getBlock(log.blockNumber); + const parsedLog = contract.interface.parseLog(log); + return { + amount: parsedLog.values.value.toString(), + name: this.eventName, + parameters: { + block: block.number, + from: parsedLog.values.from, + to: parsedLog.values.to, + txHash: log.transactionHash, + }, + timestamp: block.timestamp, + }; + }); + + return Promise.all(eventPromises); + } +} diff --git a/packages/request-client.js/src/api/payment-network/erc20/address-based.ts b/packages/payment-detection/src/erc20/address-based.ts similarity index 54% rename from packages/request-client.js/src/api/payment-network/erc20/address-based.ts rename to packages/payment-detection/src/erc20/address-based.ts index cc6a742d78..86da133118 100644 --- a/packages/request-client.js/src/api/payment-network/erc20/address-based.ts +++ b/packages/payment-detection/src/erc20/address-based.ts @@ -1,6 +1,11 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import * as Types from '../../../types'; -import erc20InfoRetriever from './info-retriever'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import getBalanceErrorObject from '../balance-error'; +import Erc20InfoRetriever from './address-based-info-retriever'; const bigNumber: any = require('bn.js'); const supportedNetworks = ['mainnet', 'rinkeby', 'private']; @@ -8,7 +13,8 @@ const supportedNetworks = ['mainnet', 'rinkeby', 'private']; /** * Handle payment networks with ERC20 based address extension */ -export default class PaymentNetworkERC20AddressBased implements Types.IPaymentNetwork { +export default class PaymentNetworkERC20AddressBased + implements PaymentTypes.IPaymentNetwork { private extension: ExtensionTypes.PnAddressBased.IAddressBased; /** * @param extension The advanced logic payment network extensions @@ -23,9 +29,9 @@ export default class PaymentNetworkERC20AddressBased implements Types.IPaymentNe * @param paymentNetworkCreationParameters Parameters to create the extension * @returns The extensionData object */ - public createExtensionsDataForCreation( + public async createExtensionsDataForCreation( paymentNetworkCreationParameters: ExtensionTypes.PnAddressBased.ICreationParameters, - ): ExtensionTypes.IAction { + ): Promise { return this.extension.createCreationAction({ paymentAddress: paymentNetworkCreationParameters.paymentAddress, refundAddress: paymentNetworkCreationParameters.refundAddress, @@ -66,59 +72,66 @@ export default class PaymentNetworkERC20AddressBased implements Types.IPaymentNe * @param request the request to check * @returns the balance and the payment/refund events */ - public async getBalance(request: RequestLogicTypes.IRequest): Promise { + public async getBalance( + request: RequestLogicTypes.IRequest, + ): Promise { if (!request.currency.network) { request.currency.network = 'mainnet'; } if (!supportedNetworks.includes(request.currency.network)) { - throw new Error( + return getBalanceErrorObject( `Payment network ${ request.currency.network } not supported by ERC20 payment detection. Supported networks: ${supportedNetworks.join( ', ', )}`, + PaymentTypes.BALANCE_ERROR_CODE.NETWORK_NOT_SUPPORTED, ); } - const paymentAddress = - request.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED].values - .paymentAddress; - const refundAddress = - request.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED].values - .refundAddress; + try { + const paymentAddress = + request.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED].values + .paymentAddress; + const refundAddress = + request.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED].values + .refundAddress; - let payments: Types.IBalanceWithEvents = { balance: '0', events: [] }; - if (paymentAddress) { - payments = await this.extractBalanceAndEvents( - paymentAddress, - Types.EVENTS_NAMES.PAYMENT, - request.currency.network, - request.currency.value, - ); - } + let payments: PaymentTypes.ERC20BalanceWithEvents = { balance: '0', events: [] }; + if (paymentAddress) { + payments = await this.extractBalanceAndEvents( + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + request.currency.network, + request.currency.value, + ); + } - let refunds: Types.IBalanceWithEvents = { balance: '0', events: [] }; - if (refundAddress) { - refunds = await this.extractBalanceAndEvents( - refundAddress, - Types.EVENTS_NAMES.REFUND, - request.currency.network, - request.currency.value, - ); - } + let refunds: PaymentTypes.ERC20BalanceWithEvents = { balance: '0', events: [] }; + if (refundAddress) { + refunds = await this.extractBalanceAndEvents( + refundAddress, + PaymentTypes.EVENTS_NAMES.REFUND, + request.currency.network, + request.currency.value, + ); + } - const balance: string = new bigNumber(payments.balance || 0) - .sub(new bigNumber(refunds.balance || 0)) - .toString(); + const balance: string = new bigNumber(payments.balance || 0) + .sub(new bigNumber(refunds.balance || 0)) + .toString(); - const events: Types.IPaymentNetworkEvent[] = [...payments.events, ...refunds.events].sort( - (a: Types.IPaymentNetworkEvent, b: Types.IPaymentNetworkEvent) => - a.parameters.timestamp - b.parameters.timestamp, - ); + const events: PaymentTypes.ERC20PaymentNetworkEvent[] = [ + ...payments.events, + ...refunds.events, + ].sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0)); - return { - balance, - events, - }; + return { + balance, + events, + }; + } catch (error) { + return getBalanceErrorObject(error.message); + } } /** @@ -133,16 +146,16 @@ export default class PaymentNetworkERC20AddressBased implements Types.IPaymentNe */ private async extractBalanceAndEvents( address: string, - eventName: Types.EVENTS_NAMES, + eventName: PaymentTypes.EVENTS_NAMES, network: string, tokenContractAddress: string, - ): Promise { - const info = await erc20InfoRetriever(tokenContractAddress, address, network); + ): Promise { + const infoRetriever = new Erc20InfoRetriever(tokenContractAddress, address, eventName, network); + const events = await infoRetriever.getTransferEvents(); - const balance = info.tokenEvents - .reduce((acc, event) => acc.add(new bigNumber(event.value)), new bigNumber(0)) + const balance = events + .reduce((acc, event) => acc.add(new bigNumber(event.amount)), new bigNumber(0)) .toString(); - const events = info.tokenEvents.map(event => ({ name: eventName, parameters: event })); return { balance, diff --git a/packages/payment-detection/src/erc20/index.ts b/packages/payment-detection/src/erc20/index.ts new file mode 100644 index 0000000000..954862e103 --- /dev/null +++ b/packages/payment-detection/src/erc20/index.ts @@ -0,0 +1,4 @@ +import AddressBased from './address-based'; +import ProxyContract from './proxy-contract'; + +export { AddressBased, ProxyContract }; diff --git a/packages/payment-detection/src/erc20/proxy-contract.ts b/packages/payment-detection/src/erc20/proxy-contract.ts new file mode 100644 index 0000000000..6972c40dcf --- /dev/null +++ b/packages/payment-detection/src/erc20/proxy-contract.ts @@ -0,0 +1,244 @@ +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; +import getBalanceErrorObject from '../balance-error'; +import PaymentReferenceCalculator from '../payment-reference-calculator'; +import ProxyInfoRetriever from './proxy-info-retriever'; + +const bigNumber: any = require('bn.js'); + +// tslint:disable:max-classes-per-file +/** Exception when network not supported */ +class NetworkNotSupported extends Error {} +/** Exception when version not supported */ +class VersionNotSupported extends Error {} + +interface IProxyContractByVersionByNetwork { + [version: string]: { + [network: string]: { address: string; creationBlockNumber: number }; + }; +} + +const PROXY_CONTRACT_ADDRESS_BY_VERSION_BY_NETWORK: IProxyContractByVersionByNetwork = { + ['0.1.0']: { + mainnet: { + address: '0x5f821c20947ff9be22e823edc5b3c709b33121b3', + creationBlockNumber: 9119380, + }, + private: { + address: '0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4', + creationBlockNumber: 0, + }, + rinkeby: { + address: '0x162edb802fae75b9ee4288345735008ba51a4ec9', + creationBlockNumber: 5628198, + }, + }, +}; + +/** + * Handle payment networks with ERC20 proxy contract extension + */ +export default class PaymentNetworkERC20ProxyContract implements PaymentTypes.IPaymentNetwork { + private extension: ExtensionTypes.PnReferenceBased.IReferenceBased; + /** + * @param extension The advanced logic payment network extensions + */ + public constructor({ advancedLogic }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic }) { + this.extension = advancedLogic.extensions.proxyContractErc20; + } + + /** + * Creates the extensions data for the creation of this extension. + * Will set a salt if none is already given + * + * @param paymentNetworkCreationParameters Parameters to create the extension + * @returns The extensionData object + */ + public async createExtensionsDataForCreation( + paymentNetworkCreationParameters: PaymentTypes.IReferenceBasedCreationParameters, + ): Promise { + // If no salt is given, generate one + const salt = + paymentNetworkCreationParameters.salt || (await Utils.crypto.generate8randomBytes()); + + return this.extension.createCreationAction({ + paymentAddress: paymentNetworkCreationParameters.paymentAddress, + refundAddress: paymentNetworkCreationParameters.refundAddress, + salt, + }); + } + + /** + * Creates the extensions data to add payment address + * + * @param parameters to add payment information + * @returns The extensionData object + */ + public createExtensionsDataForAddPaymentInformation( + parameters: ExtensionTypes.PnReferenceBased.IAddPaymentAddressParameters, + ): ExtensionTypes.IAction { + return this.extension.createAddPaymentAddressAction({ + paymentAddress: parameters.paymentAddress, + }); + } + + /** + * Creates the extensions data to add refund address + * + * @param Parameters to add refund information + * @returns The extensionData object + */ + public createExtensionsDataForAddRefundInformation( + parameters: ExtensionTypes.PnReferenceBased.IAddRefundAddressParameters, + ): ExtensionTypes.IAction { + return this.extension.createAddRefundAddressAction({ + refundAddress: parameters.refundAddress, + }); + } + + /** + * Gets the balance and the payment/refund events + * + * @param request the request to check + * @param paymentNetworkId payment network id + * @param tokenContractAddress the address of the token contract + * @returns the balance and the payment/refund events + */ + public async getBalance( + request: RequestLogicTypes.IRequest, + ): Promise { + const paymentNetworkId = ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT; + const paymentNetwork = request.extensions[paymentNetworkId]; + + if (!paymentNetwork) { + return getBalanceErrorObject( + `The request does not have the extension : ${paymentNetworkId}`, + PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION, + ); + } + try { + const paymentAddress = paymentNetwork.values.paymentAddress; + const refundAddress = paymentNetwork.values.refundAddress; + const salt = paymentNetwork.values.salt; + + let payments: PaymentTypes.IBalanceWithEvents = { balance: '0', events: [] }; + if (paymentAddress) { + payments = await this.extractBalanceAndEvents( + request, + salt, + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + paymentNetwork.version, + ); + } + + let refunds: PaymentTypes.IBalanceWithEvents = { balance: '0', events: [] }; + if (refundAddress) { + refunds = await this.extractBalanceAndEvents( + request, + salt, + refundAddress, + PaymentTypes.EVENTS_NAMES.REFUND, + paymentNetwork.version, + ); + } + + const balance: string = new bigNumber(payments.balance || 0) + .sub(new bigNumber(refunds.balance || 0)) + .toString(); + + const events: PaymentTypes.ERC20PaymentNetworkEvent[] = [ + ...payments.events, + ...refunds.events, + ].sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0)); + + return { + balance, + events, + }; + } catch (error) { + let code: PaymentTypes.BALANCE_ERROR_CODE | undefined; + if (error instanceof NetworkNotSupported) { + code = PaymentTypes.BALANCE_ERROR_CODE.NETWORK_NOT_SUPPORTED; + } + if (error instanceof VersionNotSupported) { + code = PaymentTypes.BALANCE_ERROR_CODE.VERSION_NOT_SUPPORTED; + } + return getBalanceErrorObject(error.message, code); + } + } + + /** + * Extracts the balance and events of an address + * + * @private + * @param address Address to check + * @param eventName Indicate if it is an address for payment or refund + * @param network The id of network we want to check + * @param tokenContractAddress the address of the token contract + * @returns The balance and events + */ + private async extractBalanceAndEvents( + request: RequestLogicTypes.IRequest, + salt: string, + toAddress: string, + eventName: PaymentTypes.EVENTS_NAMES, + paymentNetworkVersion: string, + ): Promise { + const network = request.currency.network; + + if (!network) { + throw new NetworkNotSupported(`Payment network not supported by ERC20 payment detection`); + } + + if (!PROXY_CONTRACT_ADDRESS_BY_VERSION_BY_NETWORK[paymentNetworkVersion]) { + throw new VersionNotSupported( + `Payment network version not supported: ${paymentNetworkVersion}`, + ); + } + + const proxyContractAddress: string | undefined = + PROXY_CONTRACT_ADDRESS_BY_VERSION_BY_NETWORK[paymentNetworkVersion][network].address; + const proxyCreationBlockNumber: number = + PROXY_CONTRACT_ADDRESS_BY_VERSION_BY_NETWORK[paymentNetworkVersion][network] + .creationBlockNumber; + + if (!proxyContractAddress) { + throw new NetworkNotSupported( + `Network not supported for this payment network: ${request.currency.network}`, + ); + } + + const paymentReference = PaymentReferenceCalculator.calculate( + request.requestId, + salt, + toAddress, + ); + + const infoRetriever = new ProxyInfoRetriever( + paymentReference, + proxyContractAddress, + proxyCreationBlockNumber, + request.currency.value, + toAddress, + eventName, + network, + ); + + const events = await infoRetriever.getTransferEvents(); + + const balance = events + .reduce((acc, event) => acc.add(new bigNumber(event.amount)), new bigNumber(0)) + .toString(); + + return { + balance, + events, + }; + } +} diff --git a/packages/payment-detection/src/erc20/proxy-info-retriever.ts b/packages/payment-detection/src/erc20/proxy-info-retriever.ts new file mode 100644 index 0000000000..b01a3bd4d7 --- /dev/null +++ b/packages/payment-detection/src/erc20/proxy-info-retriever.ts @@ -0,0 +1,94 @@ +import { PaymentTypes } from '@requestnetwork/types'; +import { ethers } from 'ethers'; + +// The ERC20 proxy smart contract ABI fragment containing TransferWithReference event +const erc20proxyContractAbiFragment = [ + 'event TransferWithReference(address tokenAddress,address to,uint256 amount,bytes indexed paymentReference)', +]; + +/** + * Retrieves a list of payment events from a payment reference, a destination address, a token address and a proxy contract + */ +export default class ProxyERC20InfoRetriever + implements PaymentTypes.IPaymentNetworkInfoRetriever { + public contractProxy: ethers.Contract; + public provider: ethers.providers.Provider; + + /** + * @param paymentReference The reference to identify the payment + * @param proxyContractAddress The address of the proxy contract + * @param proxyCreationBlockNumber The block that created the proxy contract + * @param tokenContractAddress The address of the ERC20 contract + * @param toAddress Address of the balance we want to check + * @param eventName Indicate if it is an address for payment or refund + * @param network The Ethereum network to use + */ + constructor( + private paymentReference: string, + private proxyContractAddress: string, + private proxyCreationBlockNumber: number, + private tokenContractAddress: string, + private toAddress: string, + private eventName: PaymentTypes.EVENTS_NAMES, + private network: string, + ) { + // Creates a local or default provider + this.provider = + this.network === 'private' + ? new ethers.providers.JsonRpcProvider() + : ethers.getDefaultProvider(this.network); + + // Setup the ERC20 proxy contract interface + this.contractProxy = new ethers.Contract( + this.proxyContractAddress, + erc20proxyContractAbiFragment, + this.provider, + ); + } + + /** + * Retrieves transfer events for the current contract, address and network. + */ + public async getTransferEvents(): Promise { + // Create a filter to find all the Transfer logs for the toAddress + const filter = this.contractProxy.filters.TransferWithReference( + null, + null, + null, + '0x' + this.paymentReference, + ) as ethers.providers.Filter; + filter.fromBlock = this.proxyCreationBlockNumber; + filter.toBlock = 'latest'; + + // Get the event logs + const logs = await this.provider.getLogs(filter); + + // Parses, filters and creates the events from the logs of the proxy contract + const eventPromises = logs + // Parses the logs + .map(log => { + const parsedLog = this.contractProxy.interface.parseLog(log); + return { parsedLog, log }; + }) + // Keeps only the log with the right token and the right destination address + .filter( + log => + log.parsedLog.values.tokenAddress.toLowerCase() === + this.tokenContractAddress.toLowerCase() && + log.parsedLog.values.to.toLowerCase() === this.toAddress.toLowerCase(), + ) + // Creates the balance events + .map(async t => ({ + amount: t.parsedLog.values.amount.toString(), + name: this.eventName, + parameters: { + block: t.log.blockNumber, + to: this.toAddress, + txHash: t.log.transactionHash, + }, + timestamp: (await this.provider.getBlock(t.log.blockNumber || 0)).timestamp, + })); + + return Promise.all(eventPromises); + } +} diff --git a/packages/payment-detection/src/eth/index.ts b/packages/payment-detection/src/eth/index.ts new file mode 100644 index 0000000000..515d0d45c2 --- /dev/null +++ b/packages/payment-detection/src/eth/index.ts @@ -0,0 +1,3 @@ +import InputData from './input-data'; + +export { InputData }; diff --git a/packages/payment-detection/src/eth/info-retriever.ts b/packages/payment-detection/src/eth/info-retriever.ts new file mode 100644 index 0000000000..d08a79374d --- /dev/null +++ b/packages/payment-detection/src/eth/info-retriever.ts @@ -0,0 +1,57 @@ +import { PaymentTypes } from '@requestnetwork/types'; +import { ethers } from 'ethers'; + +/** + * Gets a list of transfer events for an address and payment reference + */ +export default class ETHInfoRetriever + implements PaymentTypes.IPaymentNetworkInfoRetriever { + /** + * @param address Address to check + * @param eventName Indicate if it is an address for payment or refund + * @param network The id of network we want to check + * @param paymentReference The reference to identify the payment + * @param etherscanApiToken The etherscan API token + */ + constructor( + private toAddress: string, + private eventName: PaymentTypes.EVENTS_NAMES, + private network: string, + private paymentReference: string, + private etherscanApiKey: string = 'TCVQQU5V39TAS1V6HF61P9K7IJZVEHH1D9', + ) {} + + public async getTransferEvents(): Promise { + if (this.network === 'private') { + throw new Error( + 'ETH input data info-retriever works with etherscan and cannot work on a local network', + ); + } + const provider = new ethers.providers.EtherscanProvider(this.network, this.etherscanApiKey); + const history = await provider.getHistory(this.toAddress); + + const events = history + // keep only when address is the destination + .filter( + transaction => + transaction.to && transaction.to.toLowerCase() === this.toAddress.toLowerCase(), + ) + // keep only if data contains the payment reference + .filter( + transaction => + transaction.data.toLowerCase() === '0x' + this.paymentReference.toLowerCase(), + ) + .map(transaction => ({ + amount: transaction.value.toString(), + name: this.eventName, + parameters: { + block: transaction.blockNumber, + confirmations: transaction.confirmations, + txHash: transaction.hash, + }, + timestamp: transaction.timestamp, + })); + + return events; + } +} diff --git a/packages/payment-detection/src/eth/input-data.ts b/packages/payment-detection/src/eth/input-data.ts new file mode 100644 index 0000000000..eb16925316 --- /dev/null +++ b/packages/payment-detection/src/eth/input-data.ts @@ -0,0 +1,244 @@ +import * as SmartContracts from '@requestnetwork/smart-contracts'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; +import getBalanceErrorObject from '../balance-error'; +import PaymentReferenceCalculator from '../payment-reference-calculator'; + +import EthInputDataInfoRetriever from './info-retriever'; +import EthProxyInputDataInfoRetriever from './proxy-info-retriever'; + +const bigNumber: any = require('bn.js'); +const supportedNetworks = ['mainnet', 'rinkeby', 'private']; + +// interface of the object indexing the proxy contract version +interface IProxyContractVersion { + [version: string]: string; +} + +// the versions 0.1.0 and 0.2.0 have the same contracts +const PROXY_CONTRACT_ADDRESS_MAP: IProxyContractVersion = { + ['0.1.0']: '0.1.0', + ['0.2.0']: '0.1.0', +}; + +/** + * Handle payment networks with ETH input data extension + */ +export default class PaymentNetworkETHInputData + implements PaymentTypes.IPaymentNetwork { + private extension: ExtensionTypes.PnReferenceBased.IReferenceBased; + /** + * @param extension The advanced logic payment network extensions + */ + public constructor({ advancedLogic }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic }) { + this.extension = advancedLogic.extensions.ethereumInputData; + } + + /** + * Creates the extensions data for the creation of this extension. + * Will set a salt if none is already given + * + * @param paymentNetworkCreationParameters Parameters to create the extension + * @returns The extensionData object + */ + public async createExtensionsDataForCreation( + paymentNetworkCreationParameters: PaymentTypes.IReferenceBasedCreationParameters, + ): Promise { + // If no salt is given, generate one + const salt = + paymentNetworkCreationParameters.salt || (await Utils.crypto.generate8randomBytes()); + + return this.extension.createCreationAction({ + paymentAddress: paymentNetworkCreationParameters.paymentAddress, + refundAddress: paymentNetworkCreationParameters.refundAddress, + salt, + }); + } + + /** + * Creates the extensions data to add payment address + * + * @param parameters to add payment information + * @returns The extensionData object + */ + public createExtensionsDataForAddPaymentInformation( + parameters: ExtensionTypes.PnReferenceBased.IAddPaymentAddressParameters, + ): ExtensionTypes.IAction { + return this.extension.createAddPaymentAddressAction({ + paymentAddress: parameters.paymentAddress, + }); + } + + /** + * Creates the extensions data to add refund address + * + * @param Parameters to add refund information + * @returns The extensionData object + */ + public createExtensionsDataForAddRefundInformation( + parameters: ExtensionTypes.PnReferenceBased.IAddRefundAddressParameters, + ): ExtensionTypes.IAction { + return this.extension.createAddRefundAddressAction({ + refundAddress: parameters.refundAddress, + }); + } + + /** + * Gets the balance and the payment/refund events + * + * @param request the request to check + * @returns the balance and the payment/refund events + */ + public async getBalance( + request: RequestLogicTypes.IRequest, + ): Promise { + if (!request.currency.network) { + request.currency.network = 'mainnet'; + } + if (!supportedNetworks.includes(request.currency.network)) { + return getBalanceErrorObject( + `Payment network ${ + request.currency.network + } not supported by ETH payment detection. Supported networks: ${supportedNetworks.join( + ', ', + )}`, + PaymentTypes.BALANCE_ERROR_CODE.NETWORK_NOT_SUPPORTED, + ); + } + const paymentNetwork = request.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA]; + + if (!paymentNetwork) { + return getBalanceErrorObject( + `The request does not have the extension: ${ + ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA + }`, + PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION, + ); + } + + try { + const paymentAddress = paymentNetwork.values.paymentAddress; + const refundAddress = paymentNetwork.values.refundAddress; + + let payments: PaymentTypes.ETHBalanceWithEvents = { balance: '0', events: [] }; + if (paymentAddress) { + const paymentReferencePayment = PaymentReferenceCalculator.calculate( + request.requestId, + paymentNetwork.values.salt, + paymentAddress, + ); + payments = await this.extractBalanceAndEvents( + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + request.currency.network, + paymentReferencePayment, + paymentNetwork.version, + ); + } + + let refunds: PaymentTypes.ETHBalanceWithEvents = { balance: '0', events: [] }; + if (refundAddress) { + const paymentReferenceRefund = PaymentReferenceCalculator.calculate( + request.requestId, + paymentNetwork.values.salt, + refundAddress, + ); + refunds = await this.extractBalanceAndEvents( + refundAddress, + PaymentTypes.EVENTS_NAMES.REFUND, + request.currency.network, + paymentReferenceRefund, + paymentNetwork.version, + ); + } + + const balance: string = new bigNumber(payments.balance || 0) + .sub(new bigNumber(refunds.balance || 0)) + .toString(); + + const events: PaymentTypes.ETHPaymentNetworkEvent[] = [ + ...payments.events, + ...refunds.events, + ].sort( + (a: PaymentTypes.ETHPaymentNetworkEvent, b: PaymentTypes.ETHPaymentNetworkEvent) => + (a.timestamp || 0) - (b.timestamp || 0), + ); + + return { + balance, + events, + }; + } catch (error) { + return getBalanceErrorObject(error.message); + } + } + + /** + * Extracts the balance and events of an address + * + * @private + * @param address Address to check + * @param eventName Indicate if it is an address for payment or refund + * @param network The id of network we want to check + * @param paymentReference The reference to identify the payment + * @param paymentNetworkVersion the version of the payment network + * @returns The balance + */ + private async extractBalanceAndEvents( + address: string, + eventName: PaymentTypes.EVENTS_NAMES, + network: string, + paymentReference: string, + paymentNetworkVersion: string, + ): Promise { + const contractVersion = PROXY_CONTRACT_ADDRESS_MAP[paymentNetworkVersion]; + const proxyContractAddress = SmartContracts.ethereumProxyArtifact.getAddress( + network, + contractVersion, + ); + const proxyCreationBlockNumber = SmartContracts.ethereumProxyArtifact.getCreationBlockNumber( + network, + contractVersion, + ); + + const infoRetriever = new EthInputDataInfoRetriever( + address, + eventName, + network, + paymentReference, + ); + + const eventsInputData = await infoRetriever.getTransferEvents(); + + const proxyInfoRetriever = new EthProxyInputDataInfoRetriever( + paymentReference, + proxyContractAddress, + proxyCreationBlockNumber, + address, + eventName, + network, + ); + + const eventsFromProxy = await proxyInfoRetriever.getTransferEvents(); + + const events = eventsInputData + .concat(eventsFromProxy) + .sort( + (a: PaymentTypes.ETHPaymentNetworkEvent, b: PaymentTypes.ETHPaymentNetworkEvent) => + (a.timestamp || 0) - (b.timestamp || 0), + ); + const balance = events + .reduce((acc, event) => acc.add(new bigNumber(event.amount)), new bigNumber(0)) + .toString(); + + return { + balance, + events, + }; + } +} diff --git a/packages/payment-detection/src/eth/proxy-info-retriever.ts b/packages/payment-detection/src/eth/proxy-info-retriever.ts new file mode 100644 index 0000000000..b1a8baa1b4 --- /dev/null +++ b/packages/payment-detection/src/eth/proxy-info-retriever.ts @@ -0,0 +1,85 @@ +import { PaymentTypes } from '@requestnetwork/types'; +import { ethers } from 'ethers'; + +// The Ethereum proxy smart contract ABI fragment containing TransferWithReference event +const ethProxyContractAbiFragment = [ + 'event TransferWithReference(address to,uint256 amount,bytes indexed paymentReference)', +]; + +/** + * Retrieves a list of payment events from a payment reference, a destination address, a token address and a proxy contract + */ +export default class ProxyEthereumInfoRetriever + implements PaymentTypes.IPaymentNetworkInfoRetriever { + public contractProxy: ethers.Contract; + public provider: ethers.providers.Provider; + + /** + * @param paymentReference The reference to identify the payment + * @param proxyContractAddress The address of the proxy contract + * @param proxyCreationBlockNumber The block that created the proxy contract + * @param toAddress Address of the balance we want to check + * @param eventName Indicate if it is an address for payment or refund + * @param network The Ethereum network to use + */ + constructor( + private paymentReference: string, + private proxyContractAddress: string, + private proxyCreationBlockNumber: number, + private toAddress: string, + private eventName: PaymentTypes.EVENTS_NAMES, + private network: string, + ) { + // Creates a local or default provider + this.provider = + this.network === 'private' + ? new ethers.providers.JsonRpcProvider() + : ethers.getDefaultProvider(this.network); + + // Setup the Ethereum proxy contract interface + this.contractProxy = new ethers.Contract( + this.proxyContractAddress, + ethProxyContractAbiFragment, + this.provider, + ); + } + + /** + * Retrieves transfer events for the current contract, address and network. + */ + public async getTransferEvents(): Promise { + // Create a filter to find all the Transfer logs for the toAddress + const filter = this.contractProxy.filters.TransferWithReference( + null, + null, + '0x' + this.paymentReference, + ) as ethers.providers.Filter; + filter.fromBlock = this.proxyCreationBlockNumber; + filter.toBlock = 'latest'; + + // Get the event logs + const logs = await this.provider.getLogs(filter); + + // Parses, filters and creates the events from the logs of the proxy contract + const eventPromises = logs + // Parses the logs + .map(log => { + const parsedLog = this.contractProxy.interface.parseLog(log); + return { parsedLog, log }; + }) + // Keeps only the log with the right token and the right destination address + .filter(log => log.parsedLog.values.to.toLowerCase() === this.toAddress.toLowerCase()) + // Creates the balance events + .map(async t => ({ + amount: t.parsedLog.values.amount.toString(), + name: this.eventName, + parameters: { + block: t.log.blockNumber, + txHash: t.log.transactionHash, + }, + timestamp: (await this.provider.getBlock(t.log.blockNumber || 0)).timestamp, + })); + + return Promise.all(eventPromises); + } +} diff --git a/packages/payment-detection/src/index.ts b/packages/payment-detection/src/index.ts new file mode 100644 index 0000000000..a5603f8181 --- /dev/null +++ b/packages/payment-detection/src/index.ts @@ -0,0 +1,16 @@ +import PaymentNetworkFactory from './payment-network-factory'; +import PaymentReferenceCalculator from './payment-reference-calculator'; + +import * as BtcPaymentNetwork from './btc'; +import DeclarativePaymentNetwork from './declarative'; +import * as Erc20PaymentNetwork from './erc20'; +import * as EthPaymentNetwork from './eth'; + +export { + PaymentNetworkFactory, + PaymentReferenceCalculator, + BtcPaymentNetwork, + DeclarativePaymentNetwork, + Erc20PaymentNetwork, + EthPaymentNetwork, +}; diff --git a/packages/request-client.js/src/api/payment-network/payment-network-factory.ts b/packages/payment-detection/src/payment-network-factory.ts similarity index 69% rename from packages/request-client.js/src/api/payment-network/payment-network-factory.ts rename to packages/payment-detection/src/payment-network-factory.ts index 7e98051406..2935e8b1ce 100644 --- a/packages/request-client.js/src/api/payment-network/payment-network-factory.ts +++ b/packages/payment-detection/src/payment-network-factory.ts @@ -1,36 +1,54 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import * as Types from '../../types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; import BTCAddressedBased from './btc/mainnet-address-based'; import TestnetBTCAddressedBased from './btc/testnet-address-based'; import Declarative from './declarative'; import ERC20AddressBased from './erc20/address-based'; +import ERC20ProxyContract from './erc20/proxy-contract'; +import EthInputData from './eth/input-data'; /** Register the payment network by currency and type */ -// TODO: take into account currency network and possibly value when finding supported network -const supportedPaymentNetwork: Types.ISupportedPaymentNetworkByCurrency = { +const supportedPaymentNetwork: PaymentTypes.ISupportedPaymentNetworkByCurrency = { BTC: { mainnet: { - [ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED as string]: BTCAddressedBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED]: BTCAddressedBased, }, testnet: { - [ExtensionTypes.ID - .PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED as string]: TestnetBTCAddressedBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED]: TestnetBTCAddressedBased, }, }, ERC20: { mainnet: { - [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED as string]: ERC20AddressBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED]: ERC20AddressBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT]: ERC20ProxyContract, }, private: { - [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED as string]: ERC20AddressBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED]: ERC20AddressBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT]: ERC20ProxyContract, }, rinkeby: { - [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED as string]: ERC20AddressBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED]: ERC20AddressBased, + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT]: ERC20ProxyContract, + }, + }, + ETH: { + mainnet: { + [ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA]: EthInputData, + }, + private: { + [ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA]: EthInputData, + }, + rinkeby: { + [ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA]: EthInputData, }, }, }; -const anyCurrencyPaymentNetwork: Types.IPaymentNetworkModuleByType = { +const anyCurrencyPaymentNetwork: PaymentTypes.IPaymentNetworkModuleByType = { [ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE as string]: Declarative, }; @@ -54,9 +72,9 @@ export default class PaymentNetworkFactory { }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic; currency: RequestLogicTypes.ICurrency; - paymentNetworkCreationParameters: Types.IPaymentNetworkCreateParameters; - bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider; - }): Types.IPaymentNetwork { + paymentNetworkCreationParameters: PaymentTypes.IPaymentNetworkCreateParameters; + bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider; + }): PaymentTypes.IPaymentNetwork { const paymentNetworkForCurrency = supportedPaymentNetworksForCurrency(currency); if (!paymentNetworkForCurrency[paymentNetworkCreationParameters.id]) { @@ -90,8 +108,8 @@ export default class PaymentNetworkFactory { }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic; request: RequestLogicTypes.IRequest; - bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider; - }): Types.IPaymentNetwork | null { + bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider; + }): PaymentTypes.IPaymentNetwork | null { const currency = request.currency; const extensionPaymentNetwork = Object.values(request.extensions || {}).find( extension => extension.type === ExtensionTypes.TYPE.PAYMENT_NETWORK, @@ -126,7 +144,7 @@ export default class PaymentNetworkFactory { */ function supportedPaymentNetworksForCurrency( currency: RequestLogicTypes.ICurrency, -): Types.IPaymentNetworkModuleByType { +): PaymentTypes.IPaymentNetworkModuleByType { if (!supportedPaymentNetwork[currency.type]) { return anyCurrencyPaymentNetwork; } diff --git a/packages/payment-detection/src/payment-reference-calculator.ts b/packages/payment-detection/src/payment-reference-calculator.ts new file mode 100644 index 0000000000..bbdda64989 --- /dev/null +++ b/packages/payment-detection/src/payment-reference-calculator.ts @@ -0,0 +1,19 @@ +import Utils from '@requestnetwork/utils'; + +/** + * Compute the payment reference + * + * @param requestId The requestId + * @param salt The salt for the request + * @param address Payment or refund address + */ +function calculate(requestId: string, salt: string, address: string): string { + if (!requestId || !salt || !address) { + throw new Error('RequestId, salt and address are mandatory to calculate the payment reference'); + } + // "The value is the last 8 bytes of a salted hash of the requestId: `last8Bytes(hash(requestId + salt + address))`" + // tslint:disable:no-magic-numbers + return Utils.crypto.keccak256Hash((requestId + salt + address).toLowerCase()).slice(-16); +} + +export default { calculate }; diff --git a/packages/request-client.js/test/api/payment-network/btc/default-bitcoin-detection-provider.test.ts b/packages/payment-detection/test/btc/default-bitcoin-detection-provider.test.ts similarity index 69% rename from packages/request-client.js/test/api/payment-network/btc/default-bitcoin-detection-provider.test.ts rename to packages/payment-detection/test/btc/default-bitcoin-detection-provider.test.ts index 7088122074..c198ac3b05 100644 --- a/packages/request-client.js/test/api/payment-network/btc/default-bitcoin-detection-provider.test.ts +++ b/packages/payment-detection/test/btc/default-bitcoin-detection-provider.test.ts @@ -7,33 +7,33 @@ const expect = chai.expect; chai.use(spies); chai.use(chaiAsPromised); -import * as Types from '../../../../src/types'; +import { PaymentTypes } from '@requestnetwork/types'; -import DefaultBitcoinDetectionProvider from '../../../../src/api/payment-network/btc/default-bitcoin-detection-provider'; +import DefaultBitcoinDetectionProvider from '../../src/btc/default-bitcoin-detection-provider'; -const btcProviderMock0: Types.IBitcoinDetectionProvider = { - getAddressBalanceWithEvents: async (): Promise => ({ +const btcProviderMock0: PaymentTypes.IBitcoinDetectionProvider = { + getAddressBalanceWithEvents: async (): Promise => ({ balance: '0', events: [], }), }; -const btcProviderMock1: Types.IBitcoinDetectionProvider = { - getAddressBalanceWithEvents: async (): Promise => ({ +const btcProviderMock1: PaymentTypes.IBitcoinDetectionProvider = { + getAddressBalanceWithEvents: async (): Promise => ({ balance: '1', events: [], }), }; -const btcProviderMock2: Types.IBitcoinDetectionProvider = { - getAddressBalanceWithEvents: async (): Promise => ({ +const btcProviderMock2: PaymentTypes.IBitcoinDetectionProvider = { + getAddressBalanceWithEvents: async (): Promise => ({ balance: '2', events: [], }), }; -const btcProviderMockMinus1: Types.IBitcoinDetectionProvider = { - getAddressBalanceWithEvents: async (): Promise => ({ +const btcProviderMockMinus1: PaymentTypes.IBitcoinDetectionProvider = { + getAddressBalanceWithEvents: async (): Promise => ({ balance: '-1', events: [], }), @@ -48,7 +48,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcRetriever.providers = [btcProviderMock1, btcProviderMock1]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); }); @@ -57,12 +57,12 @@ describe('api/btc/bitcoin-info-retriever', () => { btcRetriever.providers = [btcProviderMockMinus1, btcProviderMock1, btcProviderMock1]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [btcProviderMock1, btcProviderMockMinus1, btcProviderMock1]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); }); @@ -71,12 +71,12 @@ describe('api/btc/bitcoin-info-retriever', () => { btcRetriever.providers = [btcProviderMock0, btcProviderMock1, btcProviderMock1]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [btcProviderMock1, btcProviderMock0, btcProviderMock1]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); }); @@ -90,7 +90,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [ @@ -100,7 +100,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [ @@ -110,7 +110,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [ @@ -120,7 +120,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); }); @@ -134,7 +134,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [ @@ -144,7 +144,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); btcRetriever.providers = [ @@ -154,7 +154,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcProviderMock1, ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.be.deep.equal({ balance: '1', events: [] }); }); @@ -163,7 +163,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcRetriever.providers = [btcProviderMockMinus1, btcProviderMockMinus1]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.rejectedWith('Error getting the balance from the bitcoin providers'); }); @@ -172,7 +172,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcRetriever.providers = [btcProviderMock1, btcProviderMock0]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.rejectedWith('Error getting the balance from the bitcoin providers'); }); @@ -181,7 +181,7 @@ describe('api/btc/bitcoin-info-retriever', () => { btcRetriever.providers = [btcProviderMock1, btcProviderMock2, btcProviderMock0]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.rejectedWith('Error getting the balance from the bitcoin providers'); }); @@ -194,7 +194,7 @@ describe('api/btc/bitcoin-info-retriever', () => { ]; await expect( - btcRetriever.getAddressBalanceWithEvents(0, 'address', Types.EVENTS_NAMES.PAYMENT), + btcRetriever.getAddressBalanceWithEvents(0, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), ).to.eventually.rejectedWith('Error getting the balance from the bitcoin providers'); }); }); diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockchain-info-data.ts b/packages/payment-detection/test/btc/default-providers/blockchain-info-data.ts similarity index 100% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/blockchain-info-data.ts rename to packages/payment-detection/test/btc/default-providers/blockchain-info-data.ts diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockchain-info.test.ts b/packages/payment-detection/test/btc/default-providers/blockchain-info.test.ts similarity index 63% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/blockchain-info.test.ts rename to packages/payment-detection/test/btc/default-providers/blockchain-info.test.ts index f5daeb0e5b..ab7281164f 100644 --- a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockchain-info.test.ts +++ b/packages/payment-detection/test/btc/default-providers/blockchain-info.test.ts @@ -1,10 +1,14 @@ -import * as Types from '../../../../../src/types'; - -import BlockchainInfo from '../../../../../src/api/payment-network/btc/default-providers/blockchain-info'; +import { PaymentTypes } from '@requestnetwork/types'; +import BlockchainInfo from '../../../src/btc/default-providers/blockchain-info'; import * as BlockchainInfoData from './blockchain-info-data'; -import { expect } from 'chai'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + import 'mocha'; // Most of the tests are done as integration tests in ../index.test.ts @@ -13,14 +17,11 @@ describe('api/btc/default-providers/blockchainInfo', () => { describe('getAddressInfo', () => { it('must throw if bitcoinNetworkId is not 0 or 3', async () => { const blockchainInfo = new BlockchainInfo(); - try { - await blockchainInfo.getAddressBalanceWithEvents(1, 'address', Types.EVENTS_NAMES.PAYMENT); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', - ); - } + expect( + blockchainInfo.getAddressBalanceWithEvents(1, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), + ).to.eventually.be.rejectedWith( + 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', + ); }); }); @@ -30,27 +31,27 @@ describe('api/btc/default-providers/blockchainInfo', () => { const parsedData = blockchainInfo.parse( BlockchainInfoData.exampleAddressInfo, - Types.EVENTS_NAMES.PAYMENT, + PaymentTypes.EVENTS_NAMES.PAYMENT, ); expect(parsedData.balance, 'balance wrong').to.equal('50500000'); expect(parsedData.events, 'balance wrong').to.deep.equal([ { + amount: '500000', name: 'payment', parameters: { - amount: '500000', block: 1354204, - timestamp: 1531879904, txHash: '2a14f1ad2dfa4601bdc7a6be325241bbdc2ae99d05f096357fda76264b1c5c26', }, + timestamp: 1531879904, }, { + amount: '50000000', name: 'payment', parameters: { - amount: '50000000', block: 1354075, - timestamp: 1531817766, txHash: '7d84924c034798dedcc95f479c9cdb24fe014437f7ce0ee0c2f4bf3580e017d8', }, + timestamp: 1531817766, }, ]); }); diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockcypher-com-data.ts b/packages/payment-detection/test/btc/default-providers/blockcypher-com-data.ts similarity index 100% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/blockcypher-com-data.ts rename to packages/payment-detection/test/btc/default-providers/blockcypher-com-data.ts diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockcypher-com.test.ts b/packages/payment-detection/test/btc/default-providers/blockcypher-com.test.ts similarity index 60% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/blockcypher-com.test.ts rename to packages/payment-detection/test/btc/default-providers/blockcypher-com.test.ts index 5dd1728c75..cea5d8eb4f 100644 --- a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockcypher-com.test.ts +++ b/packages/payment-detection/test/btc/default-providers/blockcypher-com.test.ts @@ -1,10 +1,14 @@ -import * as Types from '../../../../../src/types'; - -import BlockCypherCom from '../../../../../src/api/payment-network/btc/default-providers/blockcypher-com'; +import { PaymentTypes } from '@requestnetwork/types'; +import BlockCypherCom from '../../../src/btc/default-providers/blockcypher-com'; import * as BlockCypherComData from './blockcypher-com-data'; -import { expect } from 'chai'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + import 'mocha'; // Most of the tests are done as integration tests in ../index.test.ts @@ -12,15 +16,12 @@ import 'mocha'; describe('api/btc/providers/blockCypherCom', () => { describe('getAddressInfo', () => { it('must throw if bitcoinNetworkId is not 0 or 3', async () => { - try { - const blockCypherCom = new BlockCypherCom(); - await blockCypherCom.getAddressBalanceWithEvents(1, 'address', Types.EVENTS_NAMES.PAYMENT); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', - ); - } + const blockCypherCom = new BlockCypherCom(); + expect( + blockCypherCom.getAddressBalanceWithEvents(1, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), + ).to.eventually.be.rejectedWith( + 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', + ); }); }); @@ -29,25 +30,25 @@ describe('api/btc/providers/blockCypherCom', () => { const blockCypherCom = new BlockCypherCom(); const parsedData = blockCypherCom.parse( BlockCypherComData.exampleAddressInfo, - Types.EVENTS_NAMES.PAYMENT, + PaymentTypes.EVENTS_NAMES.PAYMENT, ); expect(parsedData.balance, 'balance wrong').to.equal('50500000'); expect(parsedData.events, 'balance wrong').to.deep.equal([ { + amount: '500000', name: 'payment', + // timestamp: 1531879904, parameters: { - amount: '500000', block: 1354204, - // timestamp: 1531879904, txHash: '2a14f1ad2dfa4601bdc7a6be325241bbdc2ae99d05f096357fda76264b1c5c26', }, }, { + amount: '50000000', name: 'payment', + // timestamp: 1531817766, parameters: { - amount: '50000000', block: 1354075, - // timestamp: 1531817766, txHash: '7d84924c034798dedcc95f479c9cdb24fe014437f7ce0ee0c2f4bf3580e017d8', }, }, diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockstream-info-data.ts b/packages/payment-detection/test/btc/default-providers/blockstream-info-data.ts similarity index 100% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/blockstream-info-data.ts rename to packages/payment-detection/test/btc/default-providers/blockstream-info-data.ts diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockstream-info.test.ts b/packages/payment-detection/test/btc/default-providers/blockstream-info.test.ts similarity index 60% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/blockstream-info.test.ts rename to packages/payment-detection/test/btc/default-providers/blockstream-info.test.ts index 9d03d1f85e..26e8013b10 100644 --- a/packages/request-client.js/test/api/payment-network/btc/default-providers/blockstream-info.test.ts +++ b/packages/payment-detection/test/btc/default-providers/blockstream-info.test.ts @@ -1,10 +1,15 @@ -import * as Types from '../../../../../src/types'; +import { PaymentTypes } from '@requestnetwork/types'; -import Blockstream from '../../../../../src/api/payment-network/btc/default-providers/blockstream-info'; +import Blockstream from '../../../src/btc/default-providers/blockstream-info'; import * as BlockstreamData from './blockstream-info-data'; -import { expect } from 'chai'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + import 'mocha'; // Most of the tests are done as integration tests in ../index.test.ts @@ -12,15 +17,16 @@ import 'mocha'; describe('api/btc/providers/blockstream', () => { describe('getAddressInfo', () => { it('must throw if bitcoinNetworkId is not 0 or 3', async () => { - try { - const blockstreamData = new Blockstream(); - await blockstreamData.getAddressBalanceWithEvents(1, 'address', Types.EVENTS_NAMES.PAYMENT); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', - ); - } + const blockstreamData = new Blockstream(); + expect( + blockstreamData.getAddressBalanceWithEvents( + 1, + 'address', + PaymentTypes.EVENTS_NAMES.PAYMENT, + ), + ).to.eventually.be.rejectedWith( + 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', + ); }); }); @@ -29,28 +35,28 @@ describe('api/btc/providers/blockstream', () => { const blockstreamData = new Blockstream(); const parsedData = blockstreamData.parse( { txs: BlockstreamData.exampleAddressInfo, address: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v' }, - Types.EVENTS_NAMES.PAYMENT, + PaymentTypes.EVENTS_NAMES.PAYMENT, ); expect(parsedData.balance, 'balance wrong').to.equal('50500000'); expect(parsedData.events, 'events wrong').to.deep.equal([ { + amount: '500000', name: 'payment', parameters: { - amount: '500000', block: 1354204, - timestamp: 1531880048, txHash: '2a14f1ad2dfa4601bdc7a6be325241bbdc2ae99d05f096357fda76264b1c5c26', }, + timestamp: 1531880048, }, { + amount: '50000000', name: 'payment', parameters: { - amount: '50000000', block: 1354075, - timestamp: 1531818367, txHash: '7d84924c034798dedcc95f479c9cdb24fe014437f7ce0ee0c2f4bf3580e017d8', }, + timestamp: 1531818367, }, ]); }); diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/chain-so-data.ts b/packages/payment-detection/test/btc/default-providers/chain-so-data.ts similarity index 100% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/chain-so-data.ts rename to packages/payment-detection/test/btc/default-providers/chain-so-data.ts diff --git a/packages/request-client.js/test/api/payment-network/btc/default-providers/chain-so.test.ts b/packages/payment-detection/test/btc/default-providers/chain-so.test.ts similarity index 56% rename from packages/request-client.js/test/api/payment-network/btc/default-providers/chain-so.test.ts rename to packages/payment-detection/test/btc/default-providers/chain-so.test.ts index f0f633061e..02abd3a068 100644 --- a/packages/request-client.js/test/api/payment-network/btc/default-providers/chain-so.test.ts +++ b/packages/payment-detection/test/btc/default-providers/chain-so.test.ts @@ -1,10 +1,15 @@ -import * as Types from '../../../../../src/types'; +import { PaymentTypes } from '@requestnetwork/types'; -import ChainSo from '../../../../../src/api/payment-network/btc/default-providers/chain-so'; +import ChainSo from '../../../src/btc/default-providers/chain-so'; import * as ChainSoData from './chain-so-data'; -import { expect } from 'chai'; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + import 'mocha'; // Most of the tests are done as integration tests in ../index.test.ts @@ -12,41 +17,41 @@ import 'mocha'; describe('api/btc/providers/chainSo', () => { describe('getAddressInfo', () => { it('must throw if bitcoinNetworkId is not 0 or 3', async () => { - try { - const chainSo = new ChainSo(); - await chainSo.getAddressBalanceWithEvents(1, 'address', Types.EVENTS_NAMES.PAYMENT); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', - ); - } + const chainSo = new ChainSo(); + expect( + chainSo.getAddressBalanceWithEvents(1, 'address', PaymentTypes.EVENTS_NAMES.PAYMENT), + ).to.eventually.be.rejectedWith( + 'Invalid network 0 (mainnet) or 3 (testnet) was expected but 1 was given', + ); }); }); describe('parse', () => { it('can parse data', () => { const chainSo = new ChainSo(); - const parsedData = chainSo.parse(ChainSoData.exampleAddressInfo, Types.EVENTS_NAMES.PAYMENT); + const parsedData = chainSo.parse( + ChainSoData.exampleAddressInfo, + PaymentTypes.EVENTS_NAMES.PAYMENT, + ); expect(parsedData.balance, 'balance wrong').to.equal('50500000'); expect(parsedData.events, 'balance wrong').to.deep.equal([ { + amount: '500000', name: 'payment', parameters: { - amount: '500000', block: 1354204, - timestamp: 1531879904, txHash: '2a14f1ad2dfa4601bdc7a6be325241bbdc2ae99d05f096357fda76264b1c5c26', }, + timestamp: 1531879904, }, { + amount: '50000000', name: 'payment', parameters: { - amount: '50000000', block: 1354075, - timestamp: 1531817766, txHash: '7d84924c034798dedcc95f479c9cdb24fe014437f7ce0ee0c2f4bf3580e017d8', }, + timestamp: 1531817766, }, ]); }); diff --git a/packages/request-client.js/test/api/payment-network/btc/mainnet-address-based.test.ts b/packages/payment-detection/test/btc/mainnet-address-based.test.ts similarity index 73% rename from packages/request-client.js/test/api/payment-network/btc/mainnet-address-based.test.ts rename to packages/payment-detection/test/btc/mainnet-address-based.test.ts index 67df54333e..209cb781c1 100644 --- a/packages/request-client.js/test/api/payment-network/btc/mainnet-address-based.test.ts +++ b/packages/payment-detection/test/btc/mainnet-address-based.test.ts @@ -1,6 +1,6 @@ -import { AdvancedLogicTypes } from '@requestnetwork/types'; +import { AdvancedLogicTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; -import BTCAddressedBased from '../../../../src/api/payment-network/btc/mainnet-address-based'; +import BTCAddressedBased from '../../src/btc/mainnet-address-based'; import 'chai'; import 'mocha'; @@ -43,7 +43,7 @@ describe('api/btc/mainnet-address-based', () => { it('can createExtensionsDataForCreation', async () => { const spy = sandbox.on(mockAdvancedLogic.extensions.addressBasedBtc, 'createCreationAction'); - btcAddressedBased.createExtensionsDataForCreation({ paymentAddress: 'address bitcoin' }); + await btcAddressedBased.createExtensionsDataForCreation({ paymentAddress: 'address bitcoin' }); expect(spy).to.have.been.called.once; }); @@ -73,4 +73,17 @@ describe('api/btc/mainnet-address-based', () => { expect(spy).to.have.been.called.once; }); + + it('should not throw when getBalance fail', async () => { + expect( + await btcAddressedBased.getBalance({ extensions: {} } as RequestLogicTypes.IRequest), + ).to.deep.equal({ + balance: null, + error: { + code: PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION, + message: 'The request does not have the extension: pn-bitcoin-address-based', + }, + events: [], + }); + }); }); diff --git a/packages/request-client.js/test/api/payment-network/btc/testnet-address-based.test.ts b/packages/payment-detection/test/btc/testnet-address-based.test.ts similarity index 73% rename from packages/request-client.js/test/api/payment-network/btc/testnet-address-based.test.ts rename to packages/payment-detection/test/btc/testnet-address-based.test.ts index 89d518fad9..b80936af32 100644 --- a/packages/request-client.js/test/api/payment-network/btc/testnet-address-based.test.ts +++ b/packages/payment-detection/test/btc/testnet-address-based.test.ts @@ -1,6 +1,6 @@ -import { AdvancedLogicTypes } from '@requestnetwork/types'; +import { AdvancedLogicTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; -import AddressBasedTestnetBtc from '../../../../src/api/payment-network/btc/testnet-address-based'; +import AddressBasedTestnetBtc from '../../src/btc/testnet-address-based'; import 'chai'; import 'mocha'; @@ -46,7 +46,7 @@ describe('api/btc/testnet-address-based', () => { 'createCreationAction', ); - btcAddressedBased.createExtensionsDataForCreation({ paymentAddress: 'address bitcoin' }); + await btcAddressedBased.createExtensionsDataForCreation({ paymentAddress: 'address bitcoin' }); expect(spy).to.have.been.called.once; }); @@ -76,4 +76,17 @@ describe('api/btc/testnet-address-based', () => { expect(spy).to.have.been.called.once; }); + + it('should not throw when getBalance fail', async () => { + expect( + await btcAddressedBased.getBalance({ extensions: {} } as RequestLogicTypes.IRequest), + ).to.deep.equal({ + balance: null, + error: { + code: PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION, + message: 'The request does not have the extension: pn-testnet-bitcoin-address-based', + }, + events: [], + }); + }); }); diff --git a/packages/request-client.js/test/api/payment-network/declarative.test.ts b/packages/payment-detection/test/declarative.test.ts similarity index 88% rename from packages/request-client.js/test/api/payment-network/declarative.test.ts rename to packages/payment-detection/test/declarative.test.ts index bded6776ba..0cd7645d03 100644 --- a/packages/request-client.js/test/api/payment-network/declarative.test.ts +++ b/packages/payment-detection/test/declarative.test.ts @@ -1,8 +1,12 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + IdentityTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; -import Declarative from '../../../src/api/payment-network/declarative'; - -import * as Types from '../../../src/types'; +import Declarative from '../src/declarative'; import 'chai'; import 'mocha'; @@ -50,7 +54,7 @@ const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { const requestMock: RequestLogicTypes.IRequest = { creator: { - type: Types.Identity.TYPE.ETHEREUM_ADDRESS, + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '', }, currency: { @@ -58,7 +62,7 @@ const requestMock: RequestLogicTypes.IRequest = { value: 'ETH', }, events: [], - expectedAmount: {}, + expectedAmount: '', extensions: {}, extensionsData: [], requestId: '', @@ -78,7 +82,7 @@ describe('api/declarative', () => { it('can createExtensionsDataForCreation', async () => { const spy = sandbox.on(mockAdvancedLogic.extensions.declarative, 'createCreationAction'); - declarative.createExtensionsDataForCreation({ + await declarative.createExtensionsDataForCreation({ paymentInfo: 'payment instruction', refundInfo: 'refund instruction', }); @@ -161,7 +165,7 @@ describe('api/declarative', () => { }); it('getBalance get the correct balance', async () => { - requestMock.extensions[Types.PAYMENT_NETWORK_ID.DECLARATIVE] = { + requestMock.extensions[PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE] = { events: [ { name: ExtensionTypes.PnAnyDeclarative.ACTION.DECLARE_SENT_PAYMENT, @@ -214,36 +218,36 @@ describe('api/declarative', () => { balance: '1200', // 1000 + 500 - 100 - 200 events: [ { - name: Types.EVENTS_NAMES.PAYMENT, + amount: '1000', + name: PaymentTypes.EVENTS_NAMES.PAYMENT, parameters: { - amount: '1000', note: 'first payment', - timestamp: 10, }, + timestamp: 10, }, { - name: Types.EVENTS_NAMES.PAYMENT, + amount: '500', + name: PaymentTypes.EVENTS_NAMES.PAYMENT, parameters: { - amount: '500', note: 'second payment', - timestamp: 15, }, + timestamp: 15, }, { - name: Types.EVENTS_NAMES.REFUND, + amount: '100', + name: PaymentTypes.EVENTS_NAMES.REFUND, parameters: { - amount: '100', note: 'first refund', - timestamp: 20, }, + timestamp: 20, }, { - name: Types.EVENTS_NAMES.REFUND, + amount: '200', + name: PaymentTypes.EVENTS_NAMES.REFUND, parameters: { - amount: '200', note: 'second refund', - timestamp: 25, }, + timestamp: 25, }, ], }); diff --git a/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts b/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts new file mode 100644 index 0000000000..2c2d3a5774 --- /dev/null +++ b/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts @@ -0,0 +1,53 @@ +// tslint:disable: no-invalid-this +// tslint:disable: no-magic-numbers +import { PaymentTypes } from '@requestnetwork/types'; +import ERC20InfoRetriever from '../../src/erc20/address-based-info-retriever'; + +import 'chai'; +import 'mocha'; + +const chai = require('chai'); +const expect = chai.expect; + +const erc20LocalhostContractAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40'; + +/* tslint:disable:no-unused-expression */ +describe('api/erc20/address-based-info-retriever', () => { + describe('on localhost', () => { + const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; + const payerAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; + const emptyAddress = '0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef'; + + it('can get the localhost balance of an address', async () => { + const infoRetriever = new ERC20InfoRetriever( + erc20LocalhostContractAddress, + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + ); + const events = await infoRetriever.getTransferEvents(); + + // if this assert fails it means this address received another transaction + expect(events).to.have.lengthOf(1); + expect(events[0].name).to.equal(PaymentTypes.EVENTS_NAMES.PAYMENT); + expect(events[0].amount).to.equal('10'); + expect(events[0].timestamp).to.be.a('number'); + expect(events[0].parameters!.from).to.equal(payerAddress); + expect(events[0].parameters!.to).to.equal(paymentAddress); + expect(events[0].parameters!.block).to.be.a('number'); + expect(events[0].parameters!.txHash).to.be.a('string'); + }); + + it('gets an empty list of events for an address without ERC20 on localhost', async () => { + const infoRetriever = new ERC20InfoRetriever( + erc20LocalhostContractAddress, + emptyAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + ); + + const events = await infoRetriever.getTransferEvents(); + expect(events).to.be.empty; + }); + }); +}); diff --git a/packages/request-client.js/test/api/payment-network/erc20/address-based.test.ts b/packages/payment-detection/test/erc20/address-based.test.ts similarity index 70% rename from packages/request-client.js/test/api/payment-network/erc20/address-based.test.ts rename to packages/payment-detection/test/erc20/address-based.test.ts index 3cbce06a00..9809c91795 100644 --- a/packages/request-client.js/test/api/payment-network/erc20/address-based.test.ts +++ b/packages/payment-detection/test/erc20/address-based.test.ts @@ -1,6 +1,10 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import ERC20AddressedBased from '../../../../src/api/payment-network/erc20/address-based'; -import * as Types from '../../../../src/types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import ERC20AddressedBased from '../../src/erc20/address-based'; import 'chai'; import 'mocha'; @@ -43,7 +47,9 @@ describe('api/erc20/address-based', () => { it('can createExtensionsDataForCreation', async () => { const spy = sandbox.on(mockAdvancedLogic.extensions.addressBasedErc20, 'createCreationAction'); - erc20AddressedBased.createExtensionsDataForCreation({ paymentAddress: 'ethereum address' }); + await erc20AddressedBased.createExtensionsDataForCreation({ + paymentAddress: 'ethereum address', + }); expect(spy).to.have.been.called.once; }); @@ -106,13 +112,32 @@ describe('api/erc20/address-based', () => { expect(balance.balance).to.be.equal('10'); expect(balance.events).to.have.lengthOf(1); - expect(balance.events[0].name).to.be.equal(Types.EVENTS_NAMES.PAYMENT); - expect(balance.events[0].parameters.to).to.be.equal( + expect(balance.events[0].name).to.be.equal(PaymentTypes.EVENTS_NAMES.PAYMENT); + expect(balance.events[0].amount).to.be.equal('10'); + expect(balance.events[0].timestamp).to.be.a('number'); + expect(balance.events[0].parameters!.to).to.be.equal( '0xf17f52151EbEF6C7334FAD080c5704D77216b732', ); - expect(balance.events[0].parameters.from).to.be.equal( + expect(balance.events[0].parameters!.from).to.be.equal( '0x627306090abaB3A6e1400e9345bC60c78a8BEf57', ); - expect(balance.events[0].parameters.value).to.be.equal('10'); + expect(balance.events[0].parameters!.block).to.be.a('number'); + expect(balance.events[0].parameters!.txHash).to.be.a('string'); + }); + + it('should not throw when getBalance fail', async () => { + expect( + await erc20AddressedBased.getBalance({ + currency: { network: 'wrong' }, + } as RequestLogicTypes.IRequest), + ).to.deep.equal({ + balance: null, + error: { + code: PaymentTypes.BALANCE_ERROR_CODE.NETWORK_NOT_SUPPORTED, + message: + 'Payment network wrong not supported by ERC20 payment detection. Supported networks: mainnet, rinkeby, private', + }, + events: [], + }); }); }); diff --git a/packages/payment-detection/test/erc20/proxy-contract.test.ts b/packages/payment-detection/test/erc20/proxy-contract.test.ts new file mode 100644 index 0000000000..3b8a9955fd --- /dev/null +++ b/packages/payment-detection/test/erc20/proxy-contract.test.ts @@ -0,0 +1,108 @@ +import { AdvancedLogicTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import ERC20ProxyContract from '../../src/erc20/proxy-contract'; + +import * as chai from 'chai'; +import * as spies from 'chai-spies'; +import 'mocha'; + +const expect = chai.expect; +chai.use(spies); +const sandbox = chai.spy.sandbox(); + +let erc20ProxyContract: ERC20ProxyContract; + +const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { + applyActionToExtensions(): any { + return; + }, + extensions: { + proxyContractErc20: { + createAddPaymentAddressAction(): any { + return; + }, + createAddRefundAddressAction(): any { + return; + }, + createCreationAction(): any { + return; + }, + }, + }, +}; + +/* tslint:disable:no-unused-expression */ +describe('api/erc20/proxy-contract', () => { + beforeEach(() => { + sandbox.restore(); + erc20ProxyContract = new ERC20ProxyContract({ advancedLogic: mockAdvancedLogic }); + }); + + it('can createExtensionsDataForCreation', async () => { + const spy = sandbox.on(mockAdvancedLogic.extensions.proxyContractErc20, 'createCreationAction'); + + await erc20ProxyContract.createExtensionsDataForCreation({ + paymentAddress: 'ethereum address', + salt: 'ea3bc7caf64110ca', + }); + + expect(spy).to.have.been.called.with({ + paymentAddress: 'ethereum address', + refundAddress: undefined, + salt: 'ea3bc7caf64110ca', + }); + }); + + it('can createExtensionsDataForCreation without salt', async () => { + const spy = sandbox.on(mockAdvancedLogic.extensions.proxyContractErc20, 'createCreationAction'); + + await erc20ProxyContract.createExtensionsDataForCreation({ + paymentAddress: 'ethereum address', + }); + + // Can't check parameters since salt is generated in createExtensionsDataForCreation + expect(spy).to.have.been.called; + }); + + it('can createExtensionsDataForAddPaymentInformation', async () => { + const spy = sandbox.on( + mockAdvancedLogic.extensions.proxyContractErc20, + 'createAddPaymentAddressAction', + ); + + erc20ProxyContract.createExtensionsDataForAddPaymentInformation({ + paymentAddress: 'ethereum address', + }); + + expect(spy).to.have.been.called.with({ + paymentAddress: 'ethereum address', + }); + }); + + it('can createExtensionsDataForAddRefundInformation', async () => { + const spy = sandbox.on( + mockAdvancedLogic.extensions.proxyContractErc20, + 'createAddRefundAddressAction', + ); + + erc20ProxyContract.createExtensionsDataForAddRefundInformation({ + refundAddress: 'ethereum address', + }); + + expect(spy).to.have.been.called.with({ + refundAddress: 'ethereum address', + }); + }); + + it('should not throw when getBalance fail', async () => { + expect( + await erc20ProxyContract.getBalance({ extensions: {} } as RequestLogicTypes.IRequest), + ).to.deep.equal({ + balance: null, + error: { + code: PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION, + message: 'The request does not have the extension : pn-erc20-proxy-contract', + }, + events: [], + }); + }); +}); diff --git a/packages/payment-detection/test/erc20/proxy-info-retriever.test.ts b/packages/payment-detection/test/erc20/proxy-info-retriever.test.ts new file mode 100644 index 0000000000..d4b054cf9f --- /dev/null +++ b/packages/payment-detection/test/erc20/proxy-info-retriever.test.ts @@ -0,0 +1,92 @@ +// tslint:disable: no-invalid-this +// tslint:disable: no-magic-numbers +import { PaymentTypes } from '@requestnetwork/types'; +import ProxyERC20InfoRetriever from '../../src/erc20/proxy-info-retriever'; + +import 'chai'; +import 'mocha'; + +const chai = require('chai'); +const expect = chai.expect; + +const erc20LocalhostContractAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40'; +const proxyContractAddress = '0x2C2B9C9a4a25e24B174f26114e8926a9f2128FE4'; +const paymentReferenceMock = '0111111111111111111111111111111111111111111111111'; + +/* tslint:disable:no-unused-expression */ +describe('api/erc20/proxy-info-retriever', () => { + describe('on localhost', () => { + const paymentAddress = '0xf17f52151ebef6c7334fad080c5704d77216b732'; + + it('can get the localhost balance of an address', async () => { + const infoRetriever = new ProxyERC20InfoRetriever( + paymentReferenceMock, + proxyContractAddress, + 0, + erc20LocalhostContractAddress, + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + ); + + // inject mock provider.getLogs() + infoRetriever.provider.getLogs = (): any => { + return [ + { + address: proxyContractAddress, + blockHash: '0xe8693d0fb897eac7c350696834f4cf36be3b7d86f746d6764d9903a06dc5fe44', + blockNumber: 115, + data: `0x000000000000000000000000${erc20LocalhostContractAddress.slice( + 2, + )}000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000001`, + logIndex: 2, + topics: [ + '0x4d4e9dbb7207a31cc25529ccb52e52ddba30ded7734e32691d13da66df7f81da', + '0x6330b989705733cc5c1f7285b8a5b892e08be86ed6fbe9d254713a4277bc5bd2', + ], + transactionHash: '0x66e20126e917ede3543c79357c71ddeda2af68e27d684fd5aa6a813bb6946ce4', + transactionIndex: 0, + }, + ]; + }; + + // inject mock provider.getBlock() + infoRetriever.provider.getBlock = (): any => { + return { + timestamp: 69, + }; + }; + + const events = await infoRetriever.getTransferEvents(); + + // if this assert fails it means this address received another transaction + expect(events).to.have.lengthOf(1); + expect(events[0].name).to.equal(PaymentTypes.EVENTS_NAMES.PAYMENT); + expect(events[0].amount).to.equal('1'); + expect(events[0].timestamp).to.be.a('number'); + expect(events[0].parameters!.to).to.equal(paymentAddress); + expect(events[0].parameters!.block).to.be.a('number'); + expect(events[0].parameters!.txHash).to.be.a('string'); + }); + + it('gets an empty list of events for an address without ERC20 on localhost', async () => { + const infoRetriever = new ProxyERC20InfoRetriever( + paymentReferenceMock, + proxyContractAddress, + 0, + erc20LocalhostContractAddress, + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + ); + + // inject mock provider.getLogs() + infoRetriever.provider.getLogs = (): any => { + return []; + }; + + const events = await infoRetriever.getTransferEvents(); + expect(events).to.be.empty; + }); + }); +}); diff --git a/packages/payment-detection/test/eth/info-retriever.test.ts b/packages/payment-detection/test/eth/info-retriever.test.ts new file mode 100644 index 0000000000..1eaffe616b --- /dev/null +++ b/packages/payment-detection/test/eth/info-retriever.test.ts @@ -0,0 +1,50 @@ +import { PaymentTypes } from '@requestnetwork/types'; +import EthInfoRetriever from '../../src/eth/info-retriever'; +import PaymentReferenceCalculator from '../../src/payment-reference-calculator'; + +import { expect } from 'chai'; +import 'mocha'; + +describe('api/eth/info-retriever', () => { + // In this test, we're looking this transaction: + // https://etherscan.io/tx/0x0de1759d8b246939e370e1d0509e3ed6f1d5f4f5b79735636c0283b64ff6f5ed + it('can get the balance of an address', async () => { + const paymentAddress = '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB'; + const paymentReference = PaymentReferenceCalculator.calculate( + '01000', + '1234567890123456', + paymentAddress, + ); // 9649a1a4dd5854ed + + const infoRetriever = new EthInfoRetriever( + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'mainnet', + paymentReference, + ); + const events = await infoRetriever.getTransferEvents(); + + // If this assertion fails, another transaction with the data `9649a1a4dd5854ed` + // has been set to the address `0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB` + expect(events).to.have.lengthOf(1); + + expect(events[0].name).to.equal('payment'); + expect(events[0].amount).to.equal('33'); + expect(events[0].timestamp).to.be.a('number'); + expect(events[0].parameters!.txHash).to.equal( + '0x0b53c5296a7b286fef52336529f3934584fea116725d1fe4c59552e926229059', + ); + expect(events[0].parameters!.block).to.be.a('number'); + expect(events[0].parameters!.confirmations).to.be.a('number'); + }); + + it('throws when trying to use it in local', async () => { + const infoRetreiver = new EthInfoRetriever( + '0x01', + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + '12345', + ); + expect(infoRetreiver.getTransferEvents()).to.be.rejectedWith(Error); + }); +}); diff --git a/packages/payment-detection/test/eth/input-data.test.ts b/packages/payment-detection/test/eth/input-data.test.ts new file mode 100644 index 0000000000..d520c220dd --- /dev/null +++ b/packages/payment-detection/test/eth/input-data.test.ts @@ -0,0 +1,141 @@ +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import EthInputData from '../../src/eth/input-data'; + +import 'chai'; +import 'mocha'; + +const chai = require('chai'); +const spies = require('chai-spies'); +const expect = chai.expect; +chai.use(spies); +const sandbox = chai.spy.sandbox(); + +let ethInputData: EthInputData; + +const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { + applyActionToExtensions(): any { + return; + }, + extensions: { + ethereumInputData: { + createAddPaymentAddressAction(): any { + return; + }, + createAddRefundAddressAction(): any { + return; + }, + createCreationAction(): any { + return; + }, + }, + }, +}; + +// Most of the tests are done as integration tests in ../index.test.ts +/* tslint:disable:no-unused-expression */ +describe('api/eth/input-data', () => { + beforeEach(() => { + sandbox.restore(); + ethInputData = new EthInputData({ advancedLogic: mockAdvancedLogic }); + }); + + it('can createExtensionsDataForCreation', async () => { + const spy = sandbox.on(mockAdvancedLogic.extensions.ethereumInputData, 'createCreationAction'); + + await ethInputData.createExtensionsDataForCreation({ paymentAddress: 'ethereum address' }); + + expect(spy).to.have.been.called.once; + }); + + it('can createExtensionsDataForAddPaymentInformation', async () => { + const spy = sandbox.on( + mockAdvancedLogic.extensions.ethereumInputData, + 'createAddPaymentAddressAction', + ); + + ethInputData.createExtensionsDataForAddPaymentInformation({ + paymentAddress: 'ethereum address', + }); + + expect(spy).to.have.been.called.once; + }); + + it('can createExtensionsDataForAddRefundInformation', async () => { + const spy = sandbox.on( + mockAdvancedLogic.extensions.ethereumInputData, + 'createAddRefundAddressAction', + ); + + ethInputData.createExtensionsDataForAddRefundInformation({ + refundAddress: 'ethereum address', + }); + + expect(spy).to.have.been.called.once; + }); + + // TODO: unskip + it.skip('can getBalance on a localhost request', async () => { + const mockRequest = { + creator: { type: '', value: '0x2' }, + currency: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + events: [], + expectedAmount: '0', + extensions: { + [ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED]: { + events: [], + id: '0', + type: 'none', + values: { + paymentAddress: '0xf17f52151EbEF6C7334FAD080c5704D77216b732', + }, + version: '0', + }, + }, + extensionsData: [], + requestId: '0x1', + state: 'Good', + timestamp: 0, + version: '0.2', + }; + + const balance = await ethInputData.getBalance(mockRequest as RequestLogicTypes.IRequest); + + expect(balance.balance).to.be.equal('10'); + expect(balance.events).to.have.lengthOf(1); + expect(balance.events[0].name).to.be.equal(PaymentTypes.EVENTS_NAMES.PAYMENT); + // TODO: add to & from to parameters? + // expect(balance.events[0].parameters!.to).to.be.equal( + // '0xf17f52151EbEF6C7334FAD080c5704D77216b732', + // ); + // expect(balance.events[0].parameters!.from).to.be.equal( + // '0x627306090abaB3A6e1400e9345bC60c78a8BEf57', + // ); + expect(balance.events[0].amount).to.be.equal('10'); + expect(balance.events[0].timestamp).to.be.a('number'); + }); + + it('should not throw when getBalance fail', async () => { + expect( + await ethInputData.getBalance({ + currency: { network: 'wrong' }, + } as RequestLogicTypes.IRequest), + ).to.deep.equal({ + balance: null, + error: { + code: PaymentTypes.BALANCE_ERROR_CODE.NETWORK_NOT_SUPPORTED, + message: + 'Payment network wrong not supported by ETH payment detection. Supported networks: mainnet, rinkeby, private', + }, + events: [], + }); + }); +}); diff --git a/packages/payment-detection/test/eth/proxy-info-retriever.test.ts b/packages/payment-detection/test/eth/proxy-info-retriever.test.ts new file mode 100644 index 0000000000..250d028894 --- /dev/null +++ b/packages/payment-detection/test/eth/proxy-info-retriever.test.ts @@ -0,0 +1,86 @@ +// tslint:disable: no-invalid-this +// tslint:disable: no-magic-numbers +import { PaymentTypes } from '@requestnetwork/types'; +import ProxyETHInfoRetriever from '../../src/eth/proxy-info-retriever'; + +import 'chai'; +import 'mocha'; + +const chai = require('chai'); +const expect = chai.expect; + +const proxyContractAddress = '0xf204a4ef082f5c04bb89f7d5e6568b796096735a'; +const paymentReferenceMock = '0111111111111111111111111111111111111111111111111'; + +/* tslint:disable:no-unused-expression */ +describe('api/eth/proxy-info-retriever', () => { + describe('on localhost', () => { + const paymentAddress = '0xf17f52151ebef6c7334fad080c5704d77216b732'; + + it('can get the localhost balance of an address', async () => { + const infoRetriever = new ProxyETHInfoRetriever( + paymentReferenceMock, + proxyContractAddress, + 0, + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + ); + + // inject mock provider.getLogs() + infoRetriever.provider.getLogs = (): any => { + return [ + { + address: proxyContractAddress, + blockHash: '0xe8693d0fb897eac7c350696834f4cf36be3b7d86f746d6764d9903a06dc5fe44', + blockNumber: 115, + data: `0x000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000001`, + logIndex: 2, + topics: [ + '0xf20789bd5e67749fbe748d26a9ffacd11036adee6a64a8dbc70cc37a98b4e542', + '0x6330b989705733cc5c1f7285b8a5b892e08be86ed6fbe9d254713a4277bc5bd2', + ], + transactionHash: '0x66e20126e917ede3543c79357c71ddeda2af68e27d684fd5aa6a813bb6946ce4', + transactionIndex: 0, + }, + ]; + }; + + // inject mock provider.getBlock() + infoRetriever.provider.getBlock = (): any => { + return { + timestamp: 69, + }; + }; + + const events = await infoRetriever.getTransferEvents(); + + // if this assert fails it means this address received another transaction + expect(events).to.have.lengthOf(1); + expect(events[0].name).to.equal(PaymentTypes.EVENTS_NAMES.PAYMENT); + expect(events[0].amount).to.equal('1'); + expect(events[0].timestamp).to.be.a('number'); + expect(events[0].parameters!.block).to.be.a('number'); + expect(events[0].parameters!.txHash).to.be.a('string'); + }); + + it('gets an empty list of events for an address without ETH on localhost', async () => { + const infoRetriever = new ProxyETHInfoRetriever( + paymentReferenceMock, + proxyContractAddress, + 0, + paymentAddress, + PaymentTypes.EVENTS_NAMES.PAYMENT, + 'private', + ); + + // inject mock provider.getLogs() + infoRetriever.provider.getLogs = (): any => { + return []; + }; + + const events = await infoRetriever.getTransferEvents(); + expect(events).to.be.empty; + }); + }); +}); diff --git a/packages/request-client.js/test/api/payment-network/payment-network-factory.test.ts b/packages/payment-detection/test/payment-network-factory.test.ts similarity index 88% rename from packages/request-client.js/test/api/payment-network/payment-network-factory.test.ts rename to packages/payment-detection/test/payment-network-factory.test.ts index b3619b9af4..5d8fbf2747 100644 --- a/packages/request-client.js/test/api/payment-network/payment-network-factory.test.ts +++ b/packages/payment-detection/test/payment-network-factory.test.ts @@ -1,15 +1,18 @@ -import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { + AdvancedLogicTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; -import BTCAddressedBased from '../../../src/api/payment-network/btc/mainnet-address-based'; -import Declarative from '../../../src/api/payment-network/declarative'; +import BTCAddressedBased from '../src/btc/mainnet-address-based'; +import Declarative from '../src/declarative'; import { expect } from 'chai'; import 'mocha'; -import PaymentNetworkFactory from '../../../src/api/payment-network/payment-network-factory'; - -import * as Types from '../../../src/types'; +import PaymentNetworkFactory from '../src/payment-network-factory'; const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { applyActionToExtensions(): any { @@ -23,8 +26,8 @@ const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { describe('api/payment-network/payment-network-factory', () => { describe('createPaymentNetwork', () => { it('can createPaymentNetwork', async () => { - const paymentNetworkCreationParameters: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, + const paymentNetworkCreationParameters: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: 'bitcoin address here', }, @@ -44,8 +47,8 @@ describe('api/payment-network/payment-network-factory', () => { }); it('can createPaymentNetwork with any currency', async () => { - const paymentNetworkCreationParameters: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetworkCreationParameters: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: { paymentAddress: 'bitcoin address here', }, diff --git a/packages/payment-detection/test/payment-reference-calculator.test.ts b/packages/payment-detection/test/payment-reference-calculator.test.ts new file mode 100644 index 0000000000..cb2fcad75e --- /dev/null +++ b/packages/payment-detection/test/payment-reference-calculator.test.ts @@ -0,0 +1,31 @@ +import PaymentReferenceCalculator from '../src/payment-reference-calculator'; + +import 'chai'; +import { expect } from 'chai'; +import 'mocha'; + +/* tslint:disable:no-unused-expression */ +describe('api/eth/payment-reference-calculator', () => { + it('can calculate the payment reference', async () => { + const expected = 'a0098add01acc736'; + const actual = PaymentReferenceCalculator.calculate('01847474747474', '8d03ea7', '0x000001'); + + expect(actual).to.equal(expected); + }); + + it('should throw if a parameter is missing', async () => { + const expectedErrorMessage = + 'RequestId, salt and address are mandatory to calculate the payment reference'; + expect(() => PaymentReferenceCalculator.calculate('', '8d03ea7', '0x000001')).to.throw( + expectedErrorMessage, + ); + + expect(() => PaymentReferenceCalculator.calculate('01847474747474', '', '0x000001')).to.throw( + expectedErrorMessage, + ); + + expect(() => PaymentReferenceCalculator.calculate('01847474747474', '8d03ea7', '')).to.throw( + expectedErrorMessage, + ); + }); +}); diff --git a/packages/payment-detection/tsconfig.json b/packages/payment-detection/tsconfig.json new file mode 100644 index 0000000000..5558359e85 --- /dev/null +++ b/packages/payment-detection/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["./src/**/*"], + "references": [{ "path": "../types" }, { "path": "../utils" }, { "path": "../smart-contracts" }] +} diff --git a/packages/payment-detection/tslint.json b/packages/payment-detection/tslint.json new file mode 100644 index 0000000000..0946f20963 --- /dev/null +++ b/packages/payment-detection/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/packages/payment-processor/.lintstagedrc.json b/packages/payment-processor/.lintstagedrc.json new file mode 100644 index 0000000000..09124f6125 --- /dev/null +++ b/packages/payment-processor/.lintstagedrc.json @@ -0,0 +1,3 @@ +{ + "src/**/*.ts": ["tslint --project . --fix", "prettier --single-quote --write", "git add"] +} diff --git a/packages/payment-processor/.nycrc b/packages/payment-processor/.nycrc new file mode 100644 index 0000000000..8f593ebeb3 --- /dev/null +++ b/packages/payment-processor/.nycrc @@ -0,0 +1,19 @@ +{ + "extension": [ + ".ts" + ], + "include": [ + "src/*.ts", + "src/**/*.ts" + ], + "require": [ + "ts-node/register" + ], + "reporter": [ + "text-summary", + "json", + "html" + ], + "sourceMap":true, + "all": true +} diff --git a/packages/payment-processor/.vscode/settings.json b/packages/payment-processor/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/payment-processor/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/payment-processor/CHANGELOG.md b/packages/payment-processor/CHANGELOG.md new file mode 100644 index 0000000000..b86e738706 --- /dev/null +++ b/packages/payment-processor/CHANGELOG.md @@ -0,0 +1,176 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.18.0 (2020-06-29) + + +### Features + +* add payment processing for eth proxy contract ([#220](https://github.com/RequestNetwork/requestNetwork/issues/220)) ([5859c89](https://github.com/RequestNetwork/requestNetwork/commit/5859c899434465e401e7993ffa22a6c1b0823a8a)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* payment-processor transaction overrides ([#198](https://github.com/RequestNetwork/requestNetwork/issues/198)) ([92a52aa](https://github.com/RequestNetwork/requestNetwork/commit/92a52aa7de88269e6869995444829ecdda15ede1)) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* **payment-processor:** remove instanceof usage ([#159](https://github.com/RequestNetwork/requestNetwork/issues/159)) ([48efc12](https://github.com/RequestNetwork/requestNetwork/commit/48efc1232a68782a6a8d6610d0883e241685574e)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.17.0 (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* payment-processor transaction overrides ([#198](https://github.com/RequestNetwork/requestNetwork/issues/198)) ([92a52aa](https://github.com/RequestNetwork/requestNetwork/commit/92a52aa7de88269e6869995444829ecdda15ede1)) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* **payment-processor:** remove instanceof usage ([#159](https://github.com/RequestNetwork/requestNetwork/issues/159)) ([48efc12](https://github.com/RequestNetwork/requestNetwork/commit/48efc1232a68782a6a8d6610d0883e241685574e)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.16.0 (2020-04-21) + + +### Features + +* payment-processor transaction overrides ([#198](https://github.com/RequestNetwork/requestNetwork/issues/198)) ([92a52aa](https://github.com/RequestNetwork/requestNetwork/commit/92a52aa7de88269e6869995444829ecdda15ede1)) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* **payment-processor:** remove instanceof usage ([#159](https://github.com/RequestNetwork/requestNetwork/issues/159)) ([48efc12](https://github.com/RequestNetwork/requestNetwork/commit/48efc1232a68782a6a8d6610d0883e241685574e)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* **payment-processor:** remove instanceof usage ([#159](https://github.com/RequestNetwork/requestNetwork/issues/159)) ([48efc12](https://github.com/RequestNetwork/requestNetwork/commit/48efc1232a68782a6a8d6610d0883e241685574e)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.14.0 (2020-03-23) + + +### Bug Fixes + +* **payment-processor:** remove instanceof usage ([#159](https://github.com/RequestNetwork/requestNetwork/issues/159)) ([48efc12](https://github.com/RequestNetwork/requestNetwork/commit/48efc1232a68782a6a8d6610d0883e241685574e)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) + + + + + +# 0.13.0 (2020-02-20) + + +### Features + +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) diff --git a/packages/payment-processor/README.md b/packages/payment-processor/README.md new file mode 100644 index 0000000000..c51278cbcd --- /dev/null +++ b/packages/payment-processor/README.md @@ -0,0 +1,11 @@ +# @requestnetwork/payment-processor + +`@requestnetwork/payment-processor` is a typescript library part of the [Request Network protocol](https://github.com/RequestNetwork/requestNetwork). +It contains client-side payment methods for: + +- ERC20 proxy contract +- ETH input data + +### Basic Usage + +see [ERC20](/packages/usage-examples/src/pay-erc20-request.ts) and [ETH](/packages/usage-examples/src/pay-eth-request.ts) usage examples. diff --git a/packages/payment-processor/package.json b/packages/payment-processor/package.json new file mode 100644 index 0000000000..058992eab8 --- /dev/null +++ b/packages/payment-processor/package.json @@ -0,0 +1,69 @@ +{ + "name": "@requestnetwork/payment-processor", + "version": "0.18.0", + "publishConfig": { + "access": "public" + }, + "description": "Payment processing using ethers.", + "keywords": [ + "requestnetwork" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RequestNetwork/requestNetwork.git" + }, + "homepage": "https://github.com/RequestNetwork/requestNetwork/tree/master/packages/payment-processor#readme", + "bugs": { + "url": "https://github.com/RequestNetwork/requestNetwork/issues" + }, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "directories": { + "lib": "src", + "test": "test" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc -b", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", + "lint": "tslint --project . && eslint \"src/**/*.ts\"", + "lint-staged": "lint-staged", + "prepare": "yarn run build", + "test": "nyc mocha --extension ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" + }, + "dependencies": { + "@requestnetwork/payment-detection": "0.18.0", + "@requestnetwork/smart-contracts": "0.9.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", + "ethers": "4.0.45" + }, + "devDependencies": { + "@types/chai": "4.1.7", + "@types/mocha": "5.2.7", + "@types/sinon": "7.5.0", + "@typescript-eslint/parser": "1.2.0", + "chai": "4.2.0", + "chai-as-promised": "7.1.1", + "chai-spies": "1.0.0", + "eslint": "5.13.0", + "eslint-plugin-spellcheck": "0.0.14", + "eslint-plugin-typescript": "0.14.0", + "lint-staged": "8.1.3", + "mocha": "6.2.2", + "nyc": "15.0.0", + "prettier": "1.19.1", + "shx": "0.3.2", + "sinon": "7.5.0", + "source-map-support": "0.5.13", + "tslint": "5.12.1", + "typescript": "3.7.2" + } +} diff --git a/packages/payment-processor/src/contracts/Erc20Contract.ts b/packages/payment-processor/src/contracts/Erc20Contract.ts new file mode 100644 index 0000000000..d7208e1fab --- /dev/null +++ b/packages/payment-processor/src/contracts/Erc20Contract.ts @@ -0,0 +1,31 @@ +import { Contract, Signer } from 'ethers'; +import { Provider } from 'ethers/providers'; +import { BigNumber, BigNumberish, Interface } from 'ethers/utils'; +import { ITypedFunctionDescription } from './TypedFunctionDescription'; + +const abi = [ + 'function balanceOf(address _owner) view returns (uint)', + 'function allowance(address _owner, address _spender) view returns (uint)', + 'function approve(address _spender, uint _value)', +]; + +interface IErc20ContractInterface extends Interface { + functions: { + approve: ITypedFunctionDescription<{ + encode([_spender, _value]: [string, BigNumberish]): string; + }>; + }; +} + +/** + * A typescript-documented ERC20 Contract. + */ +export abstract class ERC20Contract extends Contract { + public static connect(address: string, signerOrProvider: Signer | Provider): ERC20Contract { + return new Contract(address, abi, signerOrProvider) as ERC20Contract; + } + + public abstract interface: IErc20ContractInterface; + public abstract allowance(_owner: string, _spender: string): Promise; + public abstract balanceOf(_owner: string): Promise; +} diff --git a/packages/payment-processor/src/contracts/Erc20ProxyContract.ts b/packages/payment-processor/src/contracts/Erc20ProxyContract.ts new file mode 100644 index 0000000000..d75ca91b37 --- /dev/null +++ b/packages/payment-processor/src/contracts/Erc20ProxyContract.ts @@ -0,0 +1,30 @@ +import { Contract, Signer } from 'ethers'; +import { Provider } from 'ethers/providers'; +import { Arrayish, BigNumberish, Interface } from 'ethers/utils'; + +import { erc20ProxyArtifact } from '@requestnetwork/smart-contracts'; +import { ITypedFunctionDescription } from './TypedFunctionDescription'; + +interface IErc20ProxyContractInterface extends Interface { + functions: { + transferFromWithReference: ITypedFunctionDescription<{ + encode([_tokenAddress, _to, _amount, _paymentReference]: [ + string, + string, + BigNumberish, + Arrayish, + ]): string; + }>; + }; +} +/** + * A typescript-documented ERC20 Proxy Contract. + */ +export abstract class Erc20ProxyContract extends Contract { + public static connect(address: string, signerOrProvider: Signer | Provider): Erc20ProxyContract { + const abi = erc20ProxyArtifact.getContractAbi(); + return new Contract(address, abi, signerOrProvider) as Erc20ProxyContract; + } + + public abstract interface: IErc20ProxyContractInterface; +} diff --git a/packages/payment-processor/src/contracts/EthProxyContract.ts b/packages/payment-processor/src/contracts/EthProxyContract.ts new file mode 100644 index 0000000000..edc43aa20d --- /dev/null +++ b/packages/payment-processor/src/contracts/EthProxyContract.ts @@ -0,0 +1,25 @@ +import { Contract, Signer } from 'ethers'; +import { Provider } from 'ethers/providers'; +import { Arrayish, Interface } from 'ethers/utils'; + +import { ethereumProxyArtifact } from '@requestnetwork/smart-contracts'; +import { ITypedFunctionDescription } from './TypedFunctionDescription'; + +interface IEthProxyContractInterface extends Interface { + functions: { + transferWithReference: ITypedFunctionDescription<{ + encode([_to, _paymentReference]: [string, Arrayish]): string; + }>; + }; +} +/** + * A typescript-documented ETH Proxy Contract. + */ +export abstract class EthProxyContract extends Contract { + public static connect(address: string, signerOrProvider: Signer | Provider): EthProxyContract { + const abi = ethereumProxyArtifact.getContractAbi(); + return new Contract(address, abi, signerOrProvider) as EthProxyContract; + } + + public abstract interface: IEthProxyContractInterface; +} diff --git a/packages/payment-processor/src/contracts/TypedFunctionDescription.ts b/packages/payment-processor/src/contracts/TypedFunctionDescription.ts new file mode 100644 index 0000000000..234df350a8 --- /dev/null +++ b/packages/payment-processor/src/contracts/TypedFunctionDescription.ts @@ -0,0 +1,9 @@ +import { FunctionDescription } from 'ethers/utils'; + +/** + * Typescript-documented FunctionDescription + */ +export interface ITypedFunctionDescription> + extends FunctionDescription { + encode: T['encode']; +} diff --git a/packages/payment-processor/src/index.ts b/packages/payment-processor/src/index.ts new file mode 100644 index 0000000000..ad6cd2b408 --- /dev/null +++ b/packages/payment-processor/src/index.ts @@ -0,0 +1,8 @@ +export * from './payment'; +export * from './payment/btc-address-based'; +export * from './payment/erc20-proxy'; +export * from './payment/eth-input-data'; +export * from './payment/eth-proxy'; +import * as utils from './payment/utils'; + +export { utils }; diff --git a/packages/payment-processor/src/payment/btc-address-based.ts b/packages/payment-processor/src/payment/btc-address-based.ts new file mode 100644 index 0000000000..6be253b1c2 --- /dev/null +++ b/packages/payment-processor/src/payment/btc-address-based.ts @@ -0,0 +1,17 @@ +import { ethers } from 'ethers'; +import { BigNumberish } from 'ethers/utils'; + +import { ClientTypes } from '@requestnetwork/types'; + +import { getAmountToPay, getPaymentNetworkExtension } from './utils'; + +/** + * Returns the BIP21 payment URL based on the Request's value + * @param request + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + */ +export function getBtcPaymentUrl(request: ClientTypes.IRequestData, amount?: BigNumberish): string { + const pn = getPaymentNetworkExtension(request); + const amountToPay = getAmountToPay(request, amount); + return `bitcoin:${pn?.values.paymentAddress}?amount=${ethers.utils.formatUnits(amountToPay, 8)}`; +} diff --git a/packages/payment-processor/src/payment/erc20-proxy.ts b/packages/payment-processor/src/payment/erc20-proxy.ts new file mode 100644 index 0000000000..9c1b647b1e --- /dev/null +++ b/packages/payment-processor/src/payment/erc20-proxy.ts @@ -0,0 +1,173 @@ +import { ContractTransaction, Signer, utils } from 'ethers'; +import { Provider, Web3Provider } from 'ethers/providers'; +import { BigNumber, BigNumberish } from 'ethers/utils'; + +import { erc20ProxyArtifact } from '@requestnetwork/smart-contracts'; +import { ClientTypes, PaymentTypes } from '@requestnetwork/types'; + +import { ERC20Contract } from '../contracts/Erc20Contract'; +import { Erc20ProxyContract } from '../contracts/Erc20ProxyContract'; +import { ITransactionOverrides } from './transaction-overrides'; +import { + getAmountToPay, + getNetworkProvider, + getProvider, + getRequestPaymentValues, + getSigner, + validateRequest, +} from './utils'; + +/** + * Processes a transaction to pay an ERC20 Request. + * @param request + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + * @param overrides optionally, override default transaction values, like gas. + */ +export async function payErc20ProxyRequest( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), + amount?: BigNumberish, + overrides?: ITransactionOverrides, +): Promise { + const encodedTx = encodePayErc20Request(request, signerOrProvider, amount); + const proxyAddress = erc20ProxyArtifact.getAddress(request.currencyInfo.network!); + const signer = getSigner(signerOrProvider); + const tx = await signer.sendTransaction({ + data: encodedTx, + to: proxyAddress, + value: 0, + ...overrides, + }); + return tx; +} + +/** + * Encodes the call to pay a request through the ERC20 proxy contract, can be used with a Multisig contract. + * @param request request to pay + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + */ +export function encodePayErc20Request( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), + amount?: BigNumberish, +): string { + validateRequest(request, PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT); + const signer = getSigner(signerOrProvider); + + const tokenAddress = request.currencyInfo.value; + const proxyAddress = erc20ProxyArtifact.getAddress(request.currencyInfo.network!); + + const { paymentReference, paymentAddress } = getRequestPaymentValues(request); + const amountToPay = getAmountToPay(request, amount); + + const proxyContract = Erc20ProxyContract.connect(proxyAddress, signer); + return proxyContract.interface.functions.transferFromWithReference.encode([ + tokenAddress, + paymentAddress, + amountToPay, + `0x${paymentReference}`, + ]); +} + +/** + * Checks if a given account has the necessary allowance to pay an ERC20 request. + * @param request request to pay + * @param account account that will be used to pay the request + * @param provider the web3 provider. Defaults to Etherscan. + */ +export async function hasErc20Approval( + request: ClientTypes.IRequestData, + account: string, + provider: Provider = getNetworkProvider(request), +): Promise { + const erc20Contract = ERC20Contract.connect(request.currencyInfo.value, provider); + const allowance = await erc20Contract.allowance( + account, + erc20ProxyArtifact.getAddress(request.currencyInfo.network!), + ); + return allowance.gt(request.expectedAmount); +} + +/** + * Processes the approval transaction of the targeted ERC20. + * @param request request to pay + * @param provider the web3 provider. Defaults to Etherscan. + * @param overrides optionally, override default transaction values, like gas. + */ +export async function approveErc20( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), + overrides?: ITransactionOverrides, +): Promise { + const encodedTx = encodeApproveErc20(request, signerOrProvider); + const signer = getSigner(signerOrProvider); + const tokenAddress = request.currencyInfo.value; + const tx = await signer.sendTransaction({ + data: encodedTx, + to: tokenAddress, + value: 0, + ...overrides, + }); + return tx; +} + +/** + * Encodes the approval call, can be used with a Multisig contract. + * @param request the request to pay + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + */ +export function encodeApproveErc20( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), +): string { + validateRequest(request, PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT); + const signer = getSigner(signerOrProvider); + + const tokenAddress = request.currencyInfo.value; + const erc20interface = ERC20Contract.connect(tokenAddress, signer).interface; + const encodedApproveCall = erc20interface.functions.approve.encode([ + erc20ProxyArtifact.getAddress(request.currencyInfo.network!), + utils + .bigNumberify(2) + // tslint:disable-next-line: no-magic-numbers + .pow(256) + .sub(1), + ]); + return encodedApproveCall; +} + +/** + * Gets ERC20 balance of an address, based on the request currency information + * @param request the request that contains currency information + * @param address the address to check + * @param provider the web3 provider. Defaults to Etherscan + */ +export async function getErc20Balance( + request: ClientTypes.IRequestData, + address: string, + provider: Provider = getNetworkProvider(request), +): Promise { + const erc20Contract = ERC20Contract.connect(request.currencyInfo.value, provider); + return erc20Contract.balanceOf(address); +} + +/** + * Return the EIP-681 format URL with the transaction to pay an ERC20 + * Warning: this EIP isn't widely used, be sure to test compatibility yourself. + * + * @param request + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + */ +export function _getErc20PaymentUrl( + request: ClientTypes.IRequestData, + amount?: BigNumberish, +): string { + validateRequest(request, PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT); + const { paymentAddress, paymentReference } = getRequestPaymentValues(request); + const contractAddress = erc20ProxyArtifact.getAddress(request.currencyInfo.network!); + const amountToPay = getAmountToPay(request, amount); + const parameters = `transferFromWithReference?address=${request.currencyInfo.value}&address=${paymentAddress}&uint256=${amountToPay}&bytes=${paymentReference}`; + return `ethereum:${contractAddress}/${parameters}`; +} diff --git a/packages/payment-processor/src/payment/eth-input-data.ts b/packages/payment-processor/src/payment/eth-input-data.ts new file mode 100644 index 0000000000..f98fc3afb6 --- /dev/null +++ b/packages/payment-processor/src/payment/eth-input-data.ts @@ -0,0 +1,59 @@ +import { ContractTransaction, Signer } from 'ethers'; +import { Web3Provider } from 'ethers/providers'; +import { BigNumberish } from 'ethers/utils'; + +import { ClientTypes, PaymentTypes } from '@requestnetwork/types'; + +import { ITransactionOverrides } from './transaction-overrides'; +import { + getAmountToPay, + getProvider, + getRequestPaymentValues, + getSigner, + validateRequest, +} from './utils'; + +/** + * processes the transaction to pay an ETH request. + * @param request the request to pay + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + * @param overrides optionally, override default transaction values, like gas. + */ +export async function payEthInputDataRequest( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), + amount?: BigNumberish, + overrides?: ITransactionOverrides, +): Promise { + validateRequest(request, PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA); + const signer = getSigner(signerOrProvider); + const { paymentReference, paymentAddress } = getRequestPaymentValues(request); + + const amountToPay = getAmountToPay(request, amount); + + const tx = await signer.sendTransaction({ + data: `0x${paymentReference}`, + to: paymentAddress, + value: amountToPay, + ...overrides, + }); + return tx; +} + +/** + * processes the transaction to pay an ETH request. + * @param request the request to pay + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + */ +export function _getEthPaymentUrl( + request: ClientTypes.IRequestData, + amount?: BigNumberish, +): string { + const { paymentAddress, paymentReference } = getRequestPaymentValues(request); + const amountToPay = getAmountToPay(request, amount); + + // tslint:disable-next-line: no-console + return `ethereum:${paymentAddress}?value=${amountToPay}&data=${paymentReference}`; +} diff --git a/packages/payment-processor/src/payment/eth-proxy.ts b/packages/payment-processor/src/payment/eth-proxy.ts new file mode 100644 index 0000000000..f0a2a046a0 --- /dev/null +++ b/packages/payment-processor/src/payment/eth-proxy.ts @@ -0,0 +1,68 @@ +import { ContractTransaction, Signer } from 'ethers'; +import { Web3Provider } from 'ethers/providers'; +import { BigNumberish } from 'ethers/utils'; + +import { ClientTypes, PaymentTypes } from '@requestnetwork/types'; + +import { ethereumProxyArtifact } from '@requestnetwork/smart-contracts'; + +import { EthProxyContract } from '../contracts/EthProxyContract'; + +import { ITransactionOverrides } from './transaction-overrides'; +import { + getAmountToPay, + getProvider, + getRequestPaymentValues, + getSigner, + validateRequest, +} from './utils'; + +/** + * Processes a transaction to pay an ETH Request with the proxy contract. + * @param request + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + * @param overrides optionally, override default transaction values, like gas. + */ +export async function payEthProxyRequest( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), + amount?: BigNumberish, + overrides?: ITransactionOverrides, +): Promise { + const encodedTx = encodePayEthProxyRequest(request, signerOrProvider); + const proxyAddress = ethereumProxyArtifact.getAddress(request.currencyInfo.network!); + const signer = getSigner(signerOrProvider); + const amountToPay = getAmountToPay(request, amount); + const tx = await signer.sendTransaction({ + data: encodedTx, + to: proxyAddress, + value: amountToPay, + ...overrides, + }); + return tx; +} + +/** + * Encodes the call to pay a request through the ETH proxy contract, can be used with a Multisig contract. + * @param request request to pay + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + */ +export function encodePayEthProxyRequest( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), +): string { + validateRequest(request, PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA); + const signer = getSigner(signerOrProvider); + + const proxyAddress = ethereumProxyArtifact.getAddress(request.currencyInfo.network!); + + const { paymentReference, paymentAddress } = getRequestPaymentValues(request); + + const proxyContract = EthProxyContract.connect(proxyAddress, signer); + return proxyContract.interface.functions.transferWithReference.encode([ + paymentAddress, + `0x${paymentReference}`, + ]); +} diff --git a/packages/payment-processor/src/payment/index.ts b/packages/payment-processor/src/payment/index.ts new file mode 100644 index 0000000000..bfd14e28db --- /dev/null +++ b/packages/payment-processor/src/payment/index.ts @@ -0,0 +1,114 @@ +import { ContractTransaction, Signer } from 'ethers'; +import { Provider, Web3Provider } from 'ethers/providers'; +import { bigNumberify, BigNumberish } from 'ethers/utils'; + +import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; + +import { getBtcPaymentUrl } from './btc-address-based'; +import { _getErc20PaymentUrl, getErc20Balance, payErc20ProxyRequest } from './erc20-proxy'; +import { _getEthPaymentUrl, payEthInputDataRequest } from './eth-input-data'; +import { ITransactionOverrides } from './transaction-overrides'; +import { getNetworkProvider, getProvider, getSigner } from './utils'; + +const getPaymentNetwork = (request: ClientTypes.IRequestData): ExtensionTypes.ID | undefined => { + // tslint:disable-next-line: typedef + return Object.values(request.extensions).find(x => x.type === 'payment-network')?.id; +}; + +/** + * Error thrown when the network is not supported. + */ +export class UnsupportedNetworkError extends Error { + constructor(public networkName?: string) { + super(`Payment network ${networkName} is not supported`); + } +} + +/** + * Processes a transaction to pay a Request. + * Supported networks: ERC20_PROXY_CONTRACT, ETH_INPUT_DATA + * + * @throws UnsupportedNetworkError if network isn't supported + * @param request the request to pay. + * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum. + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + * @param overrides optionally, override default transaction values, like gas. + */ +export async function payRequest( + request: ClientTypes.IRequestData, + signerOrProvider: Web3Provider | Signer = getProvider(), + amount?: BigNumberish, + overrides?: ITransactionOverrides, +): Promise { + const signer = getSigner(signerOrProvider); + const paymentNetwork = getPaymentNetwork(request); + switch (paymentNetwork) { + case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT: + return payErc20ProxyRequest(request, signer, amount, overrides); + case ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA: + return payEthInputDataRequest(request, signer, amount, overrides); + default: + throw new UnsupportedNetworkError(paymentNetwork); + } +} + +/** + * Verifies the address has enough funds to pay the request. For ERC20 + * Supported networks: ERC20_PROXY_CONTRACT, ETH_INPUT_DATA + * + * @throws UnsupportedNetworkError if network isn't supported + * @param request the request to verify. + * @param address the address holding the funds + * @param provider the Web3 provider. Defaults to Etherscan. + */ +export async function hasSufficientFunds( + request: ClientTypes.IRequestData, + address: string, + provider?: Provider, +): Promise { + const paymentNetwork = getPaymentNetwork(request); + + let ethBalance; + switch (paymentNetwork) { + case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT: + if (!provider) { + provider = getNetworkProvider(request); + } + ethBalance = await provider.getBalance(address); + const balance = await getErc20Balance(request, address, provider); + // check ETH for gas, and token for funds transfer + return ethBalance.gt(0) && balance.gt(bigNumberify(request.expectedAmount || 0)); + case ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA: + if (!provider) { + provider = getNetworkProvider(request); + } + ethBalance = await provider.getBalance(address); + return ethBalance.gt(bigNumberify(request.expectedAmount || 0)); + default: + throw new UnsupportedNetworkError(paymentNetwork); + } +} + +/** + * Get a payment URL, if applicable to the payment network, for a request. + * BTC: BIP21. + * ERC20: EIP-681. (Warning, not widely used. Some wallets may not be able to pay.) + * ETH: EIP-681. (Warning, not widely used. Some wallets may not be able to pay.) + * @throws UnsupportedNetworkError if the network is not supported. + * @param request the request to pay + * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + */ +export function _getPaymentUrl(request: ClientTypes.IRequestData, amount?: BigNumberish): string { + const paymentNetwork = getPaymentNetwork(request); + switch (paymentNetwork) { + case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT: + return _getErc20PaymentUrl(request, amount); + case ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA: + return _getEthPaymentUrl(request, amount); + case ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED: + case ExtensionTypes.ID.PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED: + return getBtcPaymentUrl(request, amount); + default: + throw new UnsupportedNetworkError(paymentNetwork); + } +} diff --git a/packages/payment-processor/src/payment/transaction-overrides.ts b/packages/payment-processor/src/payment/transaction-overrides.ts new file mode 100644 index 0000000000..930f09a154 --- /dev/null +++ b/packages/payment-processor/src/payment/transaction-overrides.ts @@ -0,0 +1,4 @@ +import { TransactionRequest } from 'ethers/providers'; + +/** Custom values to pass to transaction */ +export interface ITransactionOverrides extends Omit {} diff --git a/packages/payment-processor/src/payment/utils.ts b/packages/payment-processor/src/payment/utils.ts new file mode 100644 index 0000000000..5310074cde --- /dev/null +++ b/packages/payment-processor/src/payment/utils.ts @@ -0,0 +1,144 @@ +import { ethers, getDefaultProvider, Signer } from 'ethers'; +import { Provider, Web3Provider } from 'ethers/providers'; +import { BigNumber, bigNumberify, BigNumberish } from 'ethers/utils'; + +import { PaymentReferenceCalculator } from '@requestnetwork/payment-detection'; +import { + ClientTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; + +/** + * Utility to get the default window.ethereum provider, or throws an error. + */ +export function getProvider(): Web3Provider { + if (typeof window !== 'undefined' && 'ethereum' in window) { + return new ethers.providers.Web3Provider((window as any).ethereum); + } + throw new Error('ethereum not found, you must pass your own web3 provider'); +} + +/** + * Utility to get a network provider, depending on the request's currency network. + * Will throw an error if the network isn't mainnet or rinkeby + * + * @param request + */ +export function getNetworkProvider(request: ClientTypes.IRequestData): Provider { + if (request.currencyInfo.network === 'mainnet') { + return getDefaultProvider(); + } + if (request.currencyInfo.network === 'rinkeby') { + return getDefaultProvider('rinkeby'); + } + throw new Error('unsupported network'); +} + +/** + * Utility to return a signer from a provider. + * @param signerOrProvider the provider, or signer. If Signer, it will simply be returned directly + * @param address optionally, the address to retrieve the signer for. + */ +export function getSigner(signerOrProvider?: Provider | Signer, address?: string): Signer { + if (!signerOrProvider) { + signerOrProvider = getProvider(); + } + if (Signer.isSigner(signerOrProvider)) { + return signerOrProvider; + } + if (Web3Provider.isProvider(signerOrProvider) && (signerOrProvider as Web3Provider).getSigner) { + return (signerOrProvider as Web3Provider).getSigner(address); + } + throw new Error('cannot get signer'); +} + +/** + * Utility to return the payment network extension of a Request. + * @param request + */ +export function getPaymentNetworkExtension( + request: ClientTypes.IRequestData, +): ExtensionTypes.IState | undefined { + // tslint:disable-next-line: typedef + return Object.values(request.extensions).find( + x => x.type === ExtensionTypes.TYPE.PAYMENT_NETWORK, + ); +} + +/** + * Utility to access the payment address and reference of a Request. + * @param request + */ +export function getRequestPaymentValues( + request: ClientTypes.IRequestData, +): { paymentAddress: string; paymentReference: string } { + const extension = getPaymentNetworkExtension(request); + if (!extension) { + throw new Error('no payment network found'); + } + const { paymentAddress, salt } = extension.values; + const paymentReference = PaymentReferenceCalculator.calculate( + request.requestId, + salt, + paymentAddress, + ); + return { paymentAddress, paymentReference }; +} + +const { ERC20_PROXY_CONTRACT, ETH_INPUT_DATA } = PaymentTypes.PAYMENT_NETWORK_ID; +const currenciesMap: any = { + [ERC20_PROXY_CONTRACT]: RequestLogicTypes.CURRENCY.ERC20, + [ETH_INPUT_DATA]: RequestLogicTypes.CURRENCY.ETH, +}; + +/** + * Utility to validate a request depending on the expected paymentNetwork. + * @param request + * @param paymentNetworkId + */ +export function validateRequest( + request: ClientTypes.IRequestData, + paymentNetworkId: PaymentTypes.PAYMENT_NETWORK_ID, +): void { + const extension = request.extensions[paymentNetworkId]; + const expectedCurrencyType = currenciesMap[paymentNetworkId]; + if ( + !expectedCurrencyType || + request.currencyInfo.type !== expectedCurrencyType || + !request.currencyInfo.network || + !extension || + !extension.values.salt || + !extension.values.paymentAddress || + (paymentNetworkId === ERC20_PROXY_CONTRACT && !request.currencyInfo.value) + ) { + throw new Error(`request cannot be processed, or is not an ${paymentNetworkId} request`); + } +} + +/** + * Computes the amount to pay. + * If `amount` is specified, it will return it. + * Otherwise, it will return the amount left to pay in the request. + * + * @param request the request to pay + * @param amount the optional amount to pay. + */ +export function getAmountToPay( + request: ClientTypes.IRequestData, + amount?: BigNumberish, +): BigNumber { + const amountToPay = + amount === undefined + ? bigNumberify(request.expectedAmount).sub(request.balance?.balance || 0) + : bigNumberify(amount); + + if (amountToPay.lt(0)) { + throw new Error('cannot pay a negative amount'); + } + if (amountToPay.isZero()) { + throw new Error('cannot pay a null amount'); + } + return amountToPay; +} diff --git a/packages/payment-processor/test/payment/btc-address-based.test.ts b/packages/payment-processor/test/payment/btc-address-based.test.ts new file mode 100644 index 0000000000..e1574de917 --- /dev/null +++ b/packages/payment-processor/test/payment/btc-address-based.test.ts @@ -0,0 +1,74 @@ +import 'mocha'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; +import { Wallet } from 'ethers'; + +import { + ClientTypes, + ExtensionTypes, + IdentityTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; + +import { getBtcPaymentUrl } from '../../src/payment/btc-address-based'; +// tslint:disable: no-unused-expression +// tslint:disable: await-promise + +const expect = chai.expect; +chai.use(chaiAsPromised); +chai.use(spies); + +const wallet = Wallet.createRandom(); +const paymentAddress = '1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX'; + +const validRequest: ClientTypes.IRequestData = { + balance: { + balance: '0', + events: [], + }, + contentData: {}, + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: wallet.address, + }, + currency: 'BTC', + currencyInfo: { + network: 'private', + type: RequestLogicTypes.CURRENCY.BTC, + value: '', + }, + events: [], + expectedAmount: '10000000', + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + salt: 'salt', + }, + version: '1.0', + }, + }, + extensionsData: [], + meta: { + transactionManagerMeta: {}, + }, + pending: null, + requestId: 'abcd', + state: RequestLogicTypes.STATE.CREATED, + timestamp: 0, + version: '1.0', +}; + +describe('getBtcPaymentUrl', () => { + it('can get a BTC url', () => { + expect(getBtcPaymentUrl(validRequest)).to.eq( + 'bitcoin:1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX?amount=0.1', + ); + }); +}); diff --git a/packages/payment-processor/test/payment/erc20-proxy.test.ts b/packages/payment-processor/test/payment/erc20-proxy.test.ts new file mode 100644 index 0000000000..a10736530f --- /dev/null +++ b/packages/payment-processor/test/payment/erc20-proxy.test.ts @@ -0,0 +1,212 @@ +import 'mocha'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; +import { Wallet } from 'ethers'; +import { JsonRpcProvider } from 'ethers/providers'; + +import { + ClientTypes, + ExtensionTypes, + IdentityTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; + +import { + _getErc20PaymentUrl, + approveErc20, + getErc20Balance, + hasErc20Approval, + payErc20ProxyRequest, +} from '../../src/payment/erc20-proxy'; +import { getRequestPaymentValues } from '../../src/payment/utils'; + +// tslint:disable: no-unused-expression +// tslint:disable: await-promise + +const expect = chai.expect; +chai.use(chaiAsPromised); +chai.use(spies); +const sandbox = chai.spy.sandbox(); + +const erc20ContractAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40'; + +const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; +const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; +const provider = new JsonRpcProvider('http://localhost:8545'); +const wallet = Wallet.fromMnemonic(mnemonic).connect(provider); + +const validRequest: ClientTypes.IRequestData = { + balance: { + balance: '0', + events: [], + }, + contentData: {}, + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: wallet.address, + }, + currency: 'DAI', + currencyInfo: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ERC20, + value: erc20ContractAddress, + }, + + events: [], + expectedAmount: '100', + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + salt: 'salt', + }, + version: '1.0', + }, + }, + extensionsData: [], + meta: { + transactionManagerMeta: {}, + }, + pending: null, + requestId: 'abcd', + state: RequestLogicTypes.STATE.CREATED, + timestamp: 0, + version: '1.0', +}; + +describe('getRequestPaymentValues', () => { + it('handles ERC20', () => { + const values = getRequestPaymentValues(validRequest); + expect(values.paymentAddress).to.eq(paymentAddress); + expect(values.paymentReference).to.eq('86dfbccad783599a'); + }); +}); + +describe('getErc20Balance', () => { + it('should read the balance', async () => { + const balance = await getErc20Balance(validRequest, wallet.address, provider); + chai.assert.isTrue(balance.gte('100')); + }); +}); + +describe('hasErc20Approval & approveErc20', () => { + it('should consider override parameters', async () => { + const spy = sandbox.on(wallet, 'sendTransaction', () => 0); + await approveErc20(validRequest, wallet, { + gasPrice: '20000000000', + }); + expect(spy).to.have.been.called.with({ + data: + '0x095ea7b30000000000000000000000002c2b9c9a4a25e24b174f26114e8926a9f2128fe4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + gasPrice: '20000000000', + to: '0x9FBDa871d559710256a2502A2517b794B482Db40', + value: 0, + }); + sandbox.restore(); + }); + it('can check and approve', async () => { + // use another address so it doesn't mess with other tests. + const otherWallet = new Wallet( + '0x8d5366123cb560bb606379f90a0bfd4769eecc0557f1b362dcae9012b548b1e5', + ).connect(provider); + let hasApproval = await hasErc20Approval(validRequest, otherWallet.address, provider); + // Warning: this test can run only once! + expect(hasApproval, 'already has approval').to.be.false; + await approveErc20(validRequest, otherWallet); + hasApproval = await hasErc20Approval(validRequest, otherWallet.address, provider); + expect(hasApproval, 'approval did not succeed').to.be.true; + }); +}); + +describe('payErc20ProxyRequest', () => { + it('should throw an error if the request is not erc20', async () => { + const request = Utils.deepCopy(validRequest) as ClientTypes.IRequestData; + request.currencyInfo.type = RequestLogicTypes.CURRENCY.ETH; + + await expect(payErc20ProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-erc20-proxy-contract request', + ); + }); + + it('should throw an error if the currencyInfo has no value', async () => { + const request = Utils.deepCopy(validRequest); + request.currencyInfo.value = ''; + await expect(payErc20ProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-erc20-proxy-contract request', + ); + }); + + it('should throw an error if currencyInfo has no network', async () => { + const request = Utils.deepCopy(validRequest); + request.currencyInfo.network = ''; + await expect(payErc20ProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-erc20-proxy-contract request', + ); + }); + + it('should throw an error if request has no extension', async () => { + const request = Utils.deepCopy(validRequest); + request.extensions = [] as any; + + await expect(payErc20ProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-erc20-proxy-contract request', + ); + }); + + it('should consider override parameters', async () => { + const spy = sandbox.on(wallet, 'sendTransaction', () => 0); + await payErc20ProxyRequest(validRequest, wallet, undefined, { + gasPrice: '20000000000', + }); + expect(spy).to.have.been.called.with({ + data: + '0x0784bca30000000000000000000000009fbda871d559710256a2502a2517b794b482db40000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b73200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000886dfbccad783599a000000000000000000000000000000000000000000000000', + gasPrice: '20000000000', + to: '0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4', + value: 0, + }); + sandbox.restore(); + }); + + it('should pay an ERC20 request', async () => { + // first approve the contract + const approvalTx = await approveErc20(validRequest, wallet); + await approvalTx.wait(1); + + // get the balance to compare after payment + + const balanceEthBefore = await wallet.getBalance(); + const balanceErc20Before = await getErc20Balance(validRequest, wallet.address, provider); + + const tx = await payErc20ProxyRequest(validRequest, wallet); + const confirmedTx = await tx.wait(1); + + const balanceEthAfter = await wallet.getBalance(); + const balanceErc20After = await getErc20Balance(validRequest, wallet.address, provider); + + expect(confirmedTx.status).to.eq(1); + expect(tx.hash).not.to.be.undefined; + + chai.assert.isTrue(balanceEthAfter.lte(balanceEthBefore), 'ETH balance should be lower'); + chai.assert.isTrue(balanceErc20After.lte(balanceErc20Before), 'ERC20 balance should be lower'); + + expect(balanceErc20Before.toString()).equal( + balanceErc20After.add(validRequest.expectedAmount).toString(), + ); + }); +}); + +describe('getErc20PaymentUrl', () => { + it('can get an ERC20 url', () => { + expect(_getErc20PaymentUrl(validRequest)).to.eq( + 'ethereum:0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4/transferFromWithReference?address=0x9FBDa871d559710256a2502A2517b794B482Db40&address=0xf17f52151EbEF6C7334FAD080c5704D77216b732&uint256=100&bytes=86dfbccad783599a', + ); + }); +}); diff --git a/packages/payment-processor/test/payment/eth-input-data.test.ts b/packages/payment-processor/test/payment/eth-input-data.test.ts new file mode 100644 index 0000000000..678aa62b88 --- /dev/null +++ b/packages/payment-processor/test/payment/eth-input-data.test.ts @@ -0,0 +1,145 @@ +import 'mocha'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; +import { Wallet } from 'ethers'; +import { JsonRpcProvider } from 'ethers/providers'; + +import { + ClientTypes, + ExtensionTypes, + IdentityTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; + +import { _getEthPaymentUrl, payEthInputDataRequest } from '../../src/payment/eth-input-data'; +import { getRequestPaymentValues } from '../../src/payment/utils'; +import { BigNumber } from 'ethers/utils'; + +// tslint:disable: no-unused-expression +// tslint:disable: await-promise + +const expect = chai.expect; +chai.use(chaiAsPromised); +chai.use(spies); +const sandbox = chai.spy.sandbox(); + +const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; +const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; +const provider = new JsonRpcProvider('http://localhost:8545'); +const wallet = Wallet.fromMnemonic(mnemonic).connect(provider); + +const validRequest: ClientTypes.IRequestData = { + balance: { + balance: '0', + events: [], + }, + contentData: {}, + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: wallet.address, + }, + currency: 'ETH', + currencyInfo: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ETH, + value: '', + }, + + events: [], + expectedAmount: '1', + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + salt: 'salt', + }, + version: '1.0', + }, + }, + extensionsData: [], + meta: { + transactionManagerMeta: {}, + }, + pending: null, + requestId: 'abcd', + state: RequestLogicTypes.STATE.CREATED, + timestamp: 0, + version: '1.0', +}; + +describe('getRequestPaymentValues', () => { + it('handles ETH', () => { + const values = getRequestPaymentValues(validRequest); + expect(values.paymentAddress).to.eq(paymentAddress); + expect(values.paymentReference).to.eq('86dfbccad783599a'); + }); +}); + +describe('payEthInputDataRequest', () => { + it('should throw an error if the request is not eth', async () => { + const request = Utils.deepCopy(validRequest) as ClientTypes.IRequestData; + request.currencyInfo.type = RequestLogicTypes.CURRENCY.ERC20; + await expect(payEthInputDataRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-eth-input-data request', + ); + }); + + it('should throw an error if currencyInfo has no network', async () => { + const request = Utils.deepCopy(validRequest); + request.currencyInfo.network = ''; + await expect(payEthInputDataRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-eth-input-data request', + ); + }); + + it('should throw an error if request has no extension', async () => { + const request = Utils.deepCopy(validRequest); + request.extensions = [] as any; + + await expect(payEthInputDataRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-eth-input-data request', + ); + }); + + it('should consider override parameters', async () => { + const spy = sandbox.on(wallet, 'sendTransaction', () => 0); + await payEthInputDataRequest(validRequest, wallet, undefined, { + gasPrice: '20000000001', + }); + expect(spy).to.have.been.called.with({ + data: '0x86dfbccad783599a', + gasPrice: '20000000001', + to: '0xf17f52151EbEF6C7334FAD080c5704D77216b732', + value: new BigNumber(1), + }); + sandbox.restore(); + }); + + it('processes a payment for a pn-eth-input-data request', async () => { + const balanceBefore = await wallet.getBalance(); + expect(balanceBefore.gt(0)); + const tx = await payEthInputDataRequest(validRequest, wallet); + const confirmedTx = await tx.wait(1); + const balanceAfter = await wallet.getBalance(); + expect(confirmedTx.status).to.eq(1); + // new_balance = old_balance + amount + fees + expect( + balanceAfter.eq(balanceBefore.sub(validRequest.expectedAmount).sub(confirmedTx.gasUsed || 0)), + ); + }); +}); + +describe('getEthPaymentUrl', () => { + it('can get an ETH url', () => { + expect(_getEthPaymentUrl(validRequest)).to.eq( + 'ethereum:0xf17f52151EbEF6C7334FAD080c5704D77216b732?value=1&data=86dfbccad783599a', + ); + }); +}); diff --git a/packages/payment-processor/test/payment/eth-proxy.test.ts b/packages/payment-processor/test/payment/eth-proxy.test.ts new file mode 100644 index 0000000000..9ffab9e9fb --- /dev/null +++ b/packages/payment-processor/test/payment/eth-proxy.test.ts @@ -0,0 +1,156 @@ +import 'mocha'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; +import { Wallet } from 'ethers'; +import { JsonRpcProvider } from 'ethers/providers'; + +import { + ClientTypes, + ExtensionTypes, + IdentityTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; + +import { bigNumberify } from 'ethers/utils'; +import { encodePayEthProxyRequest, payEthProxyRequest } from '../../src/payment/eth-proxy'; +import { getRequestPaymentValues } from '../../src/payment/utils'; + +// tslint:disable: no-unused-expression +// tslint:disable: await-promise + +const expect = chai.expect; +chai.use(chaiAsPromised); +chai.use(spies); +const sandbox = chai.spy.sandbox(); + +const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; +const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; +const provider = new JsonRpcProvider('http://localhost:8545'); +const wallet = Wallet.fromMnemonic(mnemonic).connect(provider); + +const validRequest: ClientTypes.IRequestData = { + balance: { + balance: '0', + events: [], + }, + contentData: {}, + creator: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: wallet.address, + }, + currency: 'ETH', + currencyInfo: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ETH, + value: RequestLogicTypes.CURRENCY.ETH, + }, + + events: [], + expectedAmount: '100', + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + paymentAddress, + salt: 'salt', + }, + version: '1.0', + }, + }, + extensionsData: [], + meta: { + transactionManagerMeta: {}, + }, + pending: null, + requestId: 'abcd', + state: RequestLogicTypes.STATE.CREATED, + timestamp: 0, + version: '2.0.3', +}; + +describe('getRequestPaymentValues', () => { + it('handles ETH', () => { + const values = getRequestPaymentValues(validRequest); + expect(values.paymentAddress).to.eq(paymentAddress); + expect(values.paymentReference).to.eq('86dfbccad783599a'); + }); +}); + +describe('payEthProxyRequest', () => { + it('should throw an error if the request is not erc20', async () => { + const request = Utils.deepCopy(validRequest) as ClientTypes.IRequestData; + request.currencyInfo.type = RequestLogicTypes.CURRENCY.ERC20; + + await expect(payEthProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-eth-input-data request', + ); + }); + + it('should throw an error if currencyInfo has no network', async () => { + const request = Utils.deepCopy(validRequest); + request.currencyInfo.network = ''; + await expect(payEthProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-eth-input-data request', + ); + }); + + it('should throw an error if request has no extension', async () => { + const request = Utils.deepCopy(validRequest); + request.extensions = [] as any; + + await expect(payEthProxyRequest(request, wallet)).to.eventually.be.rejectedWith( + 'request cannot be processed, or is not an pn-eth-input-data request', + ); + }); + + it('should consider override parameters', async () => { + const spy = sandbox.on(wallet, 'sendTransaction', () => 0); + await payEthProxyRequest(validRequest, wallet, undefined, { + gasPrice: '20000000000', + }); + expect(spy).to.have.been.called.with({ + data: + '0xeb7d8df3000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000886dfbccad783599a000000000000000000000000000000000000000000000000', + gasPrice: '20000000000', + to: '0xf204a4Ef082f5c04bB89F7D5E6568B796096735a', + value: bigNumberify('0x64'), + }); + sandbox.restore(); + }); + + it('should pay an ETH request', async () => { + // get the balance to compare after payment + const balanceEthBefore = await wallet.getBalance(); + + const tx = await payEthProxyRequest(validRequest, wallet); + const confirmedTx = await tx.wait(1); + + const balanceEthAfter = await wallet.getBalance(); + + expect(confirmedTx.status).to.eq(1); + expect(tx.hash).not.to.be.undefined; + + chai.assert.isTrue(balanceEthAfter.lte(balanceEthBefore), 'ETH balance should be lower'); + + expect(balanceEthBefore.toString()).equal( + balanceEthAfter + .add(validRequest.expectedAmount) + .add(confirmedTx.gasUsed!.mul(tx.gasPrice)) + .toString(), + ); + }); +}); + +describe('encodePayEthProxyRequest', () => { + it('should encode pay for an ETH request', async () => { + expect(await encodePayEthProxyRequest(validRequest, wallet)).equal( + '0xeb7d8df3000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000886dfbccad783599a000000000000000000000000000000000000000000000000', + ); + }); +}); diff --git a/packages/payment-processor/test/payment/index.test.ts b/packages/payment-processor/test/payment/index.test.ts new file mode 100644 index 0000000000..09539a72ff --- /dev/null +++ b/packages/payment-processor/test/payment/index.test.ts @@ -0,0 +1,241 @@ +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import { Wallet } from 'ethers'; +import { JsonRpcProvider } from 'ethers/providers'; +import { bigNumberify } from 'ethers/utils'; +import { stub } from 'sinon'; + +import { ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; + +import { _getPaymentUrl, hasSufficientFunds, payRequest } from '../../src/payment'; +import * as btcModule from '../../src/payment/btc-address-based'; +import * as erc20Module from '../../src/payment/erc20-proxy'; +import * as ethModule from '../../src/payment/eth-input-data'; + +// tslint:disable: no-unused-expression +// tslint:disable: await-promise + +const expect = chai.expect; +chai.use(chaiAsPromised); + +const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; +const provider = new JsonRpcProvider('http://localhost:8545'); +const wallet = Wallet.fromMnemonic(mnemonic).connect(provider); + +describe('payRequest', () => { + it('paying a declarative request should fail', async () => { + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + await expect(payRequest(request, wallet)).to.be.rejectedWith( + 'Payment network pn-any-declarative is not supported', + ); + }); + + it('paying a BTC request should fail', async () => { + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + await expect(payRequest(request, wallet)).to.be.rejectedWith( + 'Payment network pn-bitcoin-address-based is not supported', + ); + }); + + it('should call the ETH payment method', async () => { + const spy = stub(ethModule, 'payEthInputDataRequest'); + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + await payRequest(request, wallet); + expect(spy.calledOnce).to.be.true; + }); + + it('should call the ERC20 payment method', async () => { + const spy = stub(erc20Module, 'payErc20ProxyRequest'); + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + await payRequest(request, wallet); + expect(spy.calledOnce).to.be.true; + }); +}); + +describe('hasSufficientFunds', () => { + it('should throw an error on unsupported network', () => { + const request: any = { + currencyInfo: { + network: 'testnet', + }, + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + expect(hasSufficientFunds(request, '')).to.be.rejectedWith( + 'Payment network pn-any-declarative is not supported', + ); + }); + + it('should call the ETH payment method', async () => { + const fakeProvider: any = { + getBalance: stub().returns(Promise.resolve(bigNumberify('200'))), + }; + const request: any = { + balance: { + balance: '0', + }, + currencyInfo: { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ETH, + }, + expectedAmount: '100', + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + await hasSufficientFunds(request, 'abcd', fakeProvider); + expect(fakeProvider.getBalance.calledOnce).to.be.true; + }); + + it('should call the ERC20 payment method', async () => { + const spy = stub(erc20Module, 'getErc20Balance').returns(Promise.resolve(bigNumberify('200'))); + const fakeProvider: any = { + getBalance: () => Promise.resolve(bigNumberify('200')), + }; + const request: any = { + balance: { + balance: '0', + }, + currencyInfo: { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ERC20, + value: 'efgh', + }, + expectedAmount: '100', + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + await hasSufficientFunds(request, 'abcd', fakeProvider); + expect(spy.calledOnce).to.be.true; + }); +}); + +describe('_getPaymentUrl', () => { + it('should throw an error on unsupported network', () => { + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + expect(() => _getPaymentUrl(request)).to.throw( + 'Payment network pn-any-declarative is not supported', + ); + }); + + it('should call the BTC payment url method', async () => { + const spy = stub(btcModule, 'getBtcPaymentUrl'); + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + _getPaymentUrl(request); + expect(spy.calledOnce).to.be.true; + }); + + it('should call the ETH payment url method', async () => { + const spy = stub(ethModule, '_getEthPaymentUrl'); + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + _getPaymentUrl(request); + expect(spy.calledOnce).to.be.true; + }); + + it('should call the ERC20 payment url method', async () => { + const spy = stub(erc20Module, '_getErc20PaymentUrl'); + const request: any = { + extensions: { + [PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT]: { + events: [], + id: ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: {}, + version: '1.0', + }, + }, + }; + _getPaymentUrl(request); + expect(spy.calledOnce).to.be.true; + }); +}); diff --git a/packages/payment-processor/test/payment/utils.test.ts b/packages/payment-processor/test/payment/utils.test.ts new file mode 100644 index 0000000000..4e4d735da3 --- /dev/null +++ b/packages/payment-processor/test/payment/utils.test.ts @@ -0,0 +1,133 @@ +import { expect } from 'chai'; +import { Wallet } from 'ethers'; +import { BaseProvider } from 'ethers/providers'; +import { bigNumberify } from 'ethers/utils'; + +import { + getAmountToPay, + getNetworkProvider, + getProvider, + getSigner, +} from '../../src/payment/utils'; + +describe('getAmountToPay', () => { + it('returns the expectedAmount if balance is 0', () => { + expect( + getAmountToPay({ + balance: { + balance: '0', + }, + expectedAmount: '1000000', + } as any), + ).to.deep.eq(bigNumberify('1000000')); + }); + + it('returns the remaining amount if balance is not 0', () => { + expect( + getAmountToPay({ + balance: { + balance: '400000', + }, + expectedAmount: '1000000', + } as any), + ).to.deep.eq(bigNumberify('600000')); + }); + + it('returns the given amount if defined', () => { + expect( + getAmountToPay( + { + balance: { + balance: '400000', + }, + expectedAmount: '1000000', + } as any, + '3000', + ), + ).to.deep.eq(bigNumberify('3000')); + }); + + it('fails on a negative amount', () => { + expect(() => + getAmountToPay( + { + balance: { + balance: '400000', + }, + expectedAmount: '1000000', + } as any, + '-3000', + ), + ).to.throw('cannot pay a negative amount'); + }); + + it('fails on a negative remaining amount', () => { + expect(() => + getAmountToPay({ + balance: { + balance: '1400000', + }, + expectedAmount: '1000000', + } as any), + ).to.throw('cannot pay a negative amount'); + }); + + it('fails on a paid request', () => { + expect(() => + getAmountToPay({ + balance: { + balance: '1000000', + }, + expectedAmount: '1000000', + } as any), + ).to.throw('cannot pay a null amount'); + }); +}); + +describe('getProvider', () => { + it('fails if ethereum not defined', () => { + expect(() => getProvider()).to.throw( + 'ethereum not found, you must pass your own web3 provider', + ); + }); +}); + +describe('getNetworkProvider', () => { + it('returns a provider for mainnet', () => { + const request: any = { + currencyInfo: { + network: 'mainnet', + }, + }; + expect(getNetworkProvider(request)).to.be.instanceOf(BaseProvider); + }); + + it('returns a provider for rinkeby', () => { + const request: any = { + currencyInfo: { + network: 'rinkeby', + }, + }; + expect(getNetworkProvider(request)).to.be.instanceOf(BaseProvider); + }); + + it('fails for other network', () => { + const request: any = { + currencyInfo: { + network: 'ropsten', + }, + }; + expect(() => getNetworkProvider(request)).to.throw('unsupported network'); + }); +}); + +describe('getSigner', () => { + it('should return the instance if signer is passed', () => { + const wallet = Wallet.createRandom(); + expect(getSigner(wallet)).to.eq(wallet); + }); + + it('should throw an error if the provider is not supported', () => { + expect(() => getSigner({} as any)).to.throw('cannot get signer'); + }); +}); diff --git a/packages/payment-processor/tsconfig.json b/packages/payment-processor/tsconfig.json new file mode 100644 index 0000000000..f57ccc0704 --- /dev/null +++ b/packages/payment-processor/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["./src/**/*"], + "references": [ + { "path": "../types" }, + { "path": "../utils" }, + { "path": "../payment-detection" }, + { "path": "../smart-contracts" } + ] +} diff --git a/packages/payment-processor/tslint.json b/packages/payment-processor/tslint.json new file mode 100644 index 0000000000..0946f20963 --- /dev/null +++ b/packages/payment-processor/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/packages/prototype-estimator/.vscode/settings.json b/packages/prototype-estimator/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/prototype-estimator/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/prototype-estimator/CHANGELOG.md b/packages/prototype-estimator/CHANGELOG.md index 6d8adc1cd1..f4b3b25954 100644 --- a/packages/prototype-estimator/CHANGELOG.md +++ b/packages/prototype-estimator/CHANGELOG.md @@ -3,6 +3,259 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.9.0) (2020-06-29) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [1.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.8.0) (2020-05-04) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [1.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.7.0) (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [1.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.6.0) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [1.5.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.5.0) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [1.4.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.4.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [1.3.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.3.8) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/prototype-estimator + + + + + +## [1.3.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.3.7) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/prototype-estimator + + + + + +## [1.3.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.5...@requestnetwork/prototype-estimator@1.3.6) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/prototype-estimator + + + + + ## [1.3.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/prototype-estimator@1.3.4...@requestnetwork/prototype-estimator@1.3.5) (2019-11-20) **Note:** Version bump only for package @requestnetwork/prototype-estimator diff --git a/packages/prototype-estimator/package.json b/packages/prototype-estimator/package.json index bcd0e1f059..f9bdd37e8b 100644 --- a/packages/prototype-estimator/package.json +++ b/packages/prototype-estimator/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/prototype-estimator", - "version": "1.3.5", + "version": "1.9.0", "private": true, "description": "Tool to estimate size and throughput of v2 system.", "keywords": [ @@ -26,23 +26,23 @@ "start": "ts-node src/index.ts" }, "dependencies": { - "@requestnetwork/advanced-logic": "0.6.0", - "@requestnetwork/data-access": "0.5.2", - "@requestnetwork/epk-signature": "0.5.4", - "@requestnetwork/ethereum-storage": "0.4.5", - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/request-client.js": "0.9.0", - "@requestnetwork/request-logic": "0.8.0", - "@requestnetwork/request-node": "0.5.4", - "@requestnetwork/transaction-manager": "0.8.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/advanced-logic": "0.15.0", + "@requestnetwork/data-access": "0.11.0", + "@requestnetwork/epk-signature": "0.5.13", + "@requestnetwork/ethereum-storage": "0.10.0", + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/request-client.js": "0.18.0", + "@requestnetwork/request-logic": "0.14.0", + "@requestnetwork/request-node": "0.11.0", + "@requestnetwork/transaction-manager": "0.14.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "benchmark": "2.1.4", "simple-statistics": "7.0.2" }, "devDependencies": { "prettier": "1.16.4", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "typescript": "3.7.2" } } diff --git a/packages/prototype-estimator/src/mock-storage.ts b/packages/prototype-estimator/src/mock-storage.ts index 7a7ccaf18c..e2cd8dda54 100644 --- a/packages/prototype-estimator/src/mock-storage.ts +++ b/packages/prototype-estimator/src/mock-storage.ts @@ -1,40 +1,61 @@ -// Copy from packages\request-client.js\src\mock-storage.ts - import MultiFormat from '@requestnetwork/multi-format'; import { StorageTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; +import { EventEmitter } from 'events'; /** * Storage layer implemented with in-memory hashmap, to be used for testing. - * - * @export - * @class MockStorage - * @implements {StorageTypes.IStorage} */ export default class MockStorage implements StorageTypes.IStorage { - private data: { [key: string]: { content: string; timestamp: number } } = {}; + private data: { + [key: string]: { state: StorageTypes.ContentState; content: string; timestamp: number }; + } = {}; public async initialize(): Promise { return; } - public async append(content: string): Promise { + public async _ipfsAdd(): Promise { + throw Error('will never be used'); + } + + public async _getStatus(): Promise { + throw Error('will never be used'); + } + + public async append(content: string): Promise { if (!content) { throw Error('Error: no content provided'); } const hash = MultiFormat.serialize(Utils.crypto.normalizeKeccak256Hash(content)); - const timestamp = Utils.getCurrentTimestampInSecond(); - this.data[hash] = { content, timestamp }; + const nowTimestampInSec = Utils.getCurrentTimestampInSecond(); - return { - content: '', + this.data[hash] = { + content, + state: StorageTypes.ContentState.PENDING, + timestamp: nowTimestampInSec, + }; + + const resultData = { + content, id: hash, meta: { + state: StorageTypes.ContentState.PENDING, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, - timestamp, + timestamp: nowTimestampInSec, }, }; + const result = Object.assign(new EventEmitter(), resultData); + + // emit confirmed + setTimeout(() => { + this.data[hash].state = StorageTypes.ContentState.CONFIRMED; + result.emit('confirmed', resultData); + // tslint:disable-next-line:no-magic-numbers + }, 100); + + return result; } public async read(id: string): Promise { @@ -45,8 +66,9 @@ export default class MockStorage implements StorageTypes.IStorage { content: this.data[id].content, id, meta: { + state: this.data[id].state, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, - timestamp: 1, + timestamp: this.data[id].timestamp, }, }; } @@ -56,10 +78,11 @@ export default class MockStorage implements StorageTypes.IStorage { } public async getData(): Promise { - const entries = Object.entries(this.data).map(([id, { content, timestamp }]) => ({ + const entries = Object.entries(this.data).map(([id, { content, state, timestamp }]) => ({ content, id, meta: { + state, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, timestamp, }, @@ -72,4 +95,8 @@ export default class MockStorage implements StorageTypes.IStorage { lastTimestamp: nowTimestampInSec, }; } + + public async getIgnoredData(): Promise { + return []; + } } diff --git a/packages/request-client.js/.vscode/settings.json b/packages/request-client.js/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/request-client.js/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/request-client.js/CHANGELOG.md b/packages/request-client.js/CHANGELOG.md index db4733471d..d3f196619d 100644 --- a/packages/request-client.js/CHANGELOG.md +++ b/packages/request-client.js/CHANGELOG.md @@ -3,6 +3,567 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.18.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.18.0) (2020-06-29) + + +### Bug Fixes + +* clone the array topics to avoid modification of parameters ([#215](https://github.com/RequestNetwork/requestNetwork/issues/215)) ([7447c0b](https://github.com/RequestNetwork/requestNetwork/commit/7447c0be26b645ab54e1c82b1451570e88618861)) +* don't remove failed transactions from data-access ([#236](https://github.com/RequestNetwork/requestNetwork/issues/236)) ([74835f0](https://github.com/RequestNetwork/requestNetwork/commit/74835f0890de5816d0d29c43c1c253ecd756bd6e)) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) +* add the identity ethereumSmartContract to the request logic ([#218](https://github.com/RequestNetwork/requestNetwork/issues/218)) ([66d97e0](https://github.com/RequestNetwork/requestNetwork/commit/66d97e00dee7305088cb94a0edf542fe4d0bbd56)) +* amount are only number or string ([#223](https://github.com/RequestNetwork/requestNetwork/issues/223)) ([7a35bde](https://github.com/RequestNetwork/requestNetwork/commit/7a35bde63f78b9305819a80e97022fca7e9494d2)) +* replace symmetric encryption algorithm by aes-256-gcm ([#233](https://github.com/RequestNetwork/requestNetwork/issues/233)) ([969bebe](https://github.com/RequestNetwork/requestNetwork/commit/969bebeb99b4bc2fdd31405a162934cfdff6db05)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* **request-node:** Add Request Node version and Request Client version to requests header ([#192](https://github.com/RequestNetwork/requestNetwork/issues/192)) ([20ad94b](https://github.com/RequestNetwork/requestNetwork/commit/20ad94b7679b5c08a3951329b1fa8a58c8a3e2df)) +* add an option to disable payment detection in the request client ([#201](https://github.com/RequestNetwork/requestNetwork/issues/201)) ([035302f](https://github.com/RequestNetwork/requestNetwork/commit/035302f70f86fe914d2970417c4b55a6e0a32eda)) +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) +* stateful currency network for ETH and BTC ([#161](https://github.com/RequestNetwork/requestNetwork/issues/161)) ([20ec53e](https://github.com/RequestNetwork/requestNetwork/commit/20ec53ea3be6b98252bdb3dc106eb4eedccd90c1)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.17.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.17.0) (2020-05-04) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* **request-node:** Add Request Node version and Request Client version to requests header ([#192](https://github.com/RequestNetwork/requestNetwork/issues/192)) ([20ad94b](https://github.com/RequestNetwork/requestNetwork/commit/20ad94b7679b5c08a3951329b1fa8a58c8a3e2df)) +* add an option to disable payment detection in the request client ([#201](https://github.com/RequestNetwork/requestNetwork/issues/201)) ([035302f](https://github.com/RequestNetwork/requestNetwork/commit/035302f70f86fe914d2970417c4b55a6e0a32eda)) +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) +* stateful currency network for ETH and BTC ([#161](https://github.com/RequestNetwork/requestNetwork/issues/161)) ([20ec53e](https://github.com/RequestNetwork/requestNetwork/commit/20ec53ea3be6b98252bdb3dc106eb4eedccd90c1)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.16.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.16.0) (2020-04-21) + + +### Features + +* **request-node:** Add Request Node version and Request Client version to requests header ([#192](https://github.com/RequestNetwork/requestNetwork/issues/192)) ([20ad94b](https://github.com/RequestNetwork/requestNetwork/commit/20ad94b7679b5c08a3951329b1fa8a58c8a3e2df)) +* add an option to disable payment detection in the request client ([#201](https://github.com/RequestNetwork/requestNetwork/issues/201)) ([035302f](https://github.com/RequestNetwork/requestNetwork/commit/035302f70f86fe914d2970417c4b55a6e0a32eda)) +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) +* stateful currency network for ETH and BTC ([#161](https://github.com/RequestNetwork/requestNetwork/issues/161)) ([20ec53e](https://github.com/RequestNetwork/requestNetwork/commit/20ec53ea3be6b98252bdb3dc106eb4eedccd90c1)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.15.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.15.0) (2020-04-06) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) +* stateful currency network for ETH and BTC ([#161](https://github.com/RequestNetwork/requestNetwork/issues/161)) ([20ec53e](https://github.com/RequestNetwork/requestNetwork/commit/20ec53ea3be6b98252bdb3dc106eb4eedccd90c1)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.14.0) (2020-03-23) + + +### Bug Fixes + +* reduce number of call to btc providers in the tests ([#153](https://github.com/RequestNetwork/requestNetwork/issues/153)) ([469161b](https://github.com/RequestNetwork/requestNetwork/commit/469161b0a26b43c8bdf8ff7ceb7524dfd3d2029f)) +* stateful currency network for ETH and BTC ([#161](https://github.com/RequestNetwork/requestNetwork/issues/161)) ([20ec53e](https://github.com/RequestNetwork/requestNetwork/commit/20ec53ea3be6b98252bdb3dc106eb4eedccd90c1)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.13.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* merge eth-proxy-contract into eth-input-data ([#139](https://github.com/RequestNetwork/requestNetwork/issues/139)) ([380bfb9](https://github.com/RequestNetwork/requestNetwork/commit/380bfb9d036b04c5bb63d7dfef5f360bc40af985)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.12.0) (2020-01-16) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* mock BTC provider on tests ([#103](https://github.com/RequestNetwork/requestNetwork/issues/103)) ([d17f5bd](https://github.com/RequestNetwork/requestNetwork/commit/d17f5bd841690dcbb2615af126e66116685ee3be)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.11.0) (2019-12-18) + + +### Bug Fixes + +* create an ETH request without refund address ([#82](https://github.com/RequestNetwork/requestNetwork/issues/82)) ([61664cd](https://github.com/RequestNetwork/requestNetwork/commit/61664cd41eef5341678b357a153379dfe2aae14e)) +* use lowercase for payment reference ([#83](https://github.com/RequestNetwork/requestNetwork/issues/83)) ([6cbedeb](https://github.com/RequestNetwork/requestNetwork/commit/6cbedeb4d2e130d7ece1ba526cea9c17d6e545e0)) + + +### Features + +* balance event timestamps ([#78](https://github.com/RequestNetwork/requestNetwork/issues/78)) ([ee2a78f](https://github.com/RequestNetwork/requestNetwork/commit/ee2a78ff5ba83d84739b743db283bb8abfca6b63)) +* **request-client.js:** add erc20 proxy contract PN in request-client.js ([#80](https://github.com/RequestNetwork/requestNetwork/issues/80)) ([53e8839](https://github.com/RequestNetwork/requestNetwork/commit/53e8839fad5a369257b4ba7908bc80abfa53c5f6)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.9.0...@requestnetwork/request-client.js@0.10.0) (2019-12-04) + + +### Bug Fixes + +* use ERC20 default network ([#623](https://github.com/RequestNetwork/requestNetwork/issues/623)) ([772dd37](https://github.com/RequestNetwork/requestNetwork/commit/772dd37877497a38b9cc74a08c70a6c5aecefa2d)) + + +### Features + +* **request-client.js:** add an explanation when request not found ([#609](https://github.com/RequestNetwork/requestNetwork/issues/609)) ([3909958](https://github.com/RequestNetwork/requestNetwork/commit/39099580b65b86282d19a71ffad77f1b89767cca)) +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) +* ETH payment detection in request-client.js ([#626](https://github.com/RequestNetwork/requestNetwork/issues/626)) ([dc3b238](https://github.com/RequestNetwork/requestNetwork/commit/dc3b23827cff7d5466c27d5575515887c461c3b4)) +* exposes currency utilities and list of all supported currencies ([#625](https://github.com/RequestNetwork/requestNetwork/issues/625)) ([eeac838](https://github.com/RequestNetwork/requestNetwork/commit/eeac8385025274fdada39ca3fb2182fc54d470d5)) + + + + + # [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-client.js@0.8.0...@requestnetwork/request-client.js@0.9.0) (2019-11-20) diff --git a/packages/request-client.js/README.md b/packages/request-client.js/README.md index b30a6c17cd..20d6a9af40 100644 --- a/packages/request-client.js/README.md +++ b/packages/request-client.js/README.md @@ -83,6 +83,14 @@ const request = await requestNetwork.createRequest({ contentData, topics, }); + +// wait the confirmation with a promise +await request.waitForConfirmation(); + +// or wait the confirmation with an event +request.on('confirmed', confirmedRequest => { + // ... +}); ``` - `requestInfo`: [IRequestInfo](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/request-client.js/src/types.ts#L42) @@ -117,6 +125,14 @@ const request = await requestNetwork.createEncryptedRequest( }, [encryptionParameters, encryptionParameters2], ); + +// wait the confirmation with a promise +await request.waitForConfirmation(); + +// or wait the confirmation with an event +request.on('confirmed', confirmedRequest => { + // ... +}); ``` - `requestInfo`: [IRequestInfo](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/request-client.js/src/types.ts#L42) @@ -209,7 +225,12 @@ npm install @requestnetwork/epk-decryption ### Accept a request ```javascript -await request.accept(signerIdentity, refundInformation); +const requestData = await request.accept(signerIdentity, refundInformation); + +// wait the confirmation with an event +requestData.on('confirmed', confirmedRequestData => { + // ... +}); ``` - `signerIdentity`: [RequestNetwork.Types.Identity.IIdentity](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/types/src/identity-types.ts#L2) @@ -219,6 +240,11 @@ await request.accept(signerIdentity, refundInformation); ```javascript await request.cancel(signerIdentity, refundInformation); + +// wait the confirmation with an event +requestData.on('confirmed', confirmedRequestData => { + // ... +}); ``` - `signerIdentity`: [RequestNetwork.Types.Identity.IIdentity](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/types/src/identity-types.ts#L2) @@ -228,6 +254,11 @@ await request.cancel(signerIdentity, refundInformation); ```javascript await request.increaseExpectedAmountRequest(amount, signerIdentity, refundInformation); + +// wait the confirmation with an event +requestData.on('confirmed', confirmedRequestData => { + // ... +}); ``` - `amount`: string @@ -238,6 +269,11 @@ await request.increaseExpectedAmountRequest(amount, signerIdentity, refundInform ```javascript await request.reduceExpectedAmountRequest(amount, signerIdentity, paymentInformation); + +// wait the confirmation with an event +requestData.on('confirmed', confirmedRequestData => { + // ... +}); ``` - `amount`: string @@ -264,12 +300,68 @@ const requestData = request.getData(); meta, // see "Metadata of a request" balance, contentData, + pending, // see "Pending state of a request" } */ ``` `requestData.request`: [IRequestData](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/request-client.js/src/types.ts#L17) +#### Pending state of a request + +Requests have a `pending` attribute containing all the changes from transactions still pending. + +In this example, the request is already created but the `accept` and an `increaseExpectedAmount` actions are still pending in the storage (e.g: the ethereum transaction is not mined yet) + +```javascript +{ + ... + state: "created", + currency: "BTC", + expectedAmount: "1000", + payee: {...}, + payer: {...}, + requestId: + "01061052b398b00e08b5fd6e0403a3c83a1fd6b47c21ae57a219866d54d64d74d1", + events: [ + { + name: "create", + ... + } + ], + pending: { + state: "accepted", + expectedAmount: "1200", + events: [ + { + name: "accept", + ... + }, + { + name: "increaseExpectedAmount", + ... + } + ] + } +} +``` + +In the next example, the creation is still pending: + +```javascript +{ + currency: "BTC", + expectedAmount: "1000", + payee: {...}, + payer: {...}, + requestId: + "01061052b398b00e08b5fd6e0403a3c83a1fd6b47c21ae57a219866d54d64d74d1", + state: "pending", + currencyInfo: { type: "BTC", value: "BTC" }, + pending: { state: "created" } +} +``` + #### Metadata of a request In the object returned by `request.getData();`, the property `meta` contains the metadata of the request: @@ -355,8 +447,12 @@ From the information provided in payment network, the library will feed the prop The payment networks available are: -- `Types.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED` ('pn-bitcoin-address-based'): handle Bitcoin payments associated to a BTC address to the request, every transaction hitting this address will be consider as a payment. Eventually, the payer can provide a BTC address for the refunds. Note that **the addresses must be used only for one and only one request** otherwise one transaction will be considered as a payment for more than one request. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0-DRAFT.md)) -- `Types.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED` ('pn-testnet-bitcoin-address-based'): Same as previous but for the bitcoin testnet (for test purpose) +- `Types.Payment.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED` ('pn-bitcoin-address-based'): handle Bitcoin payments associated to a BTC address to the request, every transaction hitting this address will be consider as a payment. Optionally, the payer can provide a BTC address for the refunds. Note that **the addresses must be used only for one and only one request** otherwise one transaction will be considered as a payment for more than one request. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-btc-address-based-0.1.0.md)) +- `Types.Payment.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED` ('pn-testnet-bitcoin-address-based'): Same as previous but for the bitcoin testnet (for test purpose) +- `Types.Payment.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED`('pn-erc20-address-based'): Same as `BITCOIN_ADDRESS_BASED`, for ERC20 payments. +- `Types.Payment.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT`('pn-erc20-proxy-contract'): uses an intermediary contract to document which request is being paid, through the `PaymentReference`. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-erc20-address-based-0.1.0.md)) +- `Types.Payment.PAYMENT_NETWORK_ID.ETH_INPUT_DATA`('pn-eth-input-data'): uses the transaction input data to pass the `PaymentReference`. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-eth-input-data-0.1.0.md)) +- `Types.Payment.PAYMENT_NETWORK_ID.DECLARATIVE`('pn-any-declarative'): a manual alternative, where payer can declare a payment sent, and payee can declare it received, working for any currency. (see [the specification](https://github.com/RequestNetwork/requestNetwork/blob/master/packages/advanced-logic/specs/payment-network-any-declarative-0.1.0.md)) ## Contributing diff --git a/packages/request-client.js/package.json b/packages/request-client.js/package.json index c3b206d99a..dc1a241d9a 100644 --- a/packages/request-client.js/package.json +++ b/packages/request-client.js/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/request-client.js", - "version": "0.9.0", + "version": "0.18.0", "publishConfig": { "access": "public" }, @@ -34,39 +34,40 @@ "build": "run-s build:commonjs build:umd", "build:commonjs": "tsc -b", "build:umd": "webpack", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "docs": "shx rm -rf ./docs && compodoc -p tsconfig.json --output docs --disablePrivate --gaID UA-105153327-8", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "prepare": "yarn run build", - "test": "nyc mocha --require source-map-support/register --require amd-loader \"test/**/*.ts\"", + "test": "nyc mocha --require source-map-support/register --timeout 5000 --require amd-loader \"test/**/*.ts\"", "test:watch": "nyc mocha --watch --watch-extensions ts --require source-map-support/register --require amd-loader \"test/**/*.ts\"" }, "dependencies": { - "@requestnetwork/advanced-logic": "0.6.0", - "@requestnetwork/data-access": "0.5.2", - "@requestnetwork/data-format": "0.4.5", - "@requestnetwork/epk-signature": "0.5.4", - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/request-logic": "0.8.0", - "@requestnetwork/transaction-manager": "0.8.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", - "axios": "0.18.1", + "@requestnetwork/advanced-logic": "0.15.0", + "@requestnetwork/data-access": "0.11.0", + "@requestnetwork/data-format": "0.4.14", + "@requestnetwork/epk-signature": "0.5.13", + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/payment-detection": "0.18.0", + "@requestnetwork/request-logic": "0.14.0", + "@requestnetwork/smart-contracts": "0.9.0", + "@requestnetwork/transaction-manager": "0.14.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", + "axios": "0.19.0", "bn.js": "4.11.8", "currency-codes": "1.5.1", - "eth-contract-metadata": "1.11.0", - "ethers": "4.0.38", - "node-fetch": "2.3.0", - "satoshi-bitcoin": "1.0.4" + "eth-contract-metadata": "1.12.1", + "ethers": "4.0.45" }, "devDependencies": { "@compodoc/compodoc": "1.1.10", "@types/bn.js": "4.11.5", "@types/chai": "4.1.7", "@types/chai-spies": "1.0.0", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@types/node-fetch": "2.1.4", + "@types/sinon": "7.5.0", "@typescript-eslint/parser": "1.2.0", "amd-loader": "0.0.8", "awesome-typescript-loader": "5.2.1", @@ -76,23 +77,23 @@ "chai-spies": "1.0.0", "duplicate-package-checker-webpack-plugin": "3.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", + "mocha": "6.2.2", "npm-run-all": "4.1.5", - "nyc": "13.2.0", - "prettier": "1.16.4", + "nyc": "15.0.0", + "prettier": "1.19.1", "shx": "0.3.2", "sinon": "7.5.0", "source-map-support": "0.5.13", "terser-webpack-plugin": "1.3.0", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2", "webpack": "4.38.0", - "webpack-bundle-analyzer": "3.3.2", - "webpack-cli": "3.3.6" + "webpack-bundle-analyzer": "3.6.0", + "webpack-cli": "3.3.10" }, "gitHead": "6155223cfce769e48ccae480c510b35b4f54b4d0" } diff --git a/packages/request-client.js/src/api/content-data-extension.ts b/packages/request-client.js/src/api/content-data-extension.ts index 29d426ba8c..558c89fa9a 100644 --- a/packages/request-client.js/src/api/content-data-extension.ts +++ b/packages/request-client.js/src/api/content-data-extension.ts @@ -1,9 +1,5 @@ import DataFormat from '@requestnetwork/data-format'; -import { - AdvancedLogicTypes, - ExtensionTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; // Extension ID for this class: content data const CONTENT_DATA_ID = ExtensionTypes.ID.CONTENT_DATA; @@ -49,8 +45,10 @@ export default class ContentDataExtension { * @param request The request of which we want the content * @returns The content */ - public getContent(request: RequestLogicTypes.IRequest): any { - if (request.extensions[CONTENT_DATA_ID]) { + public getContent( + request: RequestLogicTypes.IRequest | RequestLogicTypes.IPendingRequest | null, + ): any { + if (request && request.extensions && request.extensions[CONTENT_DATA_ID]) { return request.extensions[CONTENT_DATA_ID].values.content; } return null; diff --git a/packages/request-client.js/src/api/currency.ts b/packages/request-client.js/src/api/currency.ts index af33beef79..7f6b0433ec 100644 --- a/packages/request-client.js/src/api/currency.ts +++ b/packages/request-client.js/src/api/currency.ts @@ -1,29 +1,47 @@ import { RequestLogicTypes } from '@requestnetwork/types'; -import * as currencyCodes from 'currency-codes'; -import { getErc20Currency, getErc20Decimals, getErc20Symbol } from './currency/erc20'; - -// List of our supported cryptocurrencies -const currencyList = new Map([ - [ - 'BTC', - { +import * as isoCurrencyCodes from 'currency-codes'; +import { + getErc20Currency, + getErc20Decimals, + getErc20Symbol, + getSupportedERC20Tokens, +} from './currency/erc20'; + +// Simple function to get the currency from the value alone +const getCurrency = (currencyValue: string, network: string): RequestLogicTypes.ICurrency => { + // Check if it's a supported cryptocurrency + if (currencyValue === 'BTC') { + return { type: RequestLogicTypes.CURRENCY.BTC, value: 'BTC', - }, - ], - - [ - 'ETH', - { + }; + } + if (currencyValue === 'ETH') { + return { type: RequestLogicTypes.CURRENCY.ETH, value: 'ETH', - }, - ], -]); + }; + } + + // Check if it's an ERC20 token and return it if found + const erc20Currency = getErc20Currency(currencyValue, network); + if (erc20Currency) { + return erc20Currency; + } + + // Check if it's one of ISO4217 currencies + if (isoCurrencyCodes.codes().includes(currencyValue)) { + return { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: currencyValue, + }; + } + throw new Error(`The currency ${currencyValue} is not supported`); +}; /** * Returns a Currency object from a user-friendly currency string. - * The string format is: -. + * The string format is: [CURRENCY_NAME]-[network]. * The network is optional. * E.g: BTC, ETH, ETH-rinkeby, SAI, USD, EUR * @@ -38,38 +56,13 @@ export function stringToCurrency(currencyString: string): RequestLogicTypes.ICur // Split the currency string value and network (if available) const [value, network] = currencyString.split('-'); - // Simple function to get the currency from the value alone - const getCurrency = (): RequestLogicTypes.ICurrency => { - // Check if it's it's a cryptocurrency - if (currencyList.has(value)) { - return currencyList.get(value)!; - } - - // Check if it's an ERC20 token and return it if found - const erc20Currency = getErc20Currency(value, network); - if (erc20Currency) { - return erc20Currency; - } - - // Check if it's one of ISO4217 currencies - if (currencyCodes.codes().includes(value)) { - return { - type: RequestLogicTypes.CURRENCY.ISO4217, - value, - }; - } - throw new Error(`The currency ${value} is not supported`); - }; - - const currency = getCurrency(); + const currency = getCurrency(value, network); // If a network was declared, add it to the currency object if (network) { - if (currency.network !== network) { + if (currency.network && currency.network !== network) { throw new Error( - `You can't declare a network with currency ${value}. It's only available on the network: ${ - currency.network - } `, + `You can't declare a network with currency ${value}. It's only available on the network: ${currency.network} `, ); } currency.network = network; @@ -119,19 +112,17 @@ export function currencyToString(currency: RequestLogicTypes.ICurrency): string * @param currency The currency * @returns The number of decimals */ -export async function getDecimalsForCurrency( - currency: RequestLogicTypes.ICurrency, -): Promise { +export function getDecimalsForCurrency(currency: RequestLogicTypes.ICurrency): number { if (currency.type === RequestLogicTypes.CURRENCY.ERC20) { return getErc20Decimals(currency); } // Return the number of decimals for ISO-4217 currencies if (currency.type === RequestLogicTypes.CURRENCY.ISO4217) { - const iso = currencyCodes.code(currency.value); + const iso = isoCurrencyCodes.code(currency.value); if (!iso) { throw new Error(`Unsupported ISO currency ${currency.value}`); } - return Promise.resolve(iso.digits); + return iso.digits; } const decimals = { @@ -140,7 +131,53 @@ export async function getDecimalsForCurrency( }[currency.type]; if (!decimals) { - throw new Error(`Currency ${currency} not implemented`); + throw new Error(`Currency ${currency.type} not implemented`); } - return Promise.resolve(decimals); + return decimals; +} + +/** + * Returns an object with all the supported currency by type + * + * @returns List of all supported currencies + */ +export function getAllSupportedCurrencies(): { + [type: string]: Array<{ name: string; symbol: string; decimals: number; address?: string }>; +} { + // Creates the list of ISO currencies + const isoCurrencyData = require('currency-codes/data') as isoCurrencyCodes.CurrencyCodeRecord[]; + const isoCurrencies = isoCurrencyData.map(cc => ({ + decimals: cc.digits, + name: cc.currency, + symbol: cc.code, + })); + + // Gets the list of ERC20 currencies + const erc20Currencies = getSupportedERC20Tokens(); + + return { + [RequestLogicTypes.CURRENCY.ETH]: [ + { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + ], + [RequestLogicTypes.CURRENCY.BTC]: [ + { + decimals: 8, + name: 'Bitcoin', + symbol: 'BTC', + }, + ], + [RequestLogicTypes.CURRENCY.ISO4217]: isoCurrencies, + [RequestLogicTypes.CURRENCY.ERC20]: erc20Currencies, + }; } + +export default { + currencyToString, + getAllSupportedCurrencies, + getDecimalsForCurrency, + stringToCurrency, +}; diff --git a/packages/request-client.js/src/api/currency/erc20.ts b/packages/request-client.js/src/api/currency/erc20.ts index 33cf2ee1e8..a17e6bca52 100644 --- a/packages/request-client.js/src/api/currency/erc20.ts +++ b/packages/request-client.js/src/api/currency/erc20.ts @@ -1,6 +1,5 @@ import { RequestLogicTypes } from '@requestnetwork/types'; import { utils } from 'ethers'; -import { getDecimals } from '../payment-network/erc20/info-retriever'; // These interfaces are declared here because they should be used only in this context // A Token description from the eth-contract-metadata list @@ -43,6 +42,24 @@ export const supportedRinkebyERC20 = new Map([ ], ]); +// Additional details about the supported rinkeby ERC20 tokens. +const supportedRinkebyERC20Details = { + // Request Central Bank token, used for testing on rinkeby. + CTBK: { + // Faucet URL: https://central.request.network + address: '0x995d6a8c21f24be1dd04e105dd0d83758343e258', + decimals: 18, + name: 'Central Bank Token', + }, + // Faucet Token on rinkeby network. + FAU: { + // Faucet URL: https://erc20faucet.com/ + address: '0xFab46E002BbF0b4509813474841E0716E6730136', + decimals: 18, + name: 'Faucet Token', + }, +}; + /** * Returns a Currency object for an ERC20, if found * @param symbol The ERC20 token symbol @@ -81,17 +98,26 @@ export function getErc20Currency( * @param currency The ERC20 Currency object * @returns The number of decimals for the ERC20 currency */ -export function getErc20Decimals(currency: RequestLogicTypes.ICurrency): Promise { +export function getErc20Decimals(currency: RequestLogicTypes.ICurrency): number { + let erc20Token; + // Get the decimals from the supported mainnet ERC20 if (!currency.network || currency.network === 'mainnet') { - // Tries to get the decimals from the supported ERC20 currencies list - const erc20Token = getMainnetErc20FromAddress(currency.value); - if (erc20Token) { - return Promise.resolve(erc20Token.decimals); - } + erc20Token = getMainnetErc20FromAddress(currency.value); + } + + // Get the decimals from the supported rinkeby ERC20 + if (currency.network === 'rinkeby') { + erc20Token = Object.values(supportedRinkebyERC20Details).find( + ({ address }) => address === currency.value, + ); + } + + if (erc20Token) { + return erc20Token.decimals; } - // For un-supported ERC20 currencies, we get the decimals from the smart contract - return getDecimals(currency.value, currency.network || 'mainnet'); + // If no supported ERC20 is found, throw error + throw new Error(`Unsupported ERC20 address: ${currency.value}`); } /** @@ -160,3 +186,25 @@ export function getErc20Symbol(currency: RequestLogicTypes.ICurrency): string | return null; } + +/** + * Returns a list of supported ERC20 currencies + * + * @returns List of supported ERC20 currencies + */ +export function getSupportedERC20Tokens(): Array<{ + name: string; + symbol: string; + decimals: number; + address: string; +}> { + return Object.entries(supportedERC20Tokens) + .filter(([, { erc20 }]) => !!erc20) + .map(([address, { name, symbol, decimals }]) => ({ name, symbol, decimals, address })) + .concat( + Object.entries(supportedRinkebyERC20Details).map(([symbol, token]) => ({ + ...token, + symbol: symbol + '-rinkeby', + })), + ); +} diff --git a/packages/request-client.js/src/api/payment-network/erc20/info-retriever.ts b/packages/request-client.js/src/api/payment-network/erc20/info-retriever.ts deleted file mode 100644 index 1d97d5cf96..0000000000 --- a/packages/request-client.js/src/api/payment-network/erc20/info-retriever.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { ethers } from 'ethers'; - -const bigNumber: any = require('bn.js'); - -// The ERC20 smart contract ABI fragment containing decimals property and Transfer event -const erc20BalanceOfAbiFragment = [ - // decimals property - { - constant: true, - inputs: [], - name: 'decimals', - outputs: [ - { - name: '', - type: 'uint8', - }, - ], - payable: false, - stateMutability: 'view', - type: 'function', - }, - // Transfer events - { - anonymous: false, - inputs: [ - { - indexed: true, - name: 'from', - type: 'address', - }, - { - indexed: true, - name: 'to', - type: 'address', - }, - { - indexed: false, - name: 'value', - type: 'uint256', - }, - ], - name: 'Transfer', - type: 'event', - }, -]; - -/** - * Gets a list of transfer events for an address - * - * @param tokenContractAddress The address of the ERC20 contract - * @param address Address of the balance we want to check - * @param network The Ethereum network to use - */ -async function getTransferEvents( - tokenContractAddress: string, - toAddress: string, - network: string, -): Promise<{ - decimals: string; - tokenEvents: Array<{ from: string; to: string; value: string }>; -}> { - // Creates a local or default provider - const provider = - network === 'private' - ? new ethers.providers.JsonRpcProvider() - : ethers.getDefaultProvider(network); - - // Setup the ERC20 contract interface - const contract = new ethers.Contract(tokenContractAddress, erc20BalanceOfAbiFragment, provider); - - // Get the amount of decimals for the ERC20 - const decimals = new bigNumber(await contract.decimals()).toString(); - - // Create a filter to find all the Transfer logs for the toAddress - const filter = contract.filters.Transfer(null, toAddress) as ethers.providers.Filter; - filter.fromBlock = 0; - filter.toBlock = 'latest'; - - // Get the event logs - const logs = await provider.getLogs(filter); - - // Clean up the Transfer logs data - const tokenEvents = logs.map(log => { - const parsedLog = contract.interface.parseLog(log); - return { - from: parsedLog.values.from, - to: parsedLog.values.to, - value: parsedLog.values.value.toString(), - }; - }); - - return { - decimals, - tokenEvents, - }; -} - -export default getTransferEvents; - -/** - * Returns the amount of decimals for an ERC20 token - * - * @param tokenContractAddress The ERC20 contract address - * @param network The ERC20 contract network - * @returns The number of decimals - */ -export async function getDecimals(tokenContractAddress: string, network: string): Promise { - // Connect to the network - const provider = - network === 'private' - ? new ethers.providers.JsonRpcProvider() - : ethers.getDefaultProvider(network); - // Setup the ERC20 contract interface - const contract = new ethers.Contract(tokenContractAddress, erc20BalanceOfAbiFragment, provider); - // Returns the amount of decimals for the ERC20 - return new bigNumber(await contract.decimals()).toNumber(); -} diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index 463acf23a5..daf4229209 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -1,36 +1,39 @@ import { AdvancedLogic } from '@requestnetwork/advanced-logic'; +import { PaymentNetworkFactory } from '@requestnetwork/payment-detection'; import { RequestLogic } from '@requestnetwork/request-logic'; import { TransactionManager } from '@requestnetwork/transaction-manager'; import { AdvancedLogicTypes, DataAccessTypes, DecryptionProviderTypes, + EncryptionTypes, IdentityTypes, + PaymentTypes, RequestLogicTypes, SignatureProviderTypes, TransactionTypes, } from '@requestnetwork/types'; -import { IEncryptionParameters } from '@requestnetwork/types/dist/encryption-types'; import Utils from '@requestnetwork/utils'; import * as Types from '../types'; import ContentDataExtension from './content-data-extension'; import { stringToCurrency } from './currency'; import { validERC20Address } from './currency/erc20'; -import PaymentNetworkFactory from './payment-network/payment-network-factory'; import Request from './request'; +import localUtils from './utils'; /** * Entry point of the request-client.js library. Create requests, get requests, manipulate requests. */ export default class RequestNetwork { + public bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider; + public supportedIdentities: IdentityTypes.TYPE[] = Utils.identity.supportedIdentities; + private requestLogic: RequestLogicTypes.IRequestLogic; private transaction: TransactionTypes.ITransactionManager; private advancedLogic: AdvancedLogicTypes.IAdvancedLogic; private contentData: ContentDataExtension; - private bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider; - /** * @param dataAccess instance of data-access layer * @param signatureProvider module in charge of the signatures @@ -41,7 +44,7 @@ export default class RequestNetwork { dataAccess: DataAccessTypes.IDataAccess, signatureProvider?: SignatureProviderTypes.ISignatureProvider, decryptionProvider?: DecryptionProviderTypes.IDecryptionProvider, - bitcoinDetectionProvider?: Types.IBitcoinDetectionProvider, + bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider, ) { this.advancedLogic = new AdvancedLogic(); this.transaction = new TransactionManager(dataAccess, decryptionProvider); @@ -60,12 +63,20 @@ export default class RequestNetwork { const { requestParameters, topics, paymentNetwork } = await this.prepareRequestParameters( parameters, ); - const { - result: { requestId }, - } = await this.requestLogic.createRequest(requestParameters, parameters.signer, topics); + + const requestLogicCreateResult = await this.requestLogic.createRequest( + requestParameters, + parameters.signer, + topics, + ); // create the request object - const request = new Request(this.requestLogic, requestId, paymentNetwork, this.contentData); + const request = new Request(requestLogicCreateResult.result.requestId, this.requestLogic, { + contentDataExtension: this.contentData, + paymentNetwork, + requestLogicCreateResult, + skipPaymentDetection: parameters.disablePaymentDetection, + }); // refresh the local request data await request.refresh(); @@ -82,14 +93,13 @@ export default class RequestNetwork { */ public async _createEncryptedRequest( parameters: Types.ICreateRequestParameters, - encryptionParams: IEncryptionParameters[], + encryptionParams: EncryptionTypes.IEncryptionParameters[], ): Promise { const { requestParameters, topics, paymentNetwork } = await this.prepareRequestParameters( parameters, ); - const { - result: { requestId }, - } = await this.requestLogic.createEncryptedRequest( + + const requestLogicCreateResult = await this.requestLogic.createEncryptedRequest( requestParameters, parameters.signer, encryptionParams, @@ -97,7 +107,12 @@ export default class RequestNetwork { ); // create the request object - const request = new Request(this.requestLogic, requestId, paymentNetwork, this.contentData); + const request = new Request(requestLogicCreateResult.result.requestId, this.requestLogic, { + contentDataExtension: this.contentData, + paymentNetwork, + requestLogicCreateResult, + skipPaymentDetection: parameters.disablePaymentDetection, + }); // refresh the local request data await request.refresh(); @@ -122,27 +137,43 @@ export default class RequestNetwork { * Create a Request instance from an existing Request's ID * * @param requestId The ID of the Request + * @param options options * @returns the Request */ - public async fromRequestId(requestId: RequestLogicTypes.RequestId): Promise { + public async fromRequestId( + requestId: RequestLogicTypes.RequestId, + options?: { disablePaymentDetection: boolean }, + ): Promise { const requestAndMeta: RequestLogicTypes.IReturnGetRequestFromId = await this.requestLogic.getRequestFromId( requestId, ); - let paymentNetwork: Types.IPaymentNetwork | null = null; - if (requestAndMeta.result.request) { - paymentNetwork = PaymentNetworkFactory.getPaymentNetworkFromRequest({ + // if no request found, throw a human readable message: + if (!requestAndMeta.result.request && !requestAndMeta.result.pending) { + throw new Error(localUtils.formatGetRequestFromIdError(requestAndMeta)); + } + + // get the request state. If the creation is not confirmed yet, get the pending state (useful for the payment network) + const requestState: RequestLogicTypes.IRequest = requestAndMeta.result.request + ? requestAndMeta.result.request + : (requestAndMeta.result.pending as RequestLogicTypes.IRequest); + const paymentNetwork: PaymentTypes.IPaymentNetwork | null = PaymentNetworkFactory.getPaymentNetworkFromRequest( + { advancedLogic: this.advancedLogic, bitcoinDetectionProvider: this.bitcoinDetectionProvider, - request: requestAndMeta.result.request, - }); - } + request: requestState, + }, + ); // create the request object - const request = new Request(this.requestLogic, requestId, paymentNetwork, this.contentData); + const request = new Request(requestId, this.requestLogic, { + contentDataExtension: this.contentData, + paymentNetwork, + skipPaymentDetection: options?.disablePaymentDetection, + }); // refresh the local request data - await request.refresh(); + await request.refresh(requestAndMeta); return request; } @@ -152,16 +183,18 @@ export default class RequestNetwork { * * @param identity * @param updatedBetween filter the requests with time boundaries + * @param options options * @returns the Requests */ public async fromIdentity( identity: IdentityTypes.IIdentity, updatedBetween?: Types.ITimestampBoundaries, + options?: { disablePaymentDetection: boolean }, ): Promise { - if (identity.type !== IdentityTypes.TYPE.ETHEREUM_ADDRESS) { + if (!this.supportedIdentities.includes(identity.type)) { throw new Error(`${identity.type} is not supported`); } - return this.fromTopic(identity, updatedBetween); + return this.fromTopic(identity, updatedBetween, options); } /** @@ -169,21 +202,23 @@ export default class RequestNetwork { * * @param identities * @param updatedBetween filter the requests with time boundaries + * @param disablePaymentDetection if true, skip the payment detection * @returns the requests */ public async fromMultipleIdentities( identities: IdentityTypes.IIdentity[], updatedBetween?: Types.ITimestampBoundaries, + options?: { disablePaymentDetection: boolean }, ): Promise { const identityNotSupported = identities.find( - identity => identity.type !== IdentityTypes.TYPE.ETHEREUM_ADDRESS, + identity => !this.supportedIdentities.includes(identity.type), ); if (identityNotSupported) { throw new Error(`${identityNotSupported.type} is not supported`); } - return this.fromMultipleTopics(identities, updatedBetween); + return this.fromMultipleTopics(identities, updatedBetween, options); } /** @@ -191,11 +226,13 @@ export default class RequestNetwork { * * @param topic * @param updatedBetween filter the requests with time boundaries + * @param options options * @returns the Requests */ public async fromTopic( topic: any, updatedBetween?: Types.ITimestampBoundaries, + options?: { disablePaymentDetection: boolean }, ): Promise { // Gets all the requests indexed by the value of the identity const requestsAndMeta: RequestLogicTypes.IReturnGetRequestsByTopic = await this.requestLogic.getRequestsByTopic( @@ -204,22 +241,29 @@ export default class RequestNetwork { ); // From the requests of the request-logic layer creates the request objects and gets the payment networks const requestPromises = requestsAndMeta.result.requests.map( - async (requestFromLogic: RequestLogicTypes.IRequest): Promise => { - const paymentNetwork: Types.IPaymentNetwork | null = PaymentNetworkFactory.getPaymentNetworkFromRequest( + async (requestFromLogic: { + request: RequestLogicTypes.IRequest | null; + pending: RequestLogicTypes.IPendingRequest | null; + }): Promise => { + // get the request state. If the creation is not confirmed yet, get the pending state (useful for the payment network) + const requestState: RequestLogicTypes.IRequest = requestFromLogic.request + ? requestFromLogic.request + : (requestFromLogic.pending as RequestLogicTypes.IRequest); + + const paymentNetwork: PaymentTypes.IPaymentNetwork | null = PaymentNetworkFactory.getPaymentNetworkFromRequest( { advancedLogic: this.advancedLogic, bitcoinDetectionProvider: this.bitcoinDetectionProvider, - request: requestFromLogic, + request: requestState, }, ); // create the request object - const request = new Request( - this.requestLogic, - requestFromLogic.requestId, + const request = new Request(requestState.requestId, this.requestLogic, { + contentDataExtension: this.contentData, paymentNetwork, - this.contentData, - ); + skipPaymentDetection: options?.disablePaymentDetection, + }); // refresh the local request data await request.refresh(); @@ -236,11 +280,13 @@ export default class RequestNetwork { * * @param topics * @param updatedBetween filter the requests with time boundaries + * @param options options * @returns the Requests */ public async fromMultipleTopics( topics: any[], updatedBetween?: Types.ITimestampBoundaries, + options?: { disablePaymentDetection: boolean }, ): Promise { // Gets all the requests indexed by the value of the identity const requestsAndMeta: RequestLogicTypes.IReturnGetRequestsByTopic = await this.requestLogic.getRequestsByMultipleTopics( @@ -250,22 +296,29 @@ export default class RequestNetwork { // From the requests of the request-logic layer creates the request objects and gets the payment networks const requestPromises = requestsAndMeta.result.requests.map( - async (requestFromLogic: RequestLogicTypes.IRequest): Promise => { - const paymentNetwork: Types.IPaymentNetwork | null = PaymentNetworkFactory.getPaymentNetworkFromRequest( + async (requestFromLogic: { + request: RequestLogicTypes.IRequest | null; + pending: RequestLogicTypes.IPendingRequest | null; + }): Promise => { + // get the request state. If the creation is not confirmed yet, get the pending state (useful for the payment network) + const requestState: RequestLogicTypes.IRequest = requestFromLogic.request + ? requestFromLogic.request + : (requestFromLogic.pending as RequestLogicTypes.IRequest); + + const paymentNetwork: PaymentTypes.IPaymentNetwork | null = PaymentNetworkFactory.getPaymentNetworkFromRequest( { advancedLogic: this.advancedLogic, bitcoinDetectionProvider: this.bitcoinDetectionProvider, - request: requestFromLogic, + request: requestState, }, ); // create the request object - const request = new Request( - this.requestLogic, - requestFromLogic.requestId, + const request = new Request(requestState.requestId, this.requestLogic, { + contentDataExtension: this.contentData, paymentNetwork, - this.contentData, - ); + skipPaymentDetection: options?.disablePaymentDetection, + }); // refresh the local request data await request.refresh(); @@ -287,12 +340,12 @@ export default class RequestNetwork { ): Promise<{ requestParameters: RequestLogicTypes.ICreateParameters; topics: any[]; - paymentNetwork: Types.IPaymentNetwork | null; + paymentNetwork: PaymentTypes.IPaymentNetwork | null; }> { const requestParameters = parameters.requestInfo; const paymentNetworkCreationParameters = parameters.paymentNetwork; const contentData = parameters.contentData; - const topics = parameters.topics || []; + const topics = parameters.topics?.slice() || []; if (requestParameters.extensionsData) { throw new Error('extensionsData in request parameters must be empty'); @@ -316,7 +369,7 @@ export default class RequestNetwork { const copiedRequestParameters = Utils.deepCopy(requestParameters); copiedRequestParameters.extensionsData = []; - let paymentNetwork: Types.IPaymentNetwork | null = null; + let paymentNetwork: PaymentTypes.IPaymentNetwork | null = null; if (paymentNetworkCreationParameters) { paymentNetwork = PaymentNetworkFactory.createPaymentNetwork({ advancedLogic: this.advancedLogic, @@ -328,7 +381,7 @@ export default class RequestNetwork { if (paymentNetwork) { // create the extensions data for the payment network copiedRequestParameters.extensionsData.push( - paymentNetwork.createExtensionsDataForCreation( + await paymentNetwork.createExtensionsDataForCreation( paymentNetworkCreationParameters.parameters, ), ); diff --git a/packages/request-client.js/src/api/request.ts b/packages/request-client.js/src/api/request.ts index 88211b1ae3..e5ed698057 100644 --- a/packages/request-client.js/src/api/request.ts +++ b/packages/request-client.js/src/api/request.ts @@ -1,9 +1,12 @@ -import { IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { EventEmitter } from 'events'; + +import { DeclarativePaymentNetwork as PaymentNetworkDeclarative } from '@requestnetwork/payment-detection'; +import { IdentityTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import * as Types from '../types'; import ContentDataExtension from './content-data-extension'; import { currencyToString } from './currency'; -import PaymentNetworkDeclarative from './payment-network/declarative'; +import localUtils from './utils'; /** * Class representing a request. @@ -19,14 +22,25 @@ export default class Request { public readonly requestId: RequestLogicTypes.RequestId; private requestLogic: RequestLogicTypes.IRequestLogic; - private paymentNetwork: Types.IPaymentNetwork | null = null; + private paymentNetwork: PaymentTypes.IPaymentNetwork | null = null; private contentDataExtension: ContentDataExtension | null; + private emitter: EventEmitter; + + /** + * true if the creation emitted an event 'error' + */ + private confirmationErrorOccurredAtCreation: boolean = false; /** * Data of the request (see request-logic) */ private requestData: RequestLogicTypes.IRequest | null = null; + /** + * Pending data of the request (see request-logic) + */ + private pendingData: RequestLogicTypes.IPendingRequest | null = null; + /** * Content data parsed from the extensions data */ @@ -40,7 +54,12 @@ export default class Request { /** * Balance and payments/refund events */ - private balance: Types.IBalanceWithEvents | null = null; + private balance: PaymentTypes.IBalanceWithEvents | null = null; + + /** + * if true, skip the payment detection + */ + private skipPaymentDetection: boolean = false; /** * Creates an instance of Request @@ -49,17 +68,63 @@ export default class Request { * @param requestId ID of the Request * @param paymentNetwork Instance of a payment network to manage the request * @param contentDataManager Instance of content data manager + * @param requestLogicCreateResult return from the first request creation (optimization) + * @param options options */ constructor( - requestLogic: RequestLogicTypes.IRequestLogic, requestId: RequestLogicTypes.RequestId, - paymentNetwork?: Types.IPaymentNetwork | null, - contentDataExtension?: ContentDataExtension | null, + requestLogic: RequestLogicTypes.IRequestLogic, + options?: { + paymentNetwork?: PaymentTypes.IPaymentNetwork | null; + contentDataExtension?: ContentDataExtension | null; + requestLogicCreateResult?: RequestLogicTypes.IReturnCreateRequest; + skipPaymentDetection?: boolean; + }, ) { this.requestLogic = requestLogic; this.requestId = requestId; - this.contentDataExtension = contentDataExtension || null; - this.paymentNetwork = paymentNetwork || null; + this.contentDataExtension = options?.contentDataExtension || null; + this.paymentNetwork = options?.paymentNetwork || null; + this.emitter = new EventEmitter(); + this.skipPaymentDetection = options?.skipPaymentDetection || false; + + if (options && options.requestLogicCreateResult) { + options.requestLogicCreateResult + .on('confirmed', async () => { + this.emitter.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.confirmationErrorOccurredAtCreation = true; + this.emitter.emit('error', error); + }); + } + } + + /** + * Listen the confirmation of the creation + * + * @param type only "confirmed" event for now + * @param callback callback to call when confirmed event is risen + * @returns this + */ + public on( + event: K, + listener: Types.IRequestEvents[K], + ): this { + this.emitter.on(event, listener); + return this; + } + + /** + * Wait for the confirmation + * + * @returns the request data + */ + public waitForConfirmation(): Promise { + return new Promise((resolve, reject): any => { + this.on('confirmed', resolve); + this.on('error', reject); + }); } /** @@ -72,7 +137,7 @@ export default class Request { public async accept( signerIdentity: IdentityTypes.IIdentity, refundInformation?: any, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (refundInformation) { if (!this.paymentNetwork) { @@ -87,10 +152,20 @@ export default class Request { requestId: this.requestId, }; - await this.requestLogic.acceptRequest(parameters, signerIdentity, true); + const acceptResult = await this.requestLogic.acceptRequest(parameters, signerIdentity, true); + + // refresh the local request data + const requestData = await this.refresh(); + + acceptResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); - // refresh the local request data and return it - return this.refresh(); + return requestData; } /** @@ -103,7 +178,7 @@ export default class Request { public async cancel( signerIdentity: IdentityTypes.IIdentity, refundInformation?: any, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (refundInformation) { if (!this.paymentNetwork) { @@ -119,10 +194,20 @@ export default class Request { requestId: this.requestId, }; - await this.requestLogic.cancelRequest(parameters, signerIdentity, true); + const cancelResult = await this.requestLogic.cancelRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + // refresh the local request data + const requestData = await this.refresh(); + + cancelResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -137,7 +222,7 @@ export default class Request { deltaAmount: RequestLogicTypes.Amount, signerIdentity: IdentityTypes.IIdentity, refundInformation?: any, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (refundInformation) { if (!this.paymentNetwork) { @@ -152,10 +237,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.increaseExpectedAmountRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const increaseExpectedResult = await this.requestLogic.increaseExpectedAmountRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + increaseExpectedResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -170,7 +270,7 @@ export default class Request { deltaAmount: RequestLogicTypes.Amount, signerIdentity: IdentityTypes.IIdentity, paymentInformation?: any, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (paymentInformation) { if (!this.paymentNetwork) { @@ -187,10 +287,24 @@ export default class Request { requestId: this.requestId, }; - await this.requestLogic.reduceExpectedAmountRequest(parameters, signerIdentity, true); + const reduceExpectedResult = await this.requestLogic.reduceExpectedAmountRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); - // refresh the local request data and return it - return this.refresh(); + reduceExpectedResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -203,7 +317,7 @@ export default class Request { public async addPaymentInformation( paymentInformation: any, signerIdentity: IdentityTypes.IIdentity, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (!this.paymentNetwork) { @@ -218,10 +332,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.addExtensionsDataRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const addExtensionResult = await this.requestLogic.addExtensionsDataRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + addExtensionResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -234,7 +363,7 @@ export default class Request { public async addRefundInformation( refundInformation: any, signerIdentity: IdentityTypes.IIdentity, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (!this.paymentNetwork) { @@ -249,10 +378,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.addExtensionsDataRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const addExtensionResult = await this.requestLogic.addExtensionsDataRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + addExtensionResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -264,10 +408,10 @@ export default class Request { * @returns The updated request */ public async declareSentPayment( - amount: string, + amount: RequestLogicTypes.Amount, note: string, signerIdentity: IdentityTypes.IIdentity, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (!this.paymentNetwork) { @@ -290,10 +434,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.addExtensionsDataRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const addExtensionResult = await this.requestLogic.addExtensionsDataRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + addExtensionResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -305,10 +464,10 @@ export default class Request { * @returns The updated request */ public async declareSentRefund( - amount: string, + amount: RequestLogicTypes.Amount, note: string, signerIdentity: IdentityTypes.IIdentity, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (!this.paymentNetwork) { @@ -334,10 +493,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.addExtensionsDataRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const addExtensionResult = await this.requestLogic.addExtensionsDataRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + addExtensionResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -349,10 +523,10 @@ export default class Request { * @returns The updated request */ public async declareReceivedPayment( - amount: string, + amount: RequestLogicTypes.Amount, note: string, signerIdentity: IdentityTypes.IIdentity, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (!this.paymentNetwork) { @@ -378,10 +552,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.addExtensionsDataRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const addExtensionResult = await this.requestLogic.addExtensionsDataRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + addExtensionResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -393,10 +582,10 @@ export default class Request { * @returns The updated request */ public async declareReceivedRefund( - amount: string, + amount: RequestLogicTypes.Amount, note: string, signerIdentity: IdentityTypes.IIdentity, - ): Promise { + ): Promise { const extensionsData: any[] = []; if (!this.paymentNetwork) { @@ -422,10 +611,25 @@ export default class Request { extensionsData, requestId: this.requestId, }; - await this.requestLogic.addExtensionsDataRequest(parameters, signerIdentity, true); - // refresh the local request data and return it - return this.refresh(); + const addExtensionResult = await this.requestLogic.addExtensionsDataRequest( + parameters, + signerIdentity, + true, + ); + + // refresh the local request data + const requestData = await this.refresh(); + + addExtensionResult + .on('confirmed', async () => { + requestData.emit('confirmed', await this.refresh()); + }) + .on('error', error => { + this.emitter.emit('error', error); + }); + + return requestData; } /** @@ -433,45 +637,100 @@ export default class Request { * * @returns The updated request data */ - public getData(): Types.IRequestData { - const requestData: RequestLogicTypes.IRequest = Utils.deepCopy(this.requestData); - const result: Types.IRequestData = { + public getData(): Types.IRequestDataWithEvents { + if (this.confirmationErrorOccurredAtCreation) { + throw Error('request confirmation failed'); + } + + let requestData: RequestLogicTypes.IRequest = Utils.deepCopy(this.requestData); + + let pending = Utils.deepCopy(this.pendingData); + if (!requestData) { + requestData = pending; + requestData.state = RequestLogicTypes.STATE.PENDING; + pending = { state: this.pendingData!.state }; + } + + return Object.assign(new EventEmitter(), { ...requestData, balance: this.balance, contentData: this.contentData, currency: requestData.currency ? currencyToString(requestData.currency) : 'unknown', currencyInfo: requestData.currency, meta: this.requestMeta, - }; - - return result; + pending, + }); } /** * Refresh the request data and balance from the network (check if new events happened - e.g: accept, payments etc..) and return these data * + * @param requestAndMeta return from getRequestFromId to avoid asking twice * @returns Refreshed request data */ - public async refresh(): Promise { - const requestAndMeta: RequestLogicTypes.IReturnGetRequestFromId = await this.requestLogic.getRequestFromId( - this.requestId, - ); - - if (!requestAndMeta.result.request) { - throw new Error(`No request found for the id: ${this.requestId}`); + public async refresh( + requestAndMeta?: RequestLogicTypes.IReturnGetRequestFromId, + ): Promise { + if (this.confirmationErrorOccurredAtCreation) { + throw Error('request confirmation failed'); + } + if (!requestAndMeta) { + requestAndMeta = await this.requestLogic.getRequestFromId(this.requestId); } - if (this.paymentNetwork) { - this.balance = await this.paymentNetwork.getBalance(requestAndMeta.result.request); + if (!requestAndMeta.result.request && !requestAndMeta.result.pending) { + throw new Error( + `No request found for the id: ${this.requestId} - ${localUtils.formatGetRequestFromIdError( + requestAndMeta, + )}`, + ); } if (this.contentDataExtension) { - this.contentData = await this.contentDataExtension.getContent(requestAndMeta.result.request); + // TODO: PROT-1131 - add a pending content + this.contentData = await this.contentDataExtension.getContent( + requestAndMeta.result.request || requestAndMeta.result.pending, + ); } this.requestData = requestAndMeta.result.request; + this.pendingData = requestAndMeta.result.pending; this.requestMeta = requestAndMeta.meta; + if (!this.skipPaymentDetection) { + // let's refresh the balance + await this.refreshBalance(); + } + return this.getData(); } + + /** + * Refresh only the balance of the request and return it + * + * @returns return the balance + */ + public async refreshBalance(): Promise | null> { + // TODO: PROT-1131 - add a pending balance + this.balance = + this.paymentNetwork && this.requestData + ? await this.paymentNetwork.getBalance(this.requestData) + : this.balance; + + return this.balance; + } + + /** + * Enables the payment detection + */ + public enablePaymentDetection(): void { + this.skipPaymentDetection = false; + } + + /** + * Disables the payment detection + */ + public disablePaymentDetection(): void { + this.skipPaymentDetection = true; + } } diff --git a/packages/request-client.js/src/api/utils.ts b/packages/request-client.js/src/api/utils.ts index 343ce79b55..a7daf9a4c2 100644 --- a/packages/request-client.js/src/api/utils.ts +++ b/packages/request-client.js/src/api/utils.ts @@ -1,9 +1,11 @@ +import { RequestLogicTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import { getDecimalsForCurrency } from './currency'; /** * Collection of utils functions related to the library, meant to simplify its use. */ export default { + formatGetRequestFromIdError, getDecimalsForCurrency, /** @@ -13,3 +15,27 @@ export default { */ getCurrentTimestampInSecond: Utils.getCurrentTimestampInSecond, }; + +/** + * Formats a human readable message from ignored transactions + * + * @param requestAndMeta return from GetRequestFromId + * @returns human readable message + */ +function formatGetRequestFromIdError( + requestAndMeta: RequestLogicTypes.IReturnGetRequestFromId, +): string { + if ( + (requestAndMeta.meta.ignoredTransactions && + requestAndMeta.meta.ignoredTransactions.length !== 0) || + requestAndMeta.meta.transactionManagerMeta.ignoredTransactions.length !== 0 + ) { + return `Invalid transaction(s) found: ${JSON.stringify( + requestAndMeta.meta.transactionManagerMeta.ignoredTransactions + .concat(requestAndMeta.meta.ignoredTransactions || []) + .filter((tx: any) => tx !== null), + )}`; + } + + return 'No transaction found'; +} diff --git a/packages/request-client.js/src/http-data-access.ts b/packages/request-client.js/src/http-data-access.ts index c7ed9f701a..00f2f5648b 100644 --- a/packages/request-client.js/src/http-data-access.ts +++ b/packages/request-client.js/src/http-data-access.ts @@ -2,12 +2,24 @@ import { DataAccessTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; import axios, { AxiosRequestConfig } from 'axios'; +import { EventEmitter } from 'events'; + +const packageJson = require('../package.json'); + +const REQUEST_CLIENT_VERSION_HEADER = 'X-Request-Network-Client-Version'; + // Maximum number of retries to attempt when http requests to the Node fail const HTTP_REQUEST_MAX_RETRY = 3; // Delay between retry in ms const HTTP_REQUEST_RETRY_DELAY = 100; +// Maximum number of retries to get the confirmation of a persistTransaction +const GET_CONFIRMATION_MAX_RETRY = 500; + +// Delay between retry in ms to get the confirmation of a persistTransaction +const GET_CONFIRMATION_RETRY_DELAY = 3000; + /** * Exposes a Data-Access module over HTTP */ @@ -16,16 +28,22 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess { * Configuration that will be sent to axios for each request. * We can also create a AxiosInstance with axios.create() but it dramatically complicates testing. */ - private axiosConfig: AxiosRequestConfig; + protected axiosConfig: AxiosRequestConfig; /** * Creates an instance of HttpDataAccess. * @param nodeConnectionConfig Configuration options to connect to the node. Follows Axios configuration format. */ constructor(nodeConnectionConfig: AxiosRequestConfig = {}) { + // Get Request Client version to set it in the header + const requestClientVersion = packageJson.version; + this.axiosConfig = Object.assign( { baseURL: 'http://localhost:3000', + headers: { + [REQUEST_CLIENT_VERSION_HEADER]: requestClientVersion, + }, }, nodeConnectionConfig, ); @@ -65,7 +83,45 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess { this.axiosConfig, ); - return data; + const transactionHash: string = Utils.crypto.normalizeKeccak256Hash(transactionData).value; + + // Create the return result with EventEmitter + const result: DataAccessTypes.IReturnPersistTransaction = Object.assign( + new EventEmitter(), + data, + ); + + // Try to get the confirmation + Utils.retry( + async () => { + return axios.get( + '/getConfirmedTransaction', + Object.assign(this.axiosConfig, { + params: { transactionHash }, + }), + ); + }, + { + maxRetries: GET_CONFIRMATION_MAX_RETRY, + retryDelay: GET_CONFIRMATION_RETRY_DELAY, + }, + )() + .then((resultConfirmed: any) => { + // when found, emit the event 'confirmed' + result.emit('confirmed', resultConfirmed.data); + }) + .catch((e: any) => { + // tslint:disable-next-line:no-magic-numbers + if (e.response.status === 404) { + throw new Error( + `Transaction confirmation not receive after ${GET_CONFIRMATION_MAX_RETRY} retries`, + ); + } else { + throw new Error(e.message); + } + }); + + return result; } /** @@ -148,4 +204,27 @@ export default class HttpDataAccess implements DataAccessTypes.IDataAccess { return data; } + + /** + * Gets information from the node (version, files etc...) + * + * @param detailed if true get the list of files hashes + */ + public async _getStatus(detailed?: boolean): Promise { + const { data } = await Utils.retry( + async () => + axios.get( + '/information', + Object.assign(this.axiosConfig, { + params: { detailed }, + }), + ), + { + maxRetries: HTTP_REQUEST_MAX_RETRY, + retryDelay: HTTP_REQUEST_RETRY_DELAY, + }, + )(); + + return data; + } } diff --git a/packages/request-client.js/src/http-metamask-data-access.ts b/packages/request-client.js/src/http-metamask-data-access.ts new file mode 100644 index 0000000000..ee0cdd87fa --- /dev/null +++ b/packages/request-client.js/src/http-metamask-data-access.ts @@ -0,0 +1,270 @@ +import { Block } from '@requestnetwork/data-access'; +import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; +import { DataAccessTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; +import axios, { AxiosRequestConfig } from 'axios'; +import { ethers } from 'ethers'; +import { EventEmitter } from 'events'; +import HttpDataAccess from './http-data-access'; + +// Maximum number of retries to attempt when http requests to the Node fail +const HTTP_REQUEST_MAX_RETRY = 3; + +// Delay between retry in ms +const HTTP_REQUEST_RETRY_DELAY = 100; + +/** + * Exposes a Data-Access module over HTTP + */ +export default class HttpMetaMaskDataAccess extends HttpDataAccess { + /** + * Cache block persisted directly (in case the node did not have the time to retrieve it) + * (public for easier testing) + */ + public cache: { + [channelId: string]: { + [ipfsHash: string]: { block: DataAccessTypes.IBlock; storageMeta: any } | null; + }; + } = {}; + + private submitterContract: ethers.Contract | undefined; + private provider: ethers.providers.JsonRpcProvider | ethers.providers.Web3Provider; + private networkName: string = ''; + + /** + * Creates an instance of HttpDataAccess. + * @param nodeConnectionConfig Configuration options to connect to the node. Follows Axios configuration format. + */ + constructor( + { + nodeConnectionConfig, + web3, + ethereumProviderUrl, + }: { + nodeConnectionConfig?: AxiosRequestConfig; + web3?: any; + ethereumProviderUrl?: string; + } = { + nodeConnectionConfig: {}, + }, + ) { + super(nodeConnectionConfig); + + ethereumProviderUrl = ethereumProviderUrl ? ethereumProviderUrl : 'http://localhost:8545'; + + // Creates a local or default provider + this.provider = web3 + ? new ethers.providers.Web3Provider(web3.currentProvider) + : new ethers.providers.JsonRpcProvider({ url: ethereumProviderUrl }); + } + + /** + * Initialize the module. Does nothing, exists only to implement IDataAccess + * + * @returns nothing + */ + public async initialize(): Promise { + // no-op, nothing to do + return; + } + + /** + * Persists a new transaction using the node only for IPFS but persisting on ethereum through local provider + * + * @param transactionData The transaction data + * @param topics The topics used to index the transaction + */ + public async persistTransaction( + transactionData: DataAccessTypes.ITransaction, + channelId: string, + topics?: string[], + ): Promise { + if (!this.submitterContract) { + const network = await this.provider.getNetwork(); + + this.networkName = + network.chainId === 1 ? 'mainnet' : network.chainId === 4 ? 'rinkeby' : 'private'; + + this.submitterContract = new ethers.Contract( + requestHashSubmitterArtifact.getAddress(this.networkName), + requestHashSubmitterArtifact.getContractAbi(), + this.provider.getSigner(), + ); + } + + // We don't use the node to persist the transaction, but we will Do it ourselves + + // create a block and add the transaction in it + const block: DataAccessTypes.IBlock = Block.pushTransaction( + Block.createEmptyBlock(), + transactionData, + channelId, + topics, + ); + + // store the block on ipfs and get the the ipfs hash and size + const { + data: { ipfsHash, ipfsSize }, + } = await axios.post('/ipfsAdd', { data: block }, this.axiosConfig); + + // get the fee required to submit the hash + const fee = await this.submitterContract.getFeesAmount(ipfsSize); + + // submit the hash to ethereum + const tx = await this.submitterContract.submitHash( + ipfsHash, + // tslint:disable:no-magic-numbers + ethers.utils.hexZeroPad(ethers.utils.hexlify(ipfsSize), 32), + { value: fee }, + ); + + const ethBlock = await this.provider.getBlock(tx.blockNumber); + + // create the storage meta from the transaction receipt + const storageMeta = { + blockConfirmation: tx.confirmations, + blockNumber: tx.blockNumber, + blockTimestamp: ethBlock.timestamp, + fee, + networkName: this.networkName, + smartContractAddress: tx.to, + transactionHash: tx.hash, + }; + + // Add the block to the cache + if (!this.cache[channelId]) { + this.cache[channelId] = {}; + } + this.cache[channelId][ipfsHash] = { block, storageMeta }; + + const result: DataAccessTypes.IReturnPersistTransaction = Object.assign(new EventEmitter(), { + meta: { + storageMeta, + topics: topics || [], + transactionStorageLocation: ipfsHash, + }, + result: {}, + }); + + // When the ethereum transaction is mined, emit an event 'confirmed' + tx.wait().then((txConfirmed: any) => { + // create the storage meta from the transaction receipt + const storageMetaConfirmed = { + blockConfirmation: txConfirmed.confirmations, + blockNumber: txConfirmed.blockNumber, + blockTimestamp: ethBlock.timestamp, + fee, + networkName: this.networkName, + smartContractAddress: txConfirmed.to, + transactionHash: txConfirmed.hash, + }; + + // emit the event to tell the request transaction is confirmed + result.emit('confirmed', { + meta: { + storageMeta: storageMetaConfirmed, + topics: topics || [], + transactionStorageLocation: ipfsHash, + }, + result: {}, + }); + }); + + return result; + } + + /** + * Gets the transactions for a channel from the node through HTTP. + * + * @param channelId The channel id to search for + * @param timestampBoundaries filter timestamp boundaries + */ + public async getTransactionsByChannelId( + channelId: string, + timestampBoundaries?: DataAccessTypes.ITimestampBoundaries, + ): Promise { + const { data } = await Utils.retry( + async () => + axios.get( + '/getTransactionsByChannelId', + Object.assign(this.axiosConfig, { + params: { channelId, timestampBoundaries }, + }), + ), + { + maxRetries: HTTP_REQUEST_MAX_RETRY, + retryDelay: HTTP_REQUEST_RETRY_DELAY, + }, + )(); + + // get the transactions from the cache + const transactionsCached: DataAccessTypes.IReturnGetTransactions = this.getCachedTransactionsAndCleanCache( + channelId, + data.meta.transactionsStorageLocation, + timestampBoundaries, + ); + + // merge cache and data from the node + return { + meta: { + storageMeta: data.meta.storageMeta.concat(transactionsCached.meta.storageMeta), + transactionsStorageLocation: data.meta.transactionsStorageLocation.concat( + transactionsCached.meta.transactionsStorageLocation, + ), + }, + result: { + transactions: data.result.transactions.concat(transactionsCached.result.transactions), + }, + }; + } + + /** + * Gets the cached transactions and remove the ones that have been retrieved from the node + * (public for easier testing) + * + * @param channelId The channel id to search for + * @param storageLocationFromNode location retrieved from the node + * @param timestampBoundaries filter timestamp boundaries + */ + public getCachedTransactionsAndCleanCache( + channelId: string, + storageLocationFromNode: string[], + timestampBoundaries?: DataAccessTypes.ITimestampBoundaries, + ): DataAccessTypes.IReturnGetTransactions { + // Remove cache found by the node + for (const location of storageLocationFromNode) { + this.cache[channelId][location] = null; + } + + // Create a IReturnGetTransactions object to be merged later with the one from the node + return Object.keys(this.cache[channelId] || []).reduce( + (accumulator: DataAccessTypes.IReturnGetTransactions, location: string) => { + const cache = this.cache[channelId][location]; + + // For each cached block for the channel, we return the transaction if they are in the time boundaries + if ( + this.cache[channelId][location] && + (!timestampBoundaries || + ((timestampBoundaries.from === undefined || + timestampBoundaries.from <= cache?.storageMeta.blockTimestamp) && + (timestampBoundaries.to === undefined || + timestampBoundaries.to >= cache?.storageMeta.blockTimestamp))) + ) { + accumulator.meta.storageMeta.push(cache?.storageMeta); + accumulator.meta.transactionsStorageLocation.push(location); + // cache?.block.transactions will always contain one transaction + accumulator.result.transactions.push({ + state: DataAccessTypes.TransactionState.PENDING, + timestamp: cache?.storageMeta.blockTimestamp, + transaction: cache?.block.transactions[0] as DataAccessTypes.ITransaction, + }); + } + return accumulator; + }, + { + meta: { storageMeta: [], transactionsStorageLocation: [] }, + result: { transactions: [] }, + }, + ); + } +} diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index c5751a5db3..7e9740597e 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -6,6 +6,7 @@ import { import { AxiosRequestConfig } from 'axios'; import RequestNetwork from './api/request-network'; import HttpDataAccess from './http-data-access'; +import HttpMetaMaskDataAccess from './http-metamask-data-access'; import MockDataAccess from './mock-data-access'; import MockStorage from './mock-storage'; @@ -13,35 +14,59 @@ import MockStorage from './mock-storage'; * Exposes RequestNetwork module configured to use http-data-access. */ export default class HttpRequestNetwork extends RequestNetwork { + /** Public for test purpose */ + public _mockStorage: MockStorage | undefined; + /** * Creates an instance of HttpRequestNetwork. * * @param options.nodeConnectionConfig Configuration options to connect to the node. Follows Axios configuration format. * @param options.useMockStorage When true, will use a mock storage in memory. Meant to simplify local development and should never be used in production. * @param options.signatureProvider Module to handle the signature. If not given it will be impossible to create new transaction (it requires to sign). + * @param options.useLocalEthereumBroadcast When true, persisting use the node only for IPFS but persisting on ethereum through local provider (given in ethereumProviderUrl). + * @param options.ethereumProviderUrl Url of the Ethereum provider use to persist transactions if useLocalEthereumBroadcast is true. + * */ constructor( { decryptionProvider, nodeConnectionConfig, + useLocalEthereumBroadcast, signatureProvider, useMockStorage, + web3, + ethereumProviderUrl, }: { decryptionProvider?: DecryptionProviderTypes.IDecryptionProvider; nodeConnectionConfig?: AxiosRequestConfig; signatureProvider?: SignatureProviderTypes.ISignatureProvider; useMockStorage?: boolean; + useLocalEthereumBroadcast?: boolean; + web3?: any; + ethereumProviderUrl?: string; } = { nodeConnectionConfig: {}, + useLocalEthereumBroadcast: false, useMockStorage: false, }, ) { - // useMockStorage === true => use mock data-access - // useMockStorage === false => use http data-access + let _mockStorage: MockStorage | undefined; + if (useMockStorage) { + _mockStorage = new MockStorage(); + } const dataAccess: DataAccessTypes.IDataAccess = useMockStorage - ? new MockDataAccess(new MockStorage()) - : new HttpDataAccess(nodeConnectionConfig); + ? // useMockStorage === true => use mock data-access + new MockDataAccess(_mockStorage!) + : // useMockStorage === false + useLocalEthereumBroadcast + ? // useLocalEthereumBroadcast === true => use http-metamask-data-access + new HttpMetaMaskDataAccess({ nodeConnectionConfig, web3, ethereumProviderUrl }) + : // useLocalEthereumBroadcast === false => use http-data-access + new HttpDataAccess(nodeConnectionConfig); super(dataAccess, signatureProvider, decryptionProvider); + + // store it for test purpose + this._mockStorage = _mockStorage; } } diff --git a/packages/request-client.js/src/index.ts b/packages/request-client.js/src/index.ts index aab55a5316..aaee5cf870 100644 --- a/packages/request-client.js/src/index.ts +++ b/packages/request-client.js/src/index.ts @@ -1,6 +1,9 @@ +import { PaymentReferenceCalculator } from '@requestnetwork/payment-detection'; + +import Currency from './api/currency'; import Request from './api/request'; import Utils from './api/utils'; import { default as RequestNetwork } from './http-request-network'; import * as Types from './types'; -export { Request, RequestNetwork, Types, Utils }; +export { Currency, PaymentReferenceCalculator, Request, RequestNetwork, Types, Utils }; diff --git a/packages/request-client.js/src/mock-storage.ts b/packages/request-client.js/src/mock-storage.ts index 916874829e..e80f86d0f2 100644 --- a/packages/request-client.js/src/mock-storage.ts +++ b/packages/request-client.js/src/mock-storage.ts @@ -1,18 +1,25 @@ import MultiFormat from '@requestnetwork/multi-format'; import { StorageTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; +import { EventEmitter } from 'events'; /** * Storage layer implemented with in-memory hashmap, to be used for testing. */ export default class MockStorage implements StorageTypes.IStorage { - private data: { [key: string]: { content: string; timestamp: number } } = {}; + private data: Map< + string, + { state: StorageTypes.ContentState; content: string; timestamp: number } + > = new Map(); + + // For test purpose we can force the next append call to emit Error + private forceEmitError: boolean = false; public async initialize(): Promise { return; } - public async append(content: string): Promise { + public async _ipfsAdd(content: string): Promise { if (!content) { throw Error('Error: no content provided'); } @@ -20,28 +27,77 @@ export default class MockStorage implements StorageTypes.IStorage { const nowTimestampInSec = Utils.getCurrentTimestampInSecond(); - this.data[hash] = { content, timestamp: nowTimestampInSec }; + this.data.set(hash, { + content, + state: StorageTypes.ContentState.PENDING, + timestamp: nowTimestampInSec, + }); return { - content: '', + ipfsHash: hash, + ipfsSize: content.length, + }; + } + + public async append(content: string): Promise { + if (!content) { + throw Error('Error: no content provided'); + } + const hash = MultiFormat.serialize(Utils.crypto.normalizeKeccak256Hash(content)); + + const nowTimestampInSec = Utils.getCurrentTimestampInSecond(); + + const dataToStore = { + content, + state: StorageTypes.ContentState.PENDING, + timestamp: nowTimestampInSec, + }; + + this.data.set(hash, dataToStore); + + const resultData = { + content, id: hash, meta: { + state: StorageTypes.ContentState.PENDING, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, timestamp: nowTimestampInSec, }, }; + const result = Object.assign(new EventEmitter(), resultData); + + setTimeout(() => { + if (this.forceEmitError) { + // emit error + this.forceEmitError = false; + result.emit('error', 'forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); + } else { + // emit confirmed + dataToStore.state = StorageTypes.ContentState.CONFIRMED; + this.data.set(hash, dataToStore); + result.emit('confirmed', resultData); + } + // tslint:disable-next-line:no-magic-numbers + }, 100); + + return result; } public async read(id: string): Promise { if (!id) { throw Error('No id provided'); } + const data = this.data.get(id); + if (!data) { + throw Error('No content found from this id'); + } return { - content: this.data[id].content, + content: data.content, id, meta: { + state: data.state, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, - timestamp: this.data[id].timestamp, + timestamp: data.timestamp, }, }; } @@ -51,10 +107,11 @@ export default class MockStorage implements StorageTypes.IStorage { } public async getData(): Promise { - const entries = Object.entries(this.data).map(([id, { content, timestamp }]) => ({ + const entries = Array.from(this.data.entries()).map(([id, { content, state, timestamp }]) => ({ content, id, meta: { + state, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, timestamp, }, @@ -67,4 +124,30 @@ export default class MockStorage implements StorageTypes.IStorage { lastTimestamp: nowTimestampInSec, }; } + + public async getIgnoredData(): Promise { + return []; + } + + /** + * Gets information + * + * @param detailed if true get the list of files hash + */ + public async _getStatus(detailed?: boolean): Promise { + return { + dataIds: { + count: Object.entries(this.data).length, + values: detailed ? Object.entries(this.data) : undefined, + }, + ignoredDataIds: { + count: 0, + values: detailed ? [] : undefined, + }, + }; + } + + public _makeNextAppendFailInsteadOfConfirmed(): void { + this.forceEmitError = true; + } } diff --git a/packages/request-client.js/src/types.ts b/packages/request-client.js/src/types.ts index 74b587d79b..89049617ac 100644 --- a/packages/request-client.js/src/types.ts +++ b/packages/request-client.js/src/types.ts @@ -1,6 +1,5 @@ -import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; +export * from '@requestnetwork/types/dist/client-types'; -// Export all the types to avoid the users to import them beside the present module export { AdvancedLogicTypes as AdvancedLogic, LogTypes as Log, @@ -14,105 +13,5 @@ export { IdentityTypes as Identity, StorageTypes as Storage, TransactionTypes as Transaction, + PaymentTypes as Payment, } from '@requestnetwork/types'; - -/** Restrict research to two timestamp */ -export interface ITimestampBoundaries { - from?: number; - to?: number; -} - -/** Interface request data */ -// TODO: when upgrading typescript to 3.5+ we should use Omit instead of Pick+Exclude -export interface IRequestData - extends Pick> { - currency: string; - meta: RequestLogicTypes.IReturnMeta | null; - balance: IBalanceWithEvents | null; - contentData: any; - currencyInfo: RequestLogicTypes.ICurrency; -} - -/** Create request parameters */ -export interface ICreateRequestParameters { - requestInfo: RequestLogicTypes.ICreateParameters | IRequestInfo; - signer: IdentityTypes.IIdentity; - paymentNetwork?: IPaymentNetworkCreateParameters; - topics?: string[]; - contentData?: any; -} - -/** Parameters to create a request. ICreateParameters with a more flexible currency */ -export interface IRequestInfo { - currency: string | RequestLogicTypes.ICurrency; - expectedAmount: RequestLogicTypes.Amount; - payee?: IdentityTypes.IIdentity; - payer?: IdentityTypes.IIdentity; - extensionsData?: any[]; - timestamp?: number; - nonce?: number; -} - -/** Object interface to list the payment network id and its module by currency */ -export interface ISupportedPaymentNetworkByCurrency { - [currency: string]: ISupportedPaymentNetworkByNetwork; -} - -/** Object interface to list the payment network module by network */ -export interface ISupportedPaymentNetworkByNetwork { - [network: string]: IPaymentNetworkModuleByType; -} - -/** Object interface to list the payment network module by id */ -export interface IPaymentNetworkModuleByType { - [type: string]: any; -} - -/** Interface to create a payment network */ -export interface IPaymentNetworkCreateParameters { - id: PAYMENT_NETWORK_ID; - parameters: any; -} - -/** Interface of the class to manage a payment network */ -export interface IPaymentNetwork { - createExtensionsDataForCreation: (paymentNetworkCreationParameters: any) => any; - createExtensionsDataForAddRefundInformation: (parameters: any) => any; - createExtensionsDataForAddPaymentInformation: (parameters: any) => any; - getBalance(request: RequestLogicTypes.IRequest): Promise; -} - -/** Interface of the class to manage the bitcoin provider API */ -export interface IBitcoinDetectionProvider { - getAddressBalanceWithEvents: ( - bitcoinNetworkId: number, - address: string, - eventName: EVENTS_NAMES, - ) => Promise; -} - -/** Interface for balances and the events link to the payments and refund */ -export interface IBalanceWithEvents { - balance: string; - events: IPaymentNetworkEvent[]; -} - -/** payment network event */ -export interface IPaymentNetworkEvent { - name: EVENTS_NAMES; - parameters?: any; -} - -/** payment network event names */ -export enum EVENTS_NAMES { - PAYMENT = 'payment', - REFUND = 'refund', -} - -/** List of payment networks available (abstract the extensions type) */ -export enum PAYMENT_NETWORK_ID { - BITCOIN_ADDRESS_BASED = ExtensionTypes.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, - TESTNET_BITCOIN_ADDRESS_BASED = ExtensionTypes.ID.PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED, - ERC20_ADDRESS_BASED = ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED, - DECLARATIVE = ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, -} diff --git a/packages/request-client.js/test/api/currency.test.ts b/packages/request-client.js/test/api/currency.test.ts index 46b2ab115b..15888e1c76 100644 --- a/packages/request-client.js/test/api/currency.test.ts +++ b/packages/request-client.js/test/api/currency.test.ts @@ -2,13 +2,65 @@ import { RequestLogicTypes } from '@requestnetwork/types'; import { assert } from 'chai'; import 'mocha'; -import { currencyToString, getDecimalsForCurrency, stringToCurrency } from '../../src/api/currency'; +import { + currencyToString, + getAllSupportedCurrencies, + getDecimalsForCurrency, + stringToCurrency, +} from '../../src/api/currency'; describe('api/currency', () => { + describe('getAllSupportedCurrencies', () => { + it('returns ETH', () => { + assert.deepEqual(getAllSupportedCurrencies().ETH[0], { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }); + }); + + it('returns BTC', () => { + assert.deepEqual(getAllSupportedCurrencies().BTC[0], { + decimals: 8, + name: 'Bitcoin', + symbol: 'BTC', + }); + }); + + it('returns SAI', () => { + assert.deepEqual(getAllSupportedCurrencies().ERC20.find(({ symbol }) => symbol === 'SAI'), { + address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359', + decimals: 18, + name: 'Sai Stablecoin v1.0', + symbol: 'SAI', + }); + }); + + it('returns CTBK', () => { + assert.deepEqual( + getAllSupportedCurrencies().ERC20.find(({ symbol }) => symbol === 'CTBK-rinkeby'), + { + address: '0x995d6a8c21f24be1dd04e105dd0d83758343e258', + decimals: 18, + name: 'Central Bank Token', + symbol: 'CTBK-rinkeby', + }, + ); + }); + + it('returns EUR', () => { + assert.deepEqual(getAllSupportedCurrencies().ISO4217.find(({ symbol }) => symbol === 'EUR'), { + decimals: 2, + name: 'Euro', + symbol: 'EUR', + }); + }); + }); + describe('getDecimalsForCurrency', () => { - it('returns the correct number of decimals', async () => { + it('returns the correct number of decimals', () => { assert.equal( - await getDecimalsForCurrency({ + getDecimalsForCurrency({ type: RequestLogicTypes.CURRENCY.ETH, value: 'ETH', }), @@ -16,19 +68,20 @@ describe('api/currency', () => { ); }); - it('throws for invalid currencies', async () => { - assert.isRejected( - getDecimalsForCurrency({ - type: 'BANANA' as RequestLogicTypes.CURRENCY, - value: 'SPLIT', - } as RequestLogicTypes.ICurrency), + it('throws for invalid currencies', () => { + assert.throws( + () => + getDecimalsForCurrency({ + type: 'BANANA' as RequestLogicTypes.CURRENCY, + value: 'SPLIT', + } as RequestLogicTypes.ICurrency), 'Currency BANANA not implemented', ); }); - it('returns the correct number of decimals for a supported ERC20', async () => { + it('returns the correct number of decimals for a supported ERC20', () => { assert.equal( - await getDecimalsForCurrency({ + getDecimalsForCurrency({ network: 'mainnet', type: RequestLogicTypes.CURRENCY.ERC20, value: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359', // SAI @@ -37,14 +90,13 @@ describe('api/currency', () => { ); }); - it('returns the correct number of decimals for a non-supported ERC20', async () => { - assert.equal( - await getDecimalsForCurrency({ + it('throws for a non-supported ERC20', () => { + assert.throws(() => + getDecimalsForCurrency({ network: 'private', type: RequestLogicTypes.CURRENCY.ERC20, value: '0x9FBDa871d559710256a2502A2517b794B482Db40', // local ERC20 contract }), - 18, ); }); }); @@ -107,6 +159,22 @@ describe('api/currency', () => { it('throws for an unsupported currency', () => { assert.throws(() => stringToCurrency('XXXXXXX')); }); + + it('does not persist state between calls', () => { + assert.deepEqual(stringToCurrency('ETH'), { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }); + assert.deepEqual(stringToCurrency('ETH-rinkeby'), { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }); + assert.deepEqual(stringToCurrency('ETH'), { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }); + }); }); describe('currencyToString', () => { diff --git a/packages/request-client.js/test/api/payment-network/erc20/info-retriever.test.ts b/packages/request-client.js/test/api/payment-network/erc20/info-retriever.test.ts deleted file mode 100644 index 3fe63ace2f..0000000000 --- a/packages/request-client.js/test/api/payment-network/erc20/info-retriever.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -// tslint:disable: no-invalid-this -// tslint:disable: no-magic-numbers -import ERC20InfoRetriever from '../../../../src/api/payment-network/erc20/info-retriever'; - -import 'chai'; -import 'mocha'; - -const chai = require('chai'); -const expect = chai.expect; - -const erc20LocalhostContractAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40'; - -/* tslint:disable:no-unused-expression */ -describe('api/erc20/info-retriever', () => { - describe('on localhost', () => { - const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; - const payerAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; - const emptyAddress = '0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef'; - - it('can get the localhost balance of an address', async () => { - const balanceObject = await ERC20InfoRetriever( - erc20LocalhostContractAddress, - paymentAddress, - 'private', - ); - - expect(balanceObject.decimals).to.be.equal('18'); - // if this assert fails it means this address received another transaction - expect(balanceObject.tokenEvents).to.have.lengthOf(1); - expect(balanceObject.tokenEvents[0]).to.deep.equal({ - from: payerAddress, - to: paymentAddress, - value: '10', - }); - }); - - it('gets an empty list of events for an address without ERC20 on localhost', async () => { - const balanceObject = await ERC20InfoRetriever( - erc20LocalhostContractAddress, - emptyAddress, - 'private', - ); - - expect(balanceObject.decimals).to.be.equal('18'); - expect(balanceObject.tokenEvents).to.be.empty; - }); - }); -}); diff --git a/packages/request-client.js/test/api/request-network.test.ts b/packages/request-client.js/test/api/request-network.test.ts index 60577a8d54..c3f7ba727b 100644 --- a/packages/request-client.js/test/api/request-network.test.ts +++ b/packages/request-client.js/test/api/request-network.test.ts @@ -1,8 +1,16 @@ -import { DataAccessTypes } from '@requestnetwork/types'; - -import { assert, expect } from 'chai'; +import MultiFormat from '@requestnetwork/multi-format'; +import { DataAccessTypes, SignatureTypes, TransactionTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; import 'mocha'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; +const assert = chai.assert; + import RequestNetwork from '../../src/api/request-network'; import Request from '../../src/api/request'; @@ -10,6 +18,9 @@ import Request from '../../src/api/request'; import * as TestData from '../data-test'; const mockDataAccess: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByTopic(): Promise { return; }, @@ -40,6 +51,9 @@ describe('api/request-network', () => { describe('createRequest', () => { it('cannot createRequest() with extensionsData', async () => { const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByTopic(): Promise { return; }, @@ -59,31 +73,28 @@ describe('api/request-network', () => { const requestnetwork = new RequestNetwork(mockDataAccessWithTxs); - try { - await requestnetwork.createRequest({ + await expect( + requestnetwork.createRequest({ requestInfo: { extensionsData: ['not expected'] } as any, signer: {} as any, - }); - // tslint:disable-next-line:no-unused-expression - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message, 'exception wrong').to.equal( - 'extensionsData in request parameters must be empty', - ); - } + }), + ).to.eventually.be.rejectedWith('extensionsData in request parameters must be empty'); }); }); describe('fromRequestId', () => { it('can get request with payment network fromRequestId', async () => { const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByTopic(): Promise { return; }, async getTransactionsByChannelId(): Promise { return { result: { - transactions: [TestData.transactionConfirmed], + transactions: [TestData.timestampedTransaction], }, }; }, @@ -99,15 +110,72 @@ describe('api/request-network', () => { }; const requestnetwork = new RequestNetwork(mockDataAccessWithTxs); + const request = await requestnetwork.fromRequestId(TestData.actionRequestId); expect(request).to.instanceOf(Request); }); + + it('cannot get request fromRequestId with if transactions are ignored', async () => { + const txIgnoredByTransactionManager: TransactionTypes.ITimestampedTransaction = { + state: DataAccessTypes.TransactionState.PENDING, + timestamp: 1549953337, + transaction: { data: 'broken transaction' }, + }; + const actionWrongSigner = Utils.signature.sign(TestData.data, { + method: SignatureTypes.METHOD.ECDSA, + privateKey: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + }); + + const txIgnoredByRequestLogic: TransactionTypes.ITimestampedTransaction = { + state: DataAccessTypes.TransactionState.PENDING, + timestamp: 1549953338, + transaction: { + data: JSON.stringify(actionWrongSigner), + }, + }; + const requestId = MultiFormat.serialize( + Utils.crypto.normalizeKeccak256Hash(actionWrongSigner), + ); + + const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, + async getChannelsByTopic(): Promise { + return; + }, + async getTransactionsByChannelId(): Promise { + return { + result: { + transactions: [txIgnoredByTransactionManager, txIgnoredByRequestLogic], + }, + }; + }, + async initialize(): Promise { + return; + }, + async persistTransaction(): Promise { + return; + }, + async getChannelsByMultipleTopics(): Promise { + return; + }, + }; + + const requestnetwork = new RequestNetwork(mockDataAccessWithTxs); + await expect(requestnetwork.fromRequestId(requestId)).to.eventually.rejectedWith( + `Invalid transaction(s) found: [{"reason":"Impossible to JSON parse the transaction","transaction":{"state":"pending","timestamp":1549953337,"transaction":{"data":"broken transaction"}}},{"reason":"Signer must be the payee or the payer","transaction":{"action":{"data":{"name":"create","parameters":{"currency":{"network":"testnet","type":"BTC","value":"BTC"},"expectedAmount":"100000000000","extensionsData":[{"action":"create","id":"pn-testnet-bitcoin-address-based","parameters":{"paymentAddress":"mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v"},"version":"0.1.0"}],"payee":{"type":"ethereumAddress","value":"0x627306090abab3a6e1400e9345bc60c78a8bef57"},"payer":{"type":"ethereumAddress","value":"0xf17f52151ebef6c7334fad080c5704d77216b732"},"timestamp":1549953337},"version":"2.0.3"},"signature":{"method":"ecdsa","value":"0xba762b281fb82cb317221ec87666d04b793e77013f9ca3951d59b4ba1e1f9554508a462ebafabd1ca4de2b6c25e878e74d1891cc1732001e5c0d8c59cb0ffa371b"}},"state":"pending","timestamp":1549953338}}]`, + ); + }); }); describe('fromIdentity', () => { it('can get requests with payment network fromIdentity', async () => { const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByTopic(topic: string): Promise { expect(topic).to.equals( '01f1a21ab419611dbf492b3136ac231c8773dc897ee0eb5167ef2051a39e685e76', @@ -119,9 +187,9 @@ describe('api/request-network', () => { }, result: { transactions: { - [TestData.actionRequestId]: [TestData.transactionConfirmed], + [TestData.actionRequestId]: [TestData.timestampedTransaction], [TestData.actionRequestIdSecondRequest]: [ - TestData.transactionConfirmedSecondRequest, + TestData.timestampedTransactionSecondRequest, ], }, }, @@ -132,6 +200,7 @@ describe('api/request-network', () => { if (channelId === TestData.actionRequestId) { transactions = [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: TestData.arbitraryTimestamp, transaction: { data: JSON.stringify(TestData.action), @@ -140,7 +209,7 @@ describe('api/request-network', () => { ]; } if (channelId === TestData.actionRequestIdSecondRequest) { - transactions = [TestData.transactionConfirmedSecondRequest]; + transactions = [TestData.timestampedTransactionSecondRequest]; } return { result: { @@ -178,6 +247,9 @@ describe('api/request-network', () => { describe('fromTopic', () => { it('can get requests with payment network fromTopic', async () => { const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByTopic(): Promise { return { meta: { @@ -186,9 +258,9 @@ describe('api/request-network', () => { }, result: { transactions: { - [TestData.actionRequestId]: [TestData.transactionConfirmed], + [TestData.actionRequestId]: [TestData.timestampedTransaction], [TestData.actionRequestIdSecondRequest]: [ - TestData.transactionConfirmedSecondRequest, + TestData.timestampedTransactionSecondRequest, ], }, }, @@ -197,10 +269,10 @@ describe('api/request-network', () => { async getTransactionsByChannelId(channelId: string): Promise { let transactions: any[] = []; if (channelId === TestData.actionRequestId) { - transactions = [TestData.transactionConfirmed]; + transactions = [TestData.timestampedTransaction]; } if (channelId === TestData.actionRequestIdSecondRequest) { - transactions = [TestData.transactionConfirmedSecondRequest]; + transactions = [TestData.timestampedTransactionSecondRequest]; } return { result: { @@ -229,8 +301,11 @@ describe('api/request-network', () => { }); describe('fromMultipleIdentities', () => { - it('can get requests with payment network fromIdentity', async () => { + it('can get requests with payment network from multiple Identities', async () => { const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByMultipleTopics(topics: [string]): Promise { expect(topics).to.deep.equals([ '01f1a21ab419611dbf492b3136ac231c8773dc897ee0eb5167ef2051a39e685e76', @@ -242,9 +317,9 @@ describe('api/request-network', () => { }, result: { transactions: { - [TestData.actionRequestId]: [TestData.transactionConfirmed], + [TestData.actionRequestId]: [TestData.timestampedTransaction], [TestData.actionRequestIdSecondRequest]: [ - TestData.transactionConfirmedSecondRequest, + TestData.timestampedTransactionSecondRequest, ], }, }, @@ -255,6 +330,7 @@ describe('api/request-network', () => { if (channelId === TestData.actionRequestId) { transactions = [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: TestData.arbitraryTimestamp, transaction: { data: JSON.stringify(TestData.action), @@ -263,7 +339,7 @@ describe('api/request-network', () => { ]; } if (channelId === TestData.actionRequestIdSecondRequest) { - transactions = [TestData.transactionConfirmedSecondRequest]; + transactions = [TestData.timestampedTransactionSecondRequest]; } return { result: { @@ -305,6 +381,9 @@ describe('api/request-network', () => { describe('fromMultipleTopics', () => { it('can get requests with payment network fromMultipleTopics', async () => { const mockDataAccessWithTxs: DataAccessTypes.IDataAccess = { + async _getStatus(): Promise { + return; + }, async getChannelsByMultipleTopics(): Promise { return { meta: { @@ -313,9 +392,9 @@ describe('api/request-network', () => { }, result: { transactions: { - [TestData.actionRequestId]: [TestData.transactionConfirmed], + [TestData.actionRequestId]: [TestData.timestampedTransaction], [TestData.actionRequestIdSecondRequest]: [ - TestData.transactionConfirmedSecondRequest, + TestData.timestampedTransactionSecondRequest, ], }, }, @@ -324,10 +403,10 @@ describe('api/request-network', () => { async getTransactionsByChannelId(channelId: string): Promise { let transactions: any[] = []; if (channelId === TestData.actionRequestId) { - transactions = [TestData.transactionConfirmed]; + transactions = [TestData.timestampedTransaction]; } if (channelId === TestData.actionRequestIdSecondRequest) { - transactions = [TestData.transactionConfirmedSecondRequest]; + transactions = [TestData.timestampedTransactionSecondRequest]; } return { result: { diff --git a/packages/request-client.js/test/api/request.test.ts b/packages/request-client.js/test/api/request.test.ts index 0ab8f4080f..6ec9107dfc 100644 --- a/packages/request-client.js/test/api/request.test.ts +++ b/packages/request-client.js/test/api/request.test.ts @@ -1,16 +1,19 @@ -import { IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { IdentityTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; + +import { EventEmitter } from 'events'; -import { assert } from 'chai'; import 'mocha'; import Request from '../../src/api/request'; -import * as Types from '../../src/types'; -const chaiAsPromised = require('chai-as-promised'); -const chai = require('chai'); -const spies = require('chai-spies'); -const expect = chai.expect; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; + chai.use(spies); chai.use(chaiAsPromised); +const expect = chai.expect; +const assert = chai.assert; + const sandbox = chai.spy.sandbox(); const mockRequestLogic: RequestLogicTypes.IRequestLogic = { @@ -24,22 +27,22 @@ const mockRequestLogic: RequestLogicTypes.IRequestLogic = { return; }, async acceptRequest(): Promise { - return { meta: {} }; + return Object.assign(new EventEmitter(), { meta: {} }); }, async cancelRequest(): Promise { - return { meta: {} }; + return Object.assign(new EventEmitter(), { meta: {} }); }, async increaseExpectedAmountRequest(): Promise { - return { meta: {} }; + return Object.assign(new EventEmitter(), { meta: {} }); }, async reduceExpectedAmountRequest(): Promise { - return { meta: {} }; + return Object.assign(new EventEmitter(), { meta: {} }); }, async addExtensionsDataRequest(): Promise { - return { meta: {}, result: {} }; + return Object.assign(new EventEmitter(), { meta: {} }); }, async getRequestFromId(): Promise { - return { meta: {}, result: { request: { requestId: '1' } } }; + return { meta: {}, result: { request: { requestId: '1' }, pending: null } }; }, async getRequestsByTopic(): Promise { return { @@ -59,7 +62,7 @@ const mockRequestLogic: RequestLogicTypes.IRequestLogic = { }, }; -const mockPaymentNetwork: Types.IPaymentNetwork = { +const mockPaymentNetwork: PaymentTypes.IPaymentNetwork = { async createExtensionsDataForCreation(): Promise { return; }, @@ -74,7 +77,7 @@ const mockPaymentNetwork: Types.IPaymentNetwork = { }, }; -const mockDeclarativePaymentNetwork: Types.IPaymentNetwork = { +const mockDeclarativePaymentNetwork: PaymentTypes.IPaymentNetwork = { async createExtensionsDataForCreation(): Promise { return; }, @@ -99,7 +102,7 @@ const mockDeclarativePaymentNetwork: Types.IPaymentNetwork = { async getBalance(): Promise { return; }, -} as Types.IPaymentNetwork; +} as PaymentTypes.IPaymentNetwork; const signatureIdentity: IdentityTypes.IIdentity = { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, @@ -117,19 +120,36 @@ describe('api/request', () => { it('exists', async () => { assert.exists(Request); - const requestNetwork = new Request(mockRequestLogic, '1'); - assert.isFunction(requestNetwork.accept); - assert.isFunction(requestNetwork.cancel); - assert.isFunction(requestNetwork.increaseExpectedAmountRequest); - assert.isFunction(requestNetwork.reduceExpectedAmountRequest); - assert.isFunction(requestNetwork.getData); + const request = new Request('1', mockRequestLogic); + assert.isFunction(request.accept); + assert.isFunction(request.cancel); + assert.isFunction(request.increaseExpectedAmountRequest); + assert.isFunction(request.reduceExpectedAmountRequest); + assert.isFunction(request.getData); + }); + + it('emits error at the creation', async () => { + const testingEmitter = new EventEmitter(); + const request = new Request('1', mockRequestLogic, { + requestLogicCreateResult: testingEmitter as any, + }); + + // tslint:disable-next-line:typedef + const handleError = chai.spy((error: any) => { + assert(error, 'error for test purpose'); + }); + request.on('error', handleError); + + testingEmitter.emit('error', 'error for test purpose'); + + expect(handleError, 'error must be emitted').to.have.called(); }); describe('accept', () => { it('calls request-logic', async () => { const spy = sandbox.on(mockRequestLogic, 'acceptRequest'); - const request = new Request(mockRequestLogic, '1'); + const request = new Request('1', mockRequestLogic); await request.accept(signatureIdentity); expect(spy).to.have.been.called.once; @@ -142,7 +162,7 @@ describe('api/request', () => { 'createExtensionsDataForAddRefundInformation', ); - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); await request.accept(signatureIdentity, { refundAddress: bitcoinAddress }); expect(spyPayNet).to.have.been.called.once; @@ -150,13 +170,10 @@ describe('api/request', () => { }); it('cannot call accept and add refund address without payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.accept(signatureIdentity, { refundAddress: bitcoinAddress }); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot add refund information without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.accept(signatureIdentity, { refundAddress: bitcoinAddress }), + ).to.eventually.be.rejectedWith('Cannot add refund information without payment network'); }); }); @@ -164,7 +181,7 @@ describe('api/request', () => { it('calls request-logic', async () => { const spy = sandbox.on(mockRequestLogic, 'cancelRequest'); - const request = new Request(mockRequestLogic, '1'); + const request = new Request('1', mockRequestLogic); await request.cancel(signatureIdentity); expect(spy).to.have.been.called.once; @@ -177,20 +194,18 @@ describe('api/request', () => { 'createExtensionsDataForAddRefundInformation', ); - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); await request.cancel(signatureIdentity, { refundAddress: bitcoinAddress }); expect(spyPayNet).to.have.been.called.once; expect(spyReqLog).to.have.been.called.once; }); it('cannot call cancel and add refund address without payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.cancel(signatureIdentity, { refundAddress: bitcoinAddress }); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot add refund information without payment network'); - } + const request = new Request('1', mockRequestLogic); + + await expect( + request.cancel(signatureIdentity, { refundAddress: bitcoinAddress }), + ).to.eventually.be.rejectedWith('Cannot add refund information without payment network'); }); }); @@ -198,7 +213,7 @@ describe('api/request', () => { it('calls request-logic', async () => { const spy = sandbox.on(mockRequestLogic, 'increaseExpectedAmountRequest'); - const request = new Request(mockRequestLogic, '1'); + const request = new Request('1', mockRequestLogic); await request.increaseExpectedAmountRequest(3, signatureIdentity); expect(spy).to.have.been.called.once; @@ -211,7 +226,7 @@ describe('api/request', () => { 'createExtensionsDataForAddRefundInformation', ); - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); await request.increaseExpectedAmountRequest(3, signatureIdentity, { refundAddress: bitcoinAddress, }); @@ -221,15 +236,12 @@ describe('api/request', () => { }); it('cannot call increase and add refund address without payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.increaseExpectedAmountRequest(3, signatureIdentity, { + const request = new Request('1', mockRequestLogic); + await expect( + request.increaseExpectedAmountRequest(3, signatureIdentity, { refundAddress: bitcoinAddress, - }); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot add refund information without payment network'); - } + }), + ).to.eventually.be.rejectedWith('Cannot add refund information without payment network'); }); }); @@ -237,7 +249,7 @@ describe('api/request', () => { it('calls request-logic', async () => { const spy = sandbox.on(mockRequestLogic, 'reduceExpectedAmountRequest'); - const request = new Request(mockRequestLogic, '1'); + const request = new Request('1', mockRequestLogic); await request.reduceExpectedAmountRequest(3, signatureIdentity); expect(spy).to.have.been.called.once; @@ -250,7 +262,7 @@ describe('api/request', () => { 'createExtensionsDataForAddPaymentInformation', ); - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); await request.reduceExpectedAmountRequest(3, signatureIdentity, { refundAddress: bitcoinAddress, }); @@ -260,15 +272,12 @@ describe('api/request', () => { }); it('cannot call reduce and add payment address without payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.reduceExpectedAmountRequest('1', signatureIdentity, { + const request = new Request('1', mockRequestLogic); + await expect( + request.reduceExpectedAmountRequest('1', signatureIdentity, { paymentInformation: bitcoinAddress, - }); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot add payment information without payment network'); - } + }), + ).to.eventually.be.rejectedWith('Cannot add payment information without payment network'); }); }); @@ -280,7 +289,7 @@ describe('api/request', () => { 'createExtensionsDataForAddPaymentInformation', ); - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); await request.addPaymentInformation({ paymentAddress: bitcoinAddress }, signatureIdentity); expect(spyPayNet).to.have.been.called.once; @@ -288,13 +297,10 @@ describe('api/request', () => { }); it('cannot add payment address without payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.addPaymentInformation({ paymentAddress: bitcoinAddress }, signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot add payment information without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.addPaymentInformation({ paymentAddress: bitcoinAddress }, signatureIdentity), + ).to.eventually.be.rejectedWith('Cannot add payment information without payment network'); }); }); @@ -306,7 +312,7 @@ describe('api/request', () => { 'createExtensionsDataForAddRefundInformation', ); - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); await request.addRefundInformation({ refundAddress: bitcoinAddress }, signatureIdentity); expect(spyPayNet).to.have.been.called.once; @@ -314,13 +320,10 @@ describe('api/request', () => { }); it('cannot add payment address without payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.addRefundInformation({ refundAddress: bitcoinAddress }, signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot add refund information without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.addRefundInformation({ refundAddress: bitcoinAddress }, signatureIdentity), + ).to.eventually.be.rejectedWith('Cannot add refund information without payment network'); }); }); @@ -332,7 +335,9 @@ describe('api/request', () => { 'createExtensionsDataForDeclareSentPayment', ); - const request = new Request(mockRequestLogic, '1', mockDeclarativePaymentNetwork); + const request = new Request('1', mockRequestLogic, { + paymentNetwork: mockDeclarativePaymentNetwork, + }); await request.declareSentPayment('1000', 'sent', signatureIdentity); expect(spyPayNet).to.have.been.called.once; @@ -340,25 +345,19 @@ describe('api/request', () => { }); it('cannot declare sent payment if no payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.declareSentPayment('1000', 'sent', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot declare sent payment without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.declareSentPayment('1000', 'sent', signatureIdentity), + ).to.eventually.be.rejectedWith('Cannot declare sent payment without payment network'); }); it('cannot declare sent payment if payment network is not declarative', async () => { - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); - try { - await request.declareSentPayment('1000', 'sent', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Cannot declare sent payment without declarative payment network', - ); - } + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); + await expect( + request.declareSentPayment('1000', 'sent', signatureIdentity), + ).to.eventually.be.rejectedWith( + 'Cannot declare sent payment without declarative payment network', + ); }); }); @@ -370,7 +369,9 @@ describe('api/request', () => { 'createExtensionsDataForDeclareSentRefund', ); - const request = new Request(mockRequestLogic, '1', mockDeclarativePaymentNetwork); + const request = new Request('1', mockRequestLogic, { + paymentNetwork: mockDeclarativePaymentNetwork, + }); await request.declareSentRefund('1000', 'sent', signatureIdentity); expect(spyPayNet).to.have.been.called.once; @@ -378,25 +379,19 @@ describe('api/request', () => { }); it('cannot declare sent refund if no payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.declareSentRefund('1000', 'sent', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot declare sent refund without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.declareSentRefund('1000', 'sent', signatureIdentity), + ).to.eventually.be.rejectedWith('Cannot declare sent refund without payment network'); }); it('cannot declare sent refund if payment network is not declarative', async () => { - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); - try { - await request.declareSentRefund('1000', 'sent', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Cannot declare sent refund without declarative payment network', - ); - } + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); + await expect( + request.declareSentRefund('1000', 'sent', signatureIdentity), + ).to.eventually.be.rejectedWith( + 'Cannot declare sent refund without declarative payment network', + ); }); }); @@ -408,7 +403,9 @@ describe('api/request', () => { 'createExtensionsDataForDeclareReceivedPayment', ); - const request = new Request(mockRequestLogic, '1', mockDeclarativePaymentNetwork); + const request = new Request('1', mockRequestLogic, { + paymentNetwork: mockDeclarativePaymentNetwork, + }); await request.declareReceivedPayment('1000', 'received', signatureIdentity); expect(spyPayNet).to.have.been.called.once; @@ -416,25 +413,19 @@ describe('api/request', () => { }); it('cannot declare received payment if no payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.declareReceivedPayment('1000', 'received', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot declare received payment without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.declareReceivedPayment('1000', 'received', signatureIdentity), + ).to.eventually.be.rejectedWith('Cannot declare received payment without payment network'); }); it('cannot declare received payment if payment network is not declarative', async () => { - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); - try { - await request.declareReceivedPayment('1000', 'received', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Cannot declare received payment without declarative payment network', - ); - } + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); + await expect( + request.declareReceivedPayment('1000', 'received', signatureIdentity), + ).to.eventually.be.rejectedWith( + 'Cannot declare received payment without declarative payment network', + ); }); }); @@ -446,7 +437,9 @@ describe('api/request', () => { 'createExtensionsDataForDeclareReceivedRefund', ); - const request = new Request(mockRequestLogic, '1', mockDeclarativePaymentNetwork); + const request = new Request('1', mockRequestLogic, { + paymentNetwork: mockDeclarativePaymentNetwork, + }); await request.declareReceivedRefund('1000', 'received', signatureIdentity); expect(spyPayNet).to.have.been.called.once; @@ -454,25 +447,19 @@ describe('api/request', () => { }); it('cannot declare received refund if no payment network', async () => { - const request = new Request(mockRequestLogic, '1'); - try { - await request.declareReceivedRefund('1000', 'received', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal('Cannot declare received refund without payment network'); - } + const request = new Request('1', mockRequestLogic); + await expect( + request.declareReceivedRefund('1000', 'received', signatureIdentity), + ).to.eventually.be.rejectedWith('Cannot declare received refund without payment network'); }); it('cannot declare received refund if payment network is not declarative', async () => { - const request = new Request(mockRequestLogic, '1', mockPaymentNetwork); - try { - await request.declareReceivedRefund('1000', 'received', signatureIdentity); - expect(false, 'should throw').to.be.true; - } catch (e) { - expect(e.message).to.equal( - 'Cannot declare received refund without declarative payment network', - ); - } + const request = new Request('1', mockRequestLogic, { paymentNetwork: mockPaymentNetwork }); + await expect( + request.declareReceivedRefund('1000', 'received', signatureIdentity), + ).to.eventually.be.rejectedWith( + 'Cannot declare received refund without declarative payment network', + ); }); }); @@ -507,6 +494,7 @@ describe('api/request', () => { return { meta: {}, result: { + pending: {}, request: {}, }, }; @@ -530,7 +518,7 @@ describe('api/request', () => { }; const spy = sandbox.on(mockRequestLogicWithRequest, 'getRequestFromId'); - const request = new Request(mockRequestLogicWithRequest, '1'); + const request = new Request('1', mockRequestLogicWithRequest); await request.refresh(); expect(spy).to.have.been.called.once; diff --git a/packages/request-client.js/test/data-test-real-btc.ts b/packages/request-client.js/test/data-test-real-btc.ts index 82b6d0a9aa..8f22d33061 100644 --- a/packages/request-client.js/test/data-test-real-btc.ts +++ b/packages/request-client.js/test/data-test-real-btc.ts @@ -47,12 +47,13 @@ export const parameters: RequestLogicTypes.ICreateParameters = { export const data = { name: RequestLogicTypes.ACTION_NAME.CREATE, parameters, - version: '2.0.2', + version: '2.0.3', }; export const action: RequestLogicTypes.IAction = Utils.signature.sign(data, payee.signatureParams); -export const transactionConfirmed: TransactionTypes.IConfirmedTransaction = { +export const timestampedTransaction: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: arbitraryTimestamp, transaction: { data: JSON.stringify(action) }, }; diff --git a/packages/request-client.js/test/data-test.ts b/packages/request-client.js/test/data-test.ts index 6261745d4c..c1ac16e3ca 100644 --- a/packages/request-client.js/test/data-test.ts +++ b/packages/request-client.js/test/data-test.ts @@ -1,12 +1,12 @@ import MultiFormat from '@requestnetwork/multi-format'; import { IdentityTypes, + PaymentTypes, RequestLogicTypes, SignatureTypes, TransactionTypes, } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; -import * as Types from '../src/types'; export const arbitraryTimestamp = 1549953337; @@ -43,7 +43,7 @@ export const parameters: RequestLogicTypes.ICreateParameters = { { action: 'create', id: 'pn-testnet-bitcoin-address-based', - parameters: {}, + parameters: { paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v' }, version: '0.1.0', }, ], @@ -98,40 +98,47 @@ export const parametersWithoutExtensionsDataForSigning: RequestLogicTypes.ICreat export const data = { name: RequestLogicTypes.ACTION_NAME.CREATE, parameters, - version: '2.0.2', + version: '2.0.3', }; export const dataWithoutExtensionsData = { name: RequestLogicTypes.ACTION_NAME.CREATE, parameters: parametersWithoutExtensionsDataForSigning, - version: '2.0.2', + version: '2.0.3', }; export const dataWithDeclarative = { name: RequestLogicTypes.ACTION_NAME.CREATE, parameters: parametersWithDeclarative, - version: '2.0.2', + version: '2.0.3', }; -export const action: RequestLogicTypes.IAction = Utils.signature.sign(data, payee.signatureParams); -export const actionWithoutExtensionsData: RequestLogicTypes.IAction = Utils.signature.sign( - dataWithoutExtensionsData, +export const action: RequestLogicTypes.IAction = Utils.signature.sign( + dataWithDeclarative, payee.signatureParams, ); -export const actionWithDeclarative: RequestLogicTypes.IAction = Utils.signature.sign( - dataWithDeclarative, +export const actionWithoutExtensionsData: RequestLogicTypes.IAction = Utils.signature.sign( + dataWithoutExtensionsData, payee.signatureParams, ); -export const transactionConfirmed: TransactionTypes.IConfirmedTransaction = { +export const timestampedTransaction: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: arbitraryTimestamp, transaction: { data: JSON.stringify(action) }, }; -export const transactionConfirmedWithoutExtensionsData: TransactionTypes.IConfirmedTransaction = { +export const timestampedTransactionWithoutExtensionsData: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: arbitraryTimestamp, transaction: { data: JSON.stringify(actionWithoutExtensionsData) }, }; -export const transactionConfirmedWithDeclarative: TransactionTypes.IConfirmedTransaction = { +export const timestampedTransactionWithoutExtensionsDataConfirmed: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: arbitraryTimestamp, - transaction: { data: JSON.stringify(actionWithDeclarative) }, + transaction: { data: JSON.stringify(actionWithoutExtensionsData) }, +}; +export const timestampedTransactionWithDeclarative: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: arbitraryTimestamp, + transaction: { data: JSON.stringify(action) }, }; export const actionRequestId = MultiFormat.serialize(Utils.crypto.normalizeKeccak256Hash(action)); @@ -141,7 +148,8 @@ export const anotherCreationAction: RequestLogicTypes.IAction = Utils.signature. payer.signatureParams, ); -export const anotherCreationTransactionConfirmed: TransactionTypes.IConfirmedTransaction = { +export const anotherCreationTransactionConfirmed: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: arbitraryTimestamp, transaction: { data: JSON.stringify(anotherCreationAction) }, }; @@ -150,7 +158,7 @@ const dataSecondRequest = { name: RequestLogicTypes.ACTION_NAME.CREATE, parameters: { currency: { - network: 'testnet', + network: 'rinkeby', type: RequestLogicTypes.CURRENCY.ETH, value: 'ETH', }, @@ -159,7 +167,7 @@ const dataSecondRequest = { payee: payee.identity, timestamp: 1544426030, }, - version: '2.0.2', + version: '2.0.3', }; export const actionCreationSecondRequest: RequestLogicTypes.IAction = Utils.signature.sign( @@ -167,7 +175,8 @@ export const actionCreationSecondRequest: RequestLogicTypes.IAction = Utils.sign payee.signatureParams, ); -export const transactionConfirmedSecondRequest: TransactionTypes.IConfirmedTransaction = { +export const timestampedTransactionSecondRequest: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: arbitraryTimestamp, transaction: { data: JSON.stringify(actionCreationSecondRequest) }, }; @@ -176,8 +185,8 @@ export const actionRequestIdSecondRequest = MultiFormat.serialize( Utils.crypto.normalizeKeccak256Hash(actionCreationSecondRequest), ); -export const declarativePaymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, +export const declarativePaymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: { paymentInformation: { BIC: 'SABAIE2D', diff --git a/packages/request-client.js/test/http-metamask-data-access.test.ts b/packages/request-client.js/test/http-metamask-data-access.test.ts new file mode 100644 index 0000000000..ac4636f9fa --- /dev/null +++ b/packages/request-client.js/test/http-metamask-data-access.test.ts @@ -0,0 +1,74 @@ +import { DataAccessTypes, TransactionTypes } from '@requestnetwork/types'; + +import { Block } from '@requestnetwork/data-access'; + +import 'mocha'; +import HttpMetamaskDataAccess from '../src/http-metamask-data-access'; + +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const expect = chai.expect; +chai.use(chaiAsPromised); + +// create a block and add the transaction in it +let block1: DataAccessTypes.IBlock = Block.pushTransaction( + Block.createEmptyBlock(), + { data: 'data1' }, + 'channel1', + ['topic1', 'topic11'], +); +block1 = Block.pushTransaction(block1, { data: 'data2' }, 'channel2', ['topic2', 'topic22']); + +let block2: DataAccessTypes.IBlock = Block.pushTransaction( + Block.createEmptyBlock(), + { data: 'data11' }, + 'channel1', +); +block2 = Block.pushTransaction(block2, { data: 'data22' }, 'channel2'); + +/* tslint:disable:no-unused-expression */ +describe('HttpMetamaskDataAccess', () => { + describe('getCacheAndClean()', () => { + it('get transaction from cache and clean the one added', async () => { + const httpMMDataAccess = new HttpMetamaskDataAccess(); + + // set up cache: + httpMMDataAccess.cache = { + channel1: { + location1: { block: block1, storageMeta: { blockTimestamp: 10 } }, + location2: { block: block2, storageMeta: { blockTimestamp: 20 } }, + }, + }; + + const cacheCleaned = httpMMDataAccess.getCachedTransactionsAndCleanCache('channel1', [ + 'location1', + ]); + + expect(cacheCleaned).to.deep.equal({ + meta: { + storageMeta: [{ blockTimestamp: 20 }], + transactionsStorageLocation: ['location2'], + }, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 20, + transaction: { data: 'data11' }, + }, + ], + }, + }); + + expect(httpMMDataAccess.cache.channel1).to.deep.equal({ + location1: null, + location2: { + block: block2, + storageMeta: { + blockTimestamp: 20, + }, + }, + }); + }); + }); +}); diff --git a/packages/request-client.js/test/index-encryption.html b/packages/request-client.js/test/index-encryption.html index f6ce2e4413..0f6ed458a2 100644 --- a/packages/request-client.js/test/index-encryption.html +++ b/packages/request-client.js/test/index-encryption.html @@ -2,7 +2,7 @@ @requestnetwork/request-client.js Test Page - + @@ -88,6 +88,7 @@

    Compatibility

    currency: { type: RequestNetwork.Types.RequestLogic.CURRENCY.BTC, value: 'BTC', + network: 'testnet', }, expectedAmount: '100000000000', payee: { @@ -100,7 +101,7 @@

    Compatibility

    }, }, paymentNetwork: { - id: RequestNetwork.Types.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', }, diff --git a/packages/request-client.js/test/index-metamask.html b/packages/request-client.js/test/index-metamask.html index d00f19b25c..3780be8e2a 100644 --- a/packages/request-client.js/test/index-metamask.html +++ b/packages/request-client.js/test/index-metamask.html @@ -2,7 +2,7 @@ @requestnetwork/request-client.js Test Page - + @@ -49,6 +49,7 @@

    Important

    currency: { type: RequestNetwork.Types.RequestLogic.CURRENCY.BTC, value: 'BTC', + network: 'testnet', }, expectedAmount: '100000000000', payee: { @@ -61,7 +62,7 @@

    Important

    }, }, paymentNetwork: { - id: RequestNetwork.Types.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', }, diff --git a/packages/request-client.js/test/index-persist-from-metamask.html b/packages/request-client.js/test/index-persist-from-metamask.html new file mode 100644 index 0000000000..177635b520 --- /dev/null +++ b/packages/request-client.js/test/index-persist-from-metamask.html @@ -0,0 +1,123 @@ + + + + @requestnetwork/request-client.js Test Page + + + + +

    Important

    +

    + This HTML page must be served on HTTP to be able to communicate with MetaMask +

    +

    + The dependencies must be built. (yarn build) +

    + + + diff --git a/packages/request-client.js/test/index.html b/packages/request-client.js/test/index.html index 197430e62c..ab7374ed16 100644 --- a/packages/request-client.js/test/index.html +++ b/packages/request-client.js/test/index.html @@ -2,7 +2,7 @@ @requestnetwork/request-client.js Test Page - + @@ -18,6 +18,7 @@ currency: { type: RequestNetwork.Types.RequestLogic.CURRENCY.BTC, value: 'BTC', + network: 'testnet', }, expectedAmount: '100000000000', payee: { @@ -30,7 +31,7 @@ }, }, paymentNetwork: { - id: RequestNetwork.Types.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', }, diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index e8d03c160b..77922c849f 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -1,22 +1,30 @@ +/* eslint-disable spellcheck/spell-checker */ const axios = require('axios'); import { DecryptionProviderTypes, EncryptionTypes, IdentityTypes, + PaymentTypes, RequestLogicTypes, SignatureProviderTypes, SignatureTypes, } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; +import { ethers } from 'ethers'; import 'mocha'; import * as sinon from 'sinon'; const mockAdapter = require('axios-mock-adapter'); -import { Request, RequestNetwork } from '../src/index'; -import * as Types from '../src/types'; +import { Request, RequestNetwork, Types } from '../src/index'; import * as TestData from './data-test'; import * as TestDataRealBTC from './data-test-real-btc'; +import { PaymentReferenceCalculator } from '@requestnetwork/payment-detection'; +import { BigNumber } from 'ethers/utils'; + +const packageJson = require('../package.json'); +const REQUEST_CLIENT_VERSION_HEADER = 'X-Request-Network-Client-Version'; + const chai = require('chai'); const spies = require('chai-spies'); const chaiAsPromised = require('chai-as-promised'); @@ -57,15 +65,15 @@ const fakeSignatureProvider: SignatureProviderTypes.ISignatureProvider = { const encryptionData = { decryptionParams: { key: '0x04674d2e53e0e14653487d7323cc5f0a7959c83067f5654cafe4094bde90fa8a', - method: Types.Encryption.METHOD.ECIES, + method: EncryptionTypes.METHOD.ECIES, }, encryptionParams: { key: '299708c07399c9b28e9870c4e643742f65c94683f35d1b3fc05d0478344ee0cc5a6a5e23f78b5ff8c93a04254232b32350c8672d2873677060d5095184dad422', - method: Types.Encryption.METHOD.ECIES, + method: EncryptionTypes.METHOD.ECIES, }, identity: { - type: Types.Identity.TYPE.ETHEREUM_ADDRESS, + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0xaf083f77f1ffd54218d91491afd06c9296eac3ce', }, }; @@ -106,12 +114,33 @@ const requestParameters: RequestLogicTypes.ICreateParameters = { function mockAxios(): any { const mock = new mockAdapter(axios); mock.onPost('/persistTransaction').reply(200, { result: {} }); - mock - .onGet('/getTransactionsByChannelId') - .reply(200, { result: { transactions: [TestData.transactionConfirmedWithoutExtensionsData] } }); + mock.onGet('/getTransactionsByChannelId').reply(200, { + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, + }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); return mock; } +const mockBTCProvider = { + getAddressBalanceWithEvents: (): Promise< + PaymentTypes.IBalanceWithEvents + > => { + return Promise.resolve({ + balance: '666743', + events: [ + { + amount: '666743', + name: PaymentTypes.EVENTS_NAMES.PAYMENT, + parameters: { + block: 561874, + txHash: '4024936746a0994cf5cdf9c8b55e03b288a251ad172682e8e94b7806a4e3dace', + }, + }, + ], + }); + }, +}; + // Integration tests /* tslint:disable:no-unused-expression */ describe('index', () => { @@ -119,7 +148,39 @@ describe('index', () => { sandbox.restore(); }); - it.skip('uses http://localhost:3000 with signatureProvider and paymentNetwork', async () => { + it('specify the Request Client version in the header', async () => { + const mock = new mockAdapter(axios); + + const callback = (config: any): any => { + expect(config.headers[REQUEST_CLIENT_VERSION_HEADER]).to.equal(packageJson.version); + return [200, {}]; + }; + const spy = chai.spy(callback); + mock.onPost('/persistTransaction').reply(spy); + mock + .onGet('/getTransactionsByChannelId') + .reply(200, { result: { transactions: [TestData.timestampedTransaction] } }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); + + const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); + + requestNetwork.bitcoinDetectionProvider = mockBTCProvider; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: payeeIdentity, + }); + expect(spy).to.have.been.called.once; + + await request.waitForConfirmation(); + }); + + it('uses http://localhost:3000 with signatureProvider and paymentNetwork', async () => { const mock = new mockAdapter(axios); const callback = (config: any): any => { @@ -130,26 +191,68 @@ describe('index', () => { mock.onPost('/persistTransaction').reply(spy); mock .onGet('/getTransactionsByChannelId') - .reply(200, { result: { transactions: [TestData.transactionConfirmed] } }); + .reply(200, { result: { transactions: [TestData.timestampedTransaction] } }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + requestNetwork.bitcoinDetectionProvider = mockBTCProvider; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: payeeIdentity, + }); + expect(spy).to.have.been.called.once; + + await request.waitForConfirmation(); + }); + + it('uses http://localhost:3000 with persist from local', async () => { + const mock = new mockAdapter(axios); + const callback = (): any => { + return [200, { ipfsSize: 100, ipfsHash: 'QmZLqH4EsjmB79gjvyzXWBcihbNBZkw8YuELco84PxGzQY' }]; + }; + // const spyPersistTransaction = chai.spy(); + const spyIpfsAdd = chai.spy(callback); + // mock.onPost('/persistTransaction').reply(spyPersistTransaction); + mock.onPost('/persistTransaction').reply(200, { meta: {}, result: {} }); + mock.onPost('/ipfsAdd').reply(spyIpfsAdd); + mock.onGet('/getTransactionsByChannelId').reply(200, { + meta: { storageMeta: [], transactionsStorageLocation: [] }, + result: { transactions: [] }, + }); + + const requestNetwork = new RequestNetwork({ + ethereumProviderUrl: 'http://localhost:8545', + signatureProvider: fakeSignatureProvider, + useLocalEthereumBroadcast: true, + }); + + requestNetwork.bitcoinDetectionProvider = mockBTCProvider; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', }, }; - await requestNetwork.createRequest({ + const request = await requestNetwork.createRequest({ paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); - expect(spy).to.have.been.called.once; + expect(spyIpfsAdd).to.have.been.called.once; + + await request.waitForConfirmation(); }); - it.skip('uses http://localhost:3000 with signatureProvider and paymentNetwork real btc', async () => { + it('uses http://localhost:3000 with signatureProvider and paymentNetwork real btc', async () => { const mock = new mockAdapter(axios); const callback = (config: any): any => { @@ -159,24 +262,29 @@ describe('index', () => { const spy = chai.spy(callback); mock.onPost('/persistTransaction').reply(spy); mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestDataRealBTC.transactionConfirmed] }, + result: { transactions: [TestDataRealBTC.timestampedTransaction] }, }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, + requestNetwork.bitcoinDetectionProvider = mockBTCProvider; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: '1FersucwSqufU26w9GrGz9M3KcwuNmy6a9', }, }; - await requestNetwork.createRequest({ + const request = await requestNetwork.createRequest({ paymentNetwork, requestInfo: requestParameters, signer: payeeIdentity, }); expect(spy).to.have.been.called.once; + + await request.waitForConfirmation(); }); it('uses http://localhost:3000 with signatureProvider', async () => { @@ -189,8 +297,9 @@ describe('index', () => { const spy = chai.spy(callback); mock.onPost('/persistTransaction').reply(spy); mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.transactionConfirmedWithoutExtensionsData] }, + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); @@ -212,18 +321,21 @@ describe('index', () => { const spy = chai.spy(callback); mock.onPost('/persistTransaction').reply(spy); mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.transactionConfirmedWithoutExtensionsData] }, + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); const requestNetwork = new RequestNetwork({ nodeConnectionConfig: { baseURL }, signatureProvider: fakeSignatureProvider, }); - await requestNetwork.createRequest({ + const request = await requestNetwork.createRequest({ requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); expect(spy).to.have.been.called.once; + + await request.waitForConfirmation(); }); it('allows to create a request', async () => { @@ -240,12 +352,14 @@ describe('index', () => { expect(request).to.be.instanceOf(Request); expect(request.requestId).to.exist; - expect(axiosSpyGet).to.have.been.called.once; + expect(axiosSpyGet).to.have.been.called.exactly(3); expect(axiosSpyPost).to.have.been.called.once; // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' const requestIdLength = 66; expect(request.requestId.length).to.equal(requestIdLength); + + await request.waitForConfirmation(); }); it('allows to compute a request id', async () => { @@ -283,7 +397,7 @@ describe('index', () => { const requestIdLength = 66; expect(requestId.length).to.equal(requestIdLength); - await new Promise((resolve): any => setTimeout(resolve, 1500)); + await new Promise((resolve): any => setTimeout(resolve, 150)); const request = await requestNetwork.createRequest({ requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, @@ -291,8 +405,10 @@ describe('index', () => { expect(request).to.be.instanceOf(Request); expect(request.requestId).to.equal(requestId); - expect(axiosSpyGet).to.have.been.called.once; + expect(axiosSpyGet).to.have.been.called.exactly(3); expect(axiosSpyPost).to.have.been.called.once; + + await request.waitForConfirmation(); }); it('allows to get a request from its ID', async () => { @@ -303,6 +419,7 @@ describe('index', () => { requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); + await request.waitForConfirmation(); const requestFromId = await requestNetwork.fromRequestId(request.requestId); @@ -313,14 +430,16 @@ describe('index', () => { const mock = new mockAdapter(axios); mock.onPost('/persistTransaction').reply(200, { result: {} }); mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.transactionConfirmedWithoutExtensionsData] }, + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); const request = await requestNetwork.createRequest({ requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); + await request.waitForConfirmation(); const axiosSpyGet = sandbox.on(axios, 'get'); const axiosSpyPost = sandbox.on(axios, 'post'); @@ -345,21 +464,115 @@ describe('index', () => { }); const data = request.getData(); + expect(data).to.exist; + expect(data.balance).to.null; + expect(data.meta).to.exist; + expect(data.currencyInfo).to.deep.equal(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).to.equal(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.CREATED); + + const dataConfirmed: Types.IRequestDataWithEvents = await new Promise((resolve): any => + request.on('confirmed', resolve), + ); + expect(dataConfirmed.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(dataConfirmed.pending).to.null; + }); + it('works with mocked storage emitting error when append', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + // ask mock up storage to emit error next append call() + requestNetwork._mockStorage!._makeNextAppendFailInsteadOfConfirmed(); + + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: payeeIdentity, + }); + + const data = request.getData(); expect(data).to.exist; - expect(data.balance).to.be.null; + expect(data.balance).to.null; + expect(data.meta).to.exist; + expect(data.currencyInfo).to.deep.equal(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).to.equal(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.CREATED); + + const errorEmitted: string = await new Promise((resolve): any => request.on('error', resolve)); + expect(errorEmitted).to.equal('forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); + + expect(() => request.getData()).to.throw('request confirmation failed'); + await expect(request.refresh()).to.eventually.be.rejectedWith('request confirmation failed'); + }); + + it('works with mocked storage emitting error when append waitForConfirmation will throw', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + // ask mock up storage to emit error next append call() + requestNetwork._mockStorage!._makeNextAppendFailInsteadOfConfirmed(); + + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: payeeIdentity, + }); + + const data = request.getData(); + expect(data).to.exist; + expect(data.balance).to.null; expect(data.meta).to.exist; - expect(data.expectedAmount).to.equal(requestParameters.expectedAmount); + expect(data.currencyInfo).to.deep.equal(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).to.equal(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.CREATED); + + await expect(request.waitForConfirmation()).to.eventually.be.rejectedWith( + 'forced error asked by _makeNextAppendFailInsteadOfConfirmed()', + ); + + expect(() => request.getData()).to.throw('request confirmation failed'); + await expect(request.refresh()).to.eventually.be.rejectedWith('request confirmation failed'); }); - it('works with mocked storage and payment network', async () => { + it('creates a request with error event', async () => { const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider, useMockStorage: true, }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: payeeIdentity, + }); + + const data = request.getData(); + expect(data).to.exist; + expect(data.balance).to.null; + expect(data.meta).to.exist; + expect(data.currencyInfo).to.deep.equal(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).to.equal(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.CREATED); + + const dataConfirmed: Types.IRequestDataWithEvents = await new Promise((resolve): any => + request.on('confirmed', resolve), + ); + expect(dataConfirmed.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(dataConfirmed.pending).to.null; + }); + + it('works with mocked storage and mocked payment network', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + requestNetwork.bitcoinDetectionProvider = mockBTCProvider; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, parameters: { paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', }, @@ -372,11 +585,20 @@ describe('index', () => { }); const data = request.getData(); - expect(data).to.exist; - expect(data.balance).to.exist; - expect(data.meta).to.exist; - expect(data.expectedAmount).to.equal(requestParameters.expectedAmount); + expect(data.balance).to.null; + expect(data.meta).to.be.exist; + expect(data.currencyInfo).to.deep.equal(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).to.equal(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.CREATED); + + const dataConfirmed: Types.IRequestDataWithEvents = await new Promise((resolve): any => + request.on('confirmed', resolve), + ); + expect(dataConfirmed.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(dataConfirmed.pending).to.null; + expect(dataConfirmed.balance?.balance).equal('666743'); + expect(dataConfirmed.balance?.events.length).equal(1); }); it('works with mocked storage and content data', async () => { @@ -400,6 +622,8 @@ describe('index', () => { expect(data).to.exist; expect(data.balance).to.be.null; expect(data.meta).to.exist; + + await request.waitForConfirmation(); }); it('allows to accept a request', async () => { @@ -415,10 +639,48 @@ describe('index', () => { await request.accept(payerIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); + it('works with mocked storage emitting error when append an accept', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: payeeIdentity, + }); + await request.waitForConfirmation(); + + // ask mock up storage to emit error next append call() + requestNetwork._mockStorage!._makeNextAppendFailInsteadOfConfirmed(); + await request.accept(payerIdentity); + + let data = request.getData(); + expect(data).to.exist; + expect(data.balance).to.null; + expect(data.meta).to.exist; + expect(data.currencyInfo).to.deep.equal(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + + const errorEmitted: string = await new Promise((resolve): any => request.on('error', resolve)); + expect(errorEmitted).to.equal('forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); + + data = request.getData(); + expect(data.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + + // TODO: For now data will be pending forever. + // Ethereum-storage should treat the errors and clean up. + data = await request.refresh(); + expect(data.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(data.pending?.state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + }); + it('allows to cancel a request', async () => { const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); const request = await requestNetwork.createRequest({ @@ -431,7 +693,7 @@ describe('index', () => { await request.cancel(payeeIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); @@ -447,7 +709,7 @@ describe('index', () => { await request.increaseExpectedAmountRequest(3, payerIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); @@ -463,7 +725,7 @@ describe('index', () => { await request.reduceExpectedAmountRequest(3, payeeIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); @@ -478,15 +740,16 @@ describe('index', () => { const spy = chai.spy(callback); mock.onPost('/persistTransaction').reply(spy); mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.transactionConfirmedWithDeclarative] }, + result: { transactions: [TestData.timestampedTransactionWithDeclarative] }, }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); }); it('allows to declare a sent payment', async () => { const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: {}, }; @@ -495,21 +758,22 @@ describe('index', () => { requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); + await request.waitForConfirmation(); const axiosSpyGet = sandbox.on(axios, 'get'); const axiosSpyPost = sandbox.on(axios, 'post'); await request.declareSentPayment('10', 'sent payment', payerIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); it('allows to declare a received payment', async () => { const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: {}, }; @@ -518,21 +782,22 @@ describe('index', () => { requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); + await request.waitForConfirmation(); const axiosSpyGet = sandbox.on(axios, 'get'); const axiosSpyPost = sandbox.on(axios, 'post'); await request.declareReceivedPayment('10', 'received payment', payeeIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); it('allows to declare a sent refund', async () => { const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: {}, }; @@ -541,21 +806,22 @@ describe('index', () => { requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); + await request.waitForConfirmation(); const axiosSpyGet = sandbox.on(axios, 'get'); const axiosSpyPost = sandbox.on(axios, 'post'); await request.declareSentRefund('10', 'sent refund', payeeIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); it('allows to declare a received refund', async () => { const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: {}, }; @@ -564,21 +830,18 @@ describe('index', () => { requestInfo: TestData.parametersWithoutExtensionsData, signer: payeeIdentity, }); + await request.waitForConfirmation(); const axiosSpyGet = sandbox.on(axios, 'get'); const axiosSpyPost = sandbox.on(axios, 'post'); await request.declareReceivedRefund('10', 'received refund', payerIdentity); - expect(axiosSpyGet).to.have.been.called.exactly(3); + expect(axiosSpyGet).to.have.been.called.exactly(4); expect(axiosSpyPost).to.have.been.called.once; }); it('allows to get the right balance', async () => { - // Use sinon clock to get a predictible timestamp - const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); - clock.tick(1000); - const requestParametersUSD: RequestLogicTypes.ICreateParameters = { currency: { type: RequestLogicTypes.CURRENCY.ISO4217, @@ -593,8 +856,8 @@ describe('index', () => { signatureProvider: fakeSignatureProvider, useMockStorage: true, }); - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.DECLARATIVE, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: {}, }; @@ -603,55 +866,75 @@ describe('index', () => { requestInfo: requestParametersUSD, signer: payeeIdentity, }); + await request.waitForConfirmation(); - await request.declareSentPayment('1', 'sent payment', payerIdentity); - await request.declareReceivedRefund('10', 'received refund', payerIdentity); + let declareResult = await request.declareSentPayment('1', 'sent payment', payerIdentity); + await new Promise((resolve): any => declareResult.on('confirmed', resolve)); + + declareResult = await request.declareReceivedRefund('10', 'received refund', payerIdentity); + await new Promise((resolve): any => declareResult.on('confirmed', resolve)); + + declareResult = await request.declareSentRefund('100', 'sent refund', payeeIdentity); + await new Promise((resolve): any => declareResult.on('confirmed', resolve)); + + declareResult = await request.declareReceivedPayment( + '1000', + 'received payment', + payeeIdentity, + ); + await new Promise((resolve): any => declareResult.on('confirmed', resolve)); - await request.declareSentRefund('100', 'sent refund', payeeIdentity); - await request.declareReceivedPayment('1000', 'received payment', payeeIdentity); + declareResult = await request.addPaymentInformation('payment info added', payeeIdentity); + await new Promise((resolve): any => declareResult.on('confirmed', resolve)); + declareResult = await request.addRefundInformation('refund info added', payerIdentity); + await new Promise((resolve): any => declareResult.on('confirmed', resolve)); + + const requestData = await request.refresh(); - const requestData = request.getData(); // @ts-ignore - expect(requestData.balance.balance).to.equal('990'); + expect(requestData.balance?.balance).to.equal('990'); // @ts-ignore - expect(requestData.balance.events[0]).to.deep.equal({ - name: 'refund', - parameters: { amount: '10', note: 'received refund', timestamp: 1 }, - }); + expect(requestData.balance?.events[0].name).to.equal('refund'); + expect(requestData.balance?.events[0].amount).to.equal('10'); + expect(requestData.balance?.events[0].parameters).to.deep.equal({ note: 'received refund' }); + // @ts-ignore - expect(requestData.balance.events[1]).to.deep.equal({ - name: 'payment', - parameters: { amount: '1000', note: 'received payment', timestamp: 1 }, - }); + expect(requestData.balance?.events[1].name).to.equal('payment'); + expect(requestData.balance?.events[1].amount).to.equal('1000'); + expect(requestData.balance?.events[1].parameters).to.deep.equal({ note: 'received payment' }); }); it('cannot use declarative function if payment network is not declarative', async () => { - const mock = new mockAdapter(axios); - - const callback = (config: any): any => { - expect(config.baseURL).to.equal('http://localhost:3000'); - return [200, {}]; - }; - const spy = chai.spy(callback); - mock.onPost('/persistTransaction').reply(spy); - mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestDataRealBTC.transactionConfirmed] }, + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, }); - const requestNetwork = new RequestNetwork({ signatureProvider: fakeSignatureProvider }); + const salt = 'ea3bc7caf64110ca'; - const paymentNetwork: Types.IPaymentNetworkCreateParameters = { - id: Types.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, parameters: { - paymentAddress: '1FersucwSqufU26w9GrGz9M3KcwuNmy6a9', + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + salt, }, }; + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + const request = await requestNetwork.createRequest({ paymentNetwork, - requestInfo: requestParameters, + requestInfo, signer: payeeIdentity, }); + await request.waitForConfirmation(); await expect( request.declareReceivedRefund('10', 'received refund', payeeIdentity), @@ -702,7 +985,7 @@ describe('index', () => { const requestData = requestFromId.getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); }); @@ -749,7 +1032,7 @@ describe('index', () => { const requestData = requestsFromTopic[0].getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); }); @@ -793,7 +1076,7 @@ describe('index', () => { const requestData = req.getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); }); }); @@ -821,7 +1104,7 @@ describe('index', () => { const requestData = requestFromIdentity[0].getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); }); @@ -846,14 +1129,23 @@ describe('index', () => { const requestData = fetchedRequest.getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); - await fetchedRequest.accept(payerIdentity); - expect(fetchedRequest.getData().state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + await new Promise((resolve): any => setTimeout(resolve, 150)); + const acceptResult = await fetchedRequest.accept(payerIdentity); + expect(acceptResult.state).to.equal(RequestLogicTypes.STATE.CREATED); + expect(acceptResult.pending?.state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + + const dataConfirmed: Types.IRequestDataWithEvents = await new Promise((resolve): any => + acceptResult.on('confirmed', resolve), + ); + expect(dataConfirmed.state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + expect(dataConfirmed.pending).to.be.null; }); it('creates an encrypted request and cancel it', async () => { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); const requestNetwork = new RequestNetwork({ decryptionProvider: fakeDecryptionProvider, signatureProvider: fakeSignatureProvider, @@ -869,19 +1161,24 @@ describe('index', () => { ); const fetchedRequest = await requestNetwork.fromRequestId(request.requestId); + expect(fetchedRequest).to.deep.equal(request); const requestData = fetchedRequest.getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); + clock.tick(150); await fetchedRequest.cancel(payeeIdentity); - expect(fetchedRequest.getData().state).to.equal(RequestLogicTypes.STATE.CANCELED); + clock.tick(150); + expect((await fetchedRequest.refresh()).state).to.equal(RequestLogicTypes.STATE.CANCELED); + sinon.restore(); }); it('creates an encrypted request, increase and decrease the amount', async () => { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); const requestNetwork = new RequestNetwork({ decryptionProvider: fakeDecryptionProvider, signatureProvider: fakeSignatureProvider, @@ -902,24 +1199,28 @@ describe('index', () => { const requestData = fetchedRequest.getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); + clock.tick(150); await fetchedRequest.increaseExpectedAmountRequest( TestData.parametersWithoutExtensionsData.expectedAmount, payerIdentity, ); - expect(fetchedRequest.getData().expectedAmount).to.equal( - String(TestData.parametersWithoutExtensionsData.expectedAmount * 2), + clock.tick(150); + expect((await fetchedRequest.refresh()).expectedAmount).to.equal( + String(new BigNumber(TestData.parametersWithoutExtensionsData.expectedAmount).mul(2)), ); await fetchedRequest.reduceExpectedAmountRequest( - TestData.parametersWithoutExtensionsData.expectedAmount * 2, + new BigNumber(TestData.parametersWithoutExtensionsData.expectedAmount).mul(2).toString(), payeeIdentity, ); - expect(fetchedRequest.getData().expectedAmount).to.equal('0'); + clock.tick(150); + expect((await fetchedRequest.refresh()).expectedAmount).to.equal('0'); + sinon.restore(); }); it('creates an encrypted declarative request, accepts it and declares a payment on it', async () => { @@ -944,27 +1245,579 @@ describe('index', () => { const requestData = fetchedRequest.getData(); expect(requestData.meta).to.not.be.null; expect(requestData.meta!.transactionManagerMeta.encryptionMethod).to.equal( - 'ecies-aes256-cbc', + 'ecies-aes256-gcm', ); - await fetchedRequest.accept(payerIdentity); - expect(fetchedRequest.getData().state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + const acceptResult = await fetchedRequest.accept(payerIdentity); - await fetchedRequest.declareSentPayment( + let dataConfirmed: Types.IRequestDataWithEvents = await new Promise((resolve): any => + acceptResult.on('confirmed', resolve), + ); + expect(dataConfirmed.state).to.equal(RequestLogicTypes.STATE.ACCEPTED); + + const declareSentPaymentResult = await fetchedRequest.declareSentPayment( TestData.parametersWithoutExtensionsData.expectedAmount, 'PAID', payerIdentity, ); - expect(fetchedRequest.getData().balance!.balance).to.equal('0'); + dataConfirmed = await new Promise((resolve): any => + declareSentPaymentResult.on('confirmed', resolve), + ); + expect(dataConfirmed.balance!.balance).to.equal('0'); - await fetchedRequest.declareReceivedPayment( - TestData.parametersWithoutExtensionsData.expectedAmount, + const declareReceivedPaymentResult = await fetchedRequest.declareReceivedPayment( + TestData.parametersWithoutExtensionsData.expectedAmount as string, 'payment received', payeeIdentity, ); - expect(fetchedRequest.getData().balance!.balance).to.equal( + + dataConfirmed = await new Promise((resolve): any => + declareReceivedPaymentResult.on('confirmed', resolve), + ); + expect(dataConfirmed.balance!.balance).to.equal( TestData.parametersWithoutExtensionsData.expectedAmount, ); }); }); + + describe('ETH requests', () => { + it('can create ETH requests with given salt', async () => { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); + + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const salt = 'ea3bc7caf64110ca'; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + salt, + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + clock.tick(150); + const data = await request.refresh(); + + expect(data).to.exist; + expect(data.balance).to.exist; + expect(data.meta).to.exist; + expect(data.currency).to.equal('ETH-rinkeby'); + expect(data.extensionsData[0].parameters.salt).to.equal(salt); + expect(data.expectedAmount).to.equal(requestParameters.expectedAmount); + sinon.restore(); + }); + + it('can create ETH requests without given salt', async () => { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); + + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + clock.tick(150); + const data = await request.refresh(); + + expect(data.extensionsData[0].parameters.salt.length).to.equal(16); + sinon.restore(); + }); + + it('can create ETH requests without refund address', async () => { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); + + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'rinkeby', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + clock.tick(150); + const data = await request.refresh(); + + expect(data.extensionsData[0].parameters.salt.length).to.equal(16); + sinon.restore(); + }); + + // This test checks that 2 payments with reference `c19da4923539c37f` have reached 0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB + it('can get the balance of an ETH request', async function(): Promise { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); + + // tslint:disable-next-line: no-invalid-this + this.timeout(20000); + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0x0000000000000000000000000000000000000002', + salt: 'a1a2a3a4a5a6a7a8', + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + clock.tick(150); + const data = await request.refresh(); + + // Payment reference should be fixed + expect( + PaymentReferenceCalculator.calculate( + data.requestId, + data.extensionsData[0].parameters.salt, + data.extensionsData[0].parameters.paymentAddress, + ), + ).to.equal('c19da4923539c37f'); + + clock.tick(150); + const dataAfterRefresh = await request.refresh(); + + expect(dataAfterRefresh.balance?.balance).to.equal('12345600000'); + expect(dataAfterRefresh.balance?.events.length).to.equal(2); + + expect(dataAfterRefresh.balance?.events[0].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[0].amount).to.equal('12300000000'); + expect(dataAfterRefresh.balance?.events[0].parameters!.txHash).to.equal( + '0x06d95c3889dcd974106e82fa27358549d9392d6fee6ea14fe1acedadc1013114', + ); + + expect(dataAfterRefresh.balance?.events[1].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[1].amount).to.equal('45600000'); + expect(dataAfterRefresh.balance?.events[1].parameters!.txHash).to.equal( + '0x38c44820c37d31fbfe3fcee9d4bcf1b887d3f90fb67d62d924af03b065a80ced', + ); + sinon.restore(); + }); + + it('can skip the get the balance of a request', async function(): Promise { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); + + // tslint:disable-next-line: no-invalid-this + this.timeout(20000); + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0x0000000000000000000000000000000000000002', + salt: 'a1a2a3a4a5a6a7a8', + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + + const request = await requestNetwork.createRequest({ + disablePaymentDetection: true, + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + clock.tick(150); + const data = await request.refresh(); + + // Payment reference should be fixed + expect( + PaymentReferenceCalculator.calculate( + data.requestId, + data.extensionsData[0].parameters.salt, + data.extensionsData[0].parameters.paymentAddress, + ), + ).to.equal('c19da4923539c37f'); + + clock.tick(150); + let dataAfterRefresh = await request.refresh(); + expect(dataAfterRefresh.balance).to.be.null; + + request.enablePaymentDetection(); + clock.tick(150); + dataAfterRefresh = await request.refresh(); + + expect(dataAfterRefresh.balance?.balance).to.equal('12345600000'); + expect(dataAfterRefresh.balance?.events.length).to.equal(2); + + expect(dataAfterRefresh.balance?.events[0].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[0].amount).to.equal('12300000000'); + expect(dataAfterRefresh.balance?.events[0].parameters!.txHash).to.equal( + '0x06d95c3889dcd974106e82fa27358549d9392d6fee6ea14fe1acedadc1013114', + ); + + expect(dataAfterRefresh.balance?.events[1].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[1].amount).to.equal('45600000'); + expect(dataAfterRefresh.balance?.events[1].parameters!.txHash).to.equal( + '0x38c44820c37d31fbfe3fcee9d4bcf1b887d3f90fb67d62d924af03b065a80ced', + ); + + request.disablePaymentDetection(); + clock.tick(150); + dataAfterRefresh = await request.refresh(); + + expect(dataAfterRefresh.balance?.balance).to.equal('12345600000'); + expect(dataAfterRefresh.balance?.events.length).to.equal(2); + + expect(dataAfterRefresh.balance?.events[0].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[0].amount).to.equal('12300000000'); + expect(dataAfterRefresh.balance?.events[0].parameters!.txHash).to.equal( + '0x06d95c3889dcd974106e82fa27358549d9392d6fee6ea14fe1acedadc1013114', + ); + + expect(dataAfterRefresh.balance?.events[1].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[1].amount).to.equal('45600000'); + expect(dataAfterRefresh.balance?.events[1].parameters!.txHash).to.equal( + '0x38c44820c37d31fbfe3fcee9d4bcf1b887d3f90fb67d62d924af03b065a80ced', + ); + sinon.restore(); + }); + + it('can get the balance on a skipped payment detection request', async function(): Promise< + void + > { + const clock: sinon.SinonFakeTimers = sinon.useFakeTimers(); + + // tslint:disable-next-line: no-invalid-this + this.timeout(20000); + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0x0000000000000000000000000000000000000002', + salt: 'a1a2a3a4a5a6a7a8', + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'mainnet', + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + }); + + const request = await requestNetwork.createRequest({ + disablePaymentDetection: true, + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + clock.tick(150); + const data = await request.refresh(); + + // Payment reference should be fixed + expect( + PaymentReferenceCalculator.calculate( + data.requestId, + data.extensionsData[0].parameters.salt, + data.extensionsData[0].parameters.paymentAddress, + ), + ).to.equal('c19da4923539c37f'); + + clock.tick(150); + let dataAfterRefresh = await request.refresh(); + expect(dataAfterRefresh.balance).to.be.null; + + const balance = await request.refreshBalance(); + expect(balance?.balance).to.equal('12345600000'); + expect(balance?.events.length).to.equal(2); + + expect(balance?.events[0].name).to.equal('payment'); + expect(balance?.events[0].amount).to.equal('12300000000'); + expect(balance?.events[0].parameters!.txHash).to.equal( + '0x06d95c3889dcd974106e82fa27358549d9392d6fee6ea14fe1acedadc1013114', + ); + + expect(balance?.events[1].name).to.equal('payment'); + expect(balance?.events[1].amount).to.equal('45600000'); + expect(balance?.events[1].parameters!.txHash).to.equal( + '0x38c44820c37d31fbfe3fcee9d4bcf1b887d3f90fb67d62d924af03b065a80ced', + ); + dataAfterRefresh = await request.getData(); + + expect(dataAfterRefresh.balance?.balance).to.equal('12345600000'); + expect(dataAfterRefresh.balance?.events.length).to.equal(2); + + expect(dataAfterRefresh.balance?.events[0].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[0].amount).to.equal('12300000000'); + expect(dataAfterRefresh.balance?.events[0].parameters!.txHash).to.equal( + '0x06d95c3889dcd974106e82fa27358549d9392d6fee6ea14fe1acedadc1013114', + ); + + expect(dataAfterRefresh.balance?.events[1].name).to.equal('payment'); + expect(dataAfterRefresh.balance?.events[1].amount).to.equal('45600000'); + expect(dataAfterRefresh.balance?.events[1].parameters!.txHash).to.equal( + '0x38c44820c37d31fbfe3fcee9d4bcf1b887d3f90fb67d62d924af03b065a80ced', + ); + + sinon.restore(); + }); + }); + + describe('ERC20 address based requests', () => { + it('can create ERC20 address based requests', async () => { + const testErc20TokenAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40'; + + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + // generate address randomly to avoid collisions + const paymentAddress = + '0x' + (await Utils.crypto.CryptoWrapper.random32Bytes()).slice(12).toString('hex'); + const refundAddress = + '0x' + (await Utils.crypto.CryptoWrapper.random32Bytes()).slice(12).toString('hex'); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED, + parameters: { + paymentAddress, + refundAddress, + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ERC20, + value: testErc20TokenAddress, + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + await new Promise((resolve): any => setTimeout(resolve, 150)); + let data = await request.refresh(); + + expect(data).to.exist; + expect(data.balance?.balance).to.equal('0'); + expect(data.balance?.events.length).to.equal(0); + expect(data.meta).to.exist; + expect(data.currency).to.equal('unknown'); + expect( + data.extensions[PaymentTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED].values.paymentAddress, + ).to.equal(paymentAddress); + expect( + data.extensions[PaymentTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED].values.refundAddress, + ).to.equal(refundAddress); + expect(data.expectedAmount).to.equal(requestParameters.expectedAmount); + + const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545'); + const erc20abiFragment = [ + 'function transfer(address _to, uint _value) returns (bool transfer)', + ]; + + // Setup the ERC20 contract interface + const contract = new ethers.Contract( + testErc20TokenAddress, + erc20abiFragment, + provider.getSigner(0), + ); + // check payment + await contract.transfer(paymentAddress, 2); + + data = await request.refresh(); + expect(data.balance?.balance).to.equal('2'); + expect(data.balance?.events.length).to.equal(1); + expect(data.balance?.events[0].amount).to.equal('2'); + expect(data.balance?.events[0].name).to.equal('payment'); + expect(data.balance?.events[0].timestamp).to.exist; + expect(data.balance?.events[0].parameters.block).to.exist; + expect(data.balance?.events[0].parameters.from.length).to.equal(42); + expect(data.balance?.events[0].parameters.to.toLowerCase()).to.equal(paymentAddress); + expect(data.balance?.events[0].parameters.txHash.length).to.equal(66); + + // check refund + await contract.transfer(refundAddress, 1); + + data = await request.refresh(); + expect(data.balance?.balance).to.equal('1'); + expect(data.balance?.events.length).to.equal(2); + expect(data.balance?.events[0].amount).to.equal('2'); + expect(data.balance?.events[0].name).to.equal('payment'); + expect(data.balance?.events[0].timestamp).to.exist; + expect(data.balance?.events[0].parameters.block).to.exist; + expect(data.balance?.events[0].parameters.from.length).to.equal(42); + expect(data.balance?.events[0].parameters.to.toLowerCase()).to.equal(paymentAddress); + expect(data.balance?.events[0].parameters.txHash.length).to.equal(66); + expect(data.balance?.events[1].amount).to.equal('1'); + expect(data.balance?.events[1].name).to.equal('refund'); + expect(data.balance?.events[1].timestamp).to.exist; + expect(data.balance?.events[1].parameters.block).to.exist; + expect(data.balance?.events[1].parameters.from.length).to.equal(42); + expect(data.balance?.events[1].parameters.to.toLowerCase()).to.equal(refundAddress); + expect(data.balance?.events[1].parameters.txHash.length).to.equal(66); + }); + }); + + describe('ERC20 proxy contract requests', () => { + it('can create ERC20 requests with given salt', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + const salt = 'ea3bc7caf64110ca'; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress: '0x6330A553Fc93768F612722BB8c2eC78aC90B3bbc', + refundAddress: '0x5AEDA56215b167893e80B4fE645BA6d5Bab767DE', + salt, + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x9FBDa871d559710256a2502A2517b794B482Db40', // Test Erc20 + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + await new Promise((resolve): any => setTimeout(resolve, 150)); + const data = await request.refresh(); + + expect(data).to.exist; + expect(data.balance?.balance).to.equal('90'); + expect(data.balance?.events.length).to.equal(2); + expect(data.meta).to.exist; + expect(data.currency).to.equal('unknown'); + expect(data.extensionsData[0].parameters.salt).to.equal(salt); + expect(data.expectedAmount).to.equal(requestParameters.expectedAmount); + }); + + it('can create ERC20 requests without given salt', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersWithoutExtensionsData, { + currency: { + network: 'private', + type: RequestLogicTypes.CURRENCY.ERC20, + value: '0x9FBDa871d559710256a2502A2517b794B482Db40', + }, + }); + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: payeeIdentity, + }); + + await new Promise((resolve): any => setTimeout(resolve, 150)); + const data = await request.refresh(); + + expect(data.extensionsData[0].parameters.salt.length).to.equal(16); + }); + }); }); diff --git a/packages/request-client.js/test/mock-storage.test.ts b/packages/request-client.js/test/mock-storage.test.ts index fee0e4b706..39a926ff94 100644 --- a/packages/request-client.js/test/mock-storage.test.ts +++ b/packages/request-client.js/test/mock-storage.test.ts @@ -1,5 +1,12 @@ import { StorageTypes } from '@requestnetwork/types'; -import { assert } from 'chai'; + +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; +const assert = chai.assert; + import 'mocha'; import MockStorage from '../src/mock-storage'; @@ -14,12 +21,9 @@ describe('mock-storage', () => { it('cannot append no data ', async () => { const storage = new MockStorage(); - try { - await storage.append(null as any); - assert.fail(); - } catch (e) { - assert.equal(e.message, 'Error: no content provided'); - } + await expect(storage.append(null as any)).to.eventually.be.rejectedWith( + 'Error: no content provided', + ); }); it('can read data', async () => { @@ -34,12 +38,7 @@ describe('mock-storage', () => { it('cannot read no data ', async () => { const storage = new MockStorage(); - try { - await storage.read(null as any); - assert.fail(); - } catch (e) { - assert.equal(e.message, 'No id provided'); - } + await expect(storage.read(null as any)).to.eventually.be.rejectedWith('No id provided'); }); it('can get all data', async () => { @@ -50,7 +49,10 @@ describe('mock-storage', () => { const { entries } = await storage.getData(); assert.notEqual(id1, id2); - assert.deepEqual(entries.map(({ content }) => content), ['stuff1', 'stuff2']); + assert.deepEqual( + entries.map(({ content }) => content), + ['stuff1', 'stuff2'], + ); assert.equal(entries.length, 2); }); diff --git a/packages/request-client.js/tsconfig.json b/packages/request-client.js/tsconfig.json index 015d8e9f6a..87c8bd0df4 100644 --- a/packages/request-client.js/tsconfig.json +++ b/packages/request-client.js/tsconfig.json @@ -12,7 +12,9 @@ { "path": "../epk-signature" }, { "path": "../multi-format" }, { "path": "../request-logic" }, + { "path": "../smart-contracts" }, { "path": "../transaction-manager" }, + { "path": "../payment-detection" }, { "path": "../types" } ] } diff --git a/packages/request-logic/.vscode/settings.json b/packages/request-logic/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/request-logic/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/request-logic/CHANGELOG.md b/packages/request-logic/CHANGELOG.md index 528af5304a..b7dc818a13 100644 --- a/packages/request-logic/CHANGELOG.md +++ b/packages/request-logic/CHANGELOG.md @@ -3,6 +3,394 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.14.0) (2020-06-29) + + +### Features + +* add the identity ethereumSmartContract to the request logic ([#218](https://github.com/RequestNetwork/requestNetwork/issues/218)) ([66d97e0](https://github.com/RequestNetwork/requestNetwork/commit/66d97e00dee7305088cb94a0edf542fe4d0bbd56)) +* amount are only number or string ([#223](https://github.com/RequestNetwork/requestNetwork/issues/223)) ([7a35bde](https://github.com/RequestNetwork/requestNetwork/commit/7a35bde63f78b9305819a80e97022fca7e9494d2)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + +### Reverts + +* bump semver from 5.6.0 to 7.1.1 ([dec266f](https://github.com/RequestNetwork/requestNetwork/commit/dec266f3e174e039d181be3d1a6e2961df072cc6)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.13.0) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + +### Reverts + +* bump semver from 5.6.0 to 7.1.1 ([dec266f](https://github.com/RequestNetwork/requestNetwork/commit/dec266f3e174e039d181be3d1a6e2961df072cc6)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.12.0) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + +### Reverts + +* bump semver from 5.6.0 to 7.1.1 ([dec266f](https://github.com/RequestNetwork/requestNetwork/commit/dec266f3e174e039d181be3d1a6e2961df072cc6)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.11.0) (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + +### Reverts + +* bump semver from 5.6.0 to 7.1.1 ([dec266f](https://github.com/RequestNetwork/requestNetwork/commit/dec266f3e174e039d181be3d1a6e2961df072cc6)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.10.0) (2020-03-23) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + +### Reverts + +* bump semver from 5.6.0 to 7.1.1 ([dec266f](https://github.com/RequestNetwork/requestNetwork/commit/dec266f3e174e039d181be3d1a6e2961df072cc6)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.9.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + +### Reverts + +* bump semver from 5.6.0 to 7.1.1 ([dec266f](https://github.com/RequestNetwork/requestNetwork/commit/dec266f3e174e039d181be3d1a6e2961df072cc6)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +## [0.8.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.8.3) (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +## [0.8.2](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.8.2) (2019-12-18) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) +* fix old version id mismatch issue ([#98](https://github.com/RequestNetwork/requestNetwork/issues/98)) ([c4d3f29](https://github.com/RequestNetwork/requestNetwork/commit/c4d3f291b7667f426a4a95cac7d65db90d13921b)) + + + +# 0.10.0 (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + +## [0.8.1](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.8.0...@requestnetwork/request-logic@0.8.1) (2019-12-04) + + +### Bug Fixes + +* rollback legacy request currency renaming from DAI to SAI ([#622](https://github.com/RequestNetwork/requestNetwork/issues/622)) ([2882811](https://github.com/RequestNetwork/requestNetwork/commit/28828117f6490ada05180f2607098d3ebada681a)) + + + + + # [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-logic@0.7.1...@requestnetwork/request-logic@0.8.0) (2019-11-20) diff --git a/packages/request-logic/package.json b/packages/request-logic/package.json index fb9860ca72..bc839509f4 100644 --- a/packages/request-logic/package.json +++ b/packages/request-logic/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/request-logic", - "version": "0.8.0", + "version": "0.14.0", "publishConfig": { "access": "public" }, @@ -32,45 +32,46 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "example": "ts-node specs/example/example.ts", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "stryker": "stryker run", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/advanced-logic": "0.6.0", - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/advanced-logic": "0.15.0", + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "semver": "5.6.0" }, "devDependencies": { - "@stryker-mutator/core": "2.1.0", + "@stryker-mutator/core": "2.4.0", "@stryker-mutator/html-reporter": "2.1.0", "@stryker-mutator/mocha-framework": "2.1.0", "@stryker-mutator/mocha-runner": "2.1.0", - "@stryker-mutator/typescript": "2.1.0", + "@stryker-mutator/typescript": "2.3.0", "@types/chai": "4.1.7", "@types/chai-spies": "1.0.0", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@types/semver": "5.5.0", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", + "sinon": "9.0.1", "source-map-support": "0.5.13", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" }, diff --git a/packages/request-logic/specs/request-logic-specification-v2.0.0.md b/packages/request-logic/specs/request-logic-specification-v2.0.0.md deleted file mode 100644 index c9d9ad1c60..0000000000 --- a/packages/request-logic/specs/request-logic-specification-v2.0.0.md +++ /dev/null @@ -1,1057 +0,0 @@ -# Request logic Specification v2.0.0 - -You can be interested in this document if: - -- You want to create your own implementation of the request protocol -- You are curious enough to dive and see what is under the hood of the request protocol - -You don't need to read this document if: - -- You want to develop an app using the request protocol (see the API library instead [here](/packages/request-client.js)) - -## Content table - -- [Request logic Specification v2.0.0](#request-logic-specification-v200) - - [Content table](#content-table) - - [Request](#request) - - [Properties](#properties) - - [Actions](#actions) - - [List of possible actions](#list-of-possible-actions) - - [Create](#create) - - [Parameters](#parameters) - - [Conditions](#conditions) - - [Result](#result) - - [Example](#example) - - [Accept](#accept) - - [Parameters](#parameters-1) - - [Conditions](#conditions-1) - - [Result](#result-1) - - [Example](#example-1) - - [Cancel](#cancel) - - [Parameters](#parameters-2) - - [Conditions](#conditions-2) - - [Result](#result-2) - - [Example](#example-2) - - [ReduceExpectedAmount](#reduceexpectedamount) - - [Parameters](#parameters-3) - - [Conditions](#conditions-3) - - [Result](#result-3) - - [Example](#example-3) - - [IncreaseExpectedAmount](#increaseexpectedamount) - - [Parameters](#parameters-4) - - [Conditions](#conditions-4) - - [Result](#result-4) - - [Example](#example-4) - - [AddExtensionsData](#addextensionsdata) - - [Parameters](#parameters-5) - - [Conditions](#conditions-5) - - [Result](#result-5) - - [Example](#example-5) - - [Get a request from a list of actions](#get-a-request-from-a-list-of-actions) - - [Types](#types) - - [Identity type](#identity-type) - - [Signature type](#signature-type) - - [Amount type](#amount-type) - - [Identity, Role and Signature](#identity--role-and-signature) - - [Identity](#identity) - - [Roles](#roles) - - [Signature](#signature) - - [SignatureParameters](#signatureparameters) - - [Signature methods supported](#signature-methods-supported) - - [Identity types supported](#identity-types-supported) - - [How to sign a JSON object](#how-to-sign-a-json-object) - - [Note on Version and Backward compatibility](#note-on-version-and-backward-compatibility) - - [Example of a request state actions after actions](#example-of-a-request-state-actions-after-actions) - -## Request - -A request is the JSON object which`properties` returned from a list of `actions` that satisfies the following formats: - -### Properties - -| Property | Type | Description | -| ------------------ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **requestId** | string | ID as unique as possible | -| **creator** | Identity | Identity of the creator of the request | -| **payee** | Identity | Identity of the payee | -| **payer** | Identity | Identity of the payer | -| **expectedAmount** | Amount | Amount expected to be paid | -| **currency** | String | Currency of the expected amount | -| **state** | Enum('created', 'accepted', 'canceled') | State of the request | -| **events** | Array | List of the actions performed | -| **extensionsData** | Array | List of data used by the above layer | -| **version** | String | Specification version by the request _(2.0.0' here)_ | -|  **timestamp** | Number | - Timestamp of the request creation in seconds
    - this timestamp is given by the creator. It is not trustless.
    - This timestamp is also used to differentiate between identical requests | -|  **nonce** | Number | Number to differentiate several identical requests with the same timestamp | - -Example - -```JSON -{ - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "currency":"ETH", - "events":[ - { - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataAddedLength":1, - "isSignedRequest":false - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } - } - ], - "expectedAmount":"123400000000000000", - "extensionsData":[ - { - "id":"extension1", - "value":"whatever1" - } - ], - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "requestId":"0x1c2610cbc5bee43b6bc9800e69ec832fb7d50ea098a88877a0afdcac5981d3f8", - "state":"created", - "timestamp": 1545224094, - "version":"2.0.0" -} -``` - -### Actions - -An action is a JSON object created to modify the properties of a request. -An action is performed on a `request` under **conditions** specified in the action itself. -An action is ignored if there exists a previous identical action. If their normalized hashes (see [How to sign a JSON object](#how-to-sign-a-json-object)) are identical the second action is ignored. -This mechanism counters a replay attack where one can replay an action already signed (e.g.: reduceExpectedAmount). -If one wants to create two similar actions, he must add an arbitrary number (see nonce later in this document). - -IMPORTANT : - -- The `actions` of a `request` are **ordered** (it means that only one `action` modify a request at a time) -- An `action` which does not satisfy its condition will be simply ignored - -| Property | Type | Description | -| -------------- | --------- | -------------------------------------------------------------- | -| **name** | Enum() | Name of the action to perform _(see list of possible actions)_ | -| **parameters** | Object | Parameters of the actions | -| **signature** | Signature | Signature of the object { name, parameters, version } | - -#### List of possible actions - -| Name | Description | Role Authorized | -| -------------------------- | ---------------------------- | ------------------------- | -| **create** | create a request | payee, payer | -| **accept** | accept a request | payer | -| **cancel** | cancel a request | payee, payer | -| **reduceExpectedAmount** | reduce the expected amount | payee | -| **increaseExpectedAmount** | increase the expected amount | payer | -| **addExtensionsData** | add data for the extensions | payee, payer, third-party | - ---- - -#### Create - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | -| **version** | String | specification version | **Mandatory** | -| **expectedAmount** | Amount | amount expected to be paid | **Mandatory** | -| **currency** | String | Currency of the expected amount | **Mandatory** | -| **timestamp** | Number | - Timestamp of the request creation in seconds
    - this timestamp is given by the creator. It is not trustless.
    - This timestamp is also used to differentiate between identical requests | **Mandatory** | -| **payee** | Identity | identity of the payee | Optional _if payer given_ | -| **payer** | Identity | identity of the payer | Optional _if payee given_ | -| **extensionsData** | Array | list of data used by the above layer | Optional | -| **nonce** | Number | Number to differentiate several identical requests with the same timestamp
    - should be incremented by one for every new identical request | Optional | - -##### Conditions - -This action is valid, if: - -- the Role of the action **signer is payee or payer** - -##### Result - -A request created with the following properties: - -| Property | Value | -| ------------------ | ------------------------------------------------------------------- | -| **requestId** | Hash of this action | -| **creator** | Identity of the signer | -| **payee** | payee from parameters if exists, undefined otherwise | -| **payee** | payer from parameters if exists, undefined otherwise | -| **expectedAmount** | expectedAmount from parameters | -| **currency** | currency from parameters | -| **state** | `created` if signer is **payee**, `accepted` if signer is **payer** | -| **version** | versions of Request protocol for which the request has been created | -| **extensionsData** | extensionsData from parameters if exists, [] otherwise | -| **events** | Array with one 'create' event _(see below)_ | - -the 'create' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'create' | -| **parameters** | Object | -| **parameters.isSignedRequest** | false | -| **parameters.ExpectedAmount** | expectedAmount from parameters | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of creation action: - -```JSON -{ - "name":"create", - "parameters":{ - "currency":"ETH", - "expectedAmount":"123400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "timestamp": 1545224094, - }, - "version":"2.0.0", - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'create' event: - -```JSON -{ - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsLength":0, - "isSignedRequest":false - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### Accept - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ------------------------------------ | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payer** -- the Role of the action **signer is the payer** -- the request state is `created` - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | ---------------------------------------------------- | -| **state** | `accepted` | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'accept' event _(see below)_ at its end | - -the 'accept' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'accept' | -| **parameters** | Object | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of action creation: - -```JSON -{ - "name":"accept", - "parameters":{ - "requestId":"0xd38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'accept' event: - -```JSON -{ - "name":"accept", - "parameters":{ - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### Cancel - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ------------------------------------ | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payer** -- the Role of the action **signer is the payer** -- the request state is `created` - - **Or, if**: - -- the request **has a payee** -- the Role of the action **signer is the payee** -- the request state is **NOT** `canceled` - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | ---------------------------------------------------- | -| **state** | `canceled` | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'cancel' event _(see below)_ at its end | - -the 'cancel' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'cancel' | -| **parameters** | Object | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of cancel action: - -```JSON -{ - "name":"cancel", - "parameters":{ - "requestId":"0xd38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" -} -} -``` - -Example of 'cancel' event: - -```JSON -{ - "name":"cancel", - "parameters":{ - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### ReduceExpectedAmount - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ----------------------------------------- | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **deltaAmount** | Amount | amount to reduce to the expectedAmount | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | -| **nonce** | Number | Number to differentiate identical actions | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payee** -- the Role of the action **signer is the payee** -- the request state is **NOT** `canceled` -- the **deltaAmount is smaller or equal to expectedAmount** - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | ------------------------------------------------------------ | -| **expectedAmount** | expectedAmount **minus** deltaAmount from parameters | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'reduceExpectedAmount' event _(see below)_ at its end | - -the 'reduceExpectedAmount' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'reduceExpectedAmount' | -| **parameters** | Object | -| **parameters.deltaAmount** | deltaAmount from parameters    | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of reduceExpectedAmount action: - -```JSON -{ - "name":"reduceExpectedAmount", - "parameters":{ - "requestId":"0xd38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - "deltaAmount": "10000000", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'reduceExpectedAmount' event: - -```JSON -{ - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount": "10000000", - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### IncreaseExpectedAmount - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ----------------------------------------- | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **deltaAmount** | Amount | amount to add to the expectedAmount | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | -| **nonce** | Number | Number to differentiate identical actions | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payer** -- the Role of the action **signer is the payer** -- the request state is **NOT** `canceled` - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | -------------------------------------------------------------- | -| **expectedAmount** | expectedAmount **plus** deltaAmount from parameters | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'increaseExpectedAmount' event _(see below)_ at its end | - -the 'increaseExpectedAmount' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'increaseExpectedAmount' | -| **parameters** | Object | -| **parameters.deltaAmount** | deltaAmount from parameters    | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of increaseExpectedAmount action: - -```JSON -{ - "name":"increaseExpectedAmount", - "parameters":{ - "requestId":"0xd38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - "deltaAmount": "10000000", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'increaseExpectedAmount' event: - -```JSON -{ - "name":"increaseExpectedAmount", - "parameters":{ - "deltaAmount": "10000000", - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### AddExtensionsData - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ----------------------------------------- | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | **Mandatory** | -| **nonce** | Number | Number to differentiate identical actions | Optional | - -##### Conditions - -None. - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | --------------------------------------------------------- | -| **extensionsData** | concatenate with the extensionsData from parameters | -| **events** | add an 'addExtensionsData' event _(see below)_ at its end | - -the 'addExtensionsData' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'addExtensionsData' | -| **parameters** | Object | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of addExtensionsData action: - -```JSON -{ - "name":"addExtensionsData", - "parameters":{ - "requestId":"0xd38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - "extensionsData": [{ - "id": "pn_btc_address_based", - "action": "addPaymentAddress", - "parameters": { - "refundAddress": "2NEH4zBsz3za2hzjyjXsGETz4SpHzhjiSiG" - } - }] - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'addExtensionsData' event: - -```JSON -{ - "name":"addExtensionsData", - "parameters":{ - "extensionsLength":1, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -## Get a request from a list of actions - -As mentioned above a `request` is an JSON object with `properties` modified by `actions`. -From another perspective, you can get the `properties` of a `request` by interpreting a list of `actions`. - ---- - -## Types - -### Identity type - -(see Identity, Role and Signature) - -### Signature type - -(see Identity, Role and Signature) - -### Amount type - -To have a standard format for number that avoid limitation of size number in some languages, a valid amount is a **`string`** representing a **positive `integer`** in **base 10**. - -Examples: - -```JSON -"11235813213455891442333776109871597258441816765" // OK -"0" // OK -0 // not valid -11235813213455891442333776109871597258441816765 // not valid -"-1123" // not valid -"3.14159" // not valid -"NaN" // not valid -"thirteen" // not valid -"0x12324094" // not valid -``` - -**Note:** Decimal number are not allowed for amount. But, each currency will be defined with a number of decimal (e.g.: 2 for USD, 18 for ETH, 8 for BTC ) - -Examples: - -```JSON -"12345667890123456789" -// could mean: -// 123456678901234567.89 USD -// 123456678901.23456789 BTC -// 12.345667890123456789 ETH -``` - ---- - -## Identity, Role and Signature - -To manage low level identity in the request logic we use three different types of objects: - -- `Identity`: a public piece of information an actor uses to identify themself _(e.g.: the address in ethereum)_ -- `Signature`: a proof of acknowledgment from an `Identity` -- `SignatureParameters`: the parameters needed for an `Identity` to create a `Signature` _(e.g.: the privatekey in ethereum)_ - And two methods: -- `sign()`: generate a `Signature` for data from `SignatureParameters` -- `recover()`: get the `Identity` from data and its `Signature` - -``` - +----------------+ - +---------------------+ | Signed data | - |/SignatureParameters/| | | - +-----------+---------+ | +--------+ | - | | | Data | | -+--------+ Sign() | | +--------+ | Recover() +----------+ -| Data +------------------------------->+ +------------------->+/Identity/| -+--------+ | +-----------+ | +----------+ - | |/Signature/| | - | +-----------+ | - +----------------+ -``` - -### Identity - -this is a json object with two properties: - -- `type`: the type of identity _(e.g.: 'ethereumAddress' for an ethereum identity like)_ -- `value`: the actual value to recognize the identity _(e.g.: for 'ethereumAddress' type, it would be something like '0x742d35cc6634c0532925a3b844bc454e4438f44e')_ - -Note: Two identities are equal if and only if their `type` and `value` are equal - -Example - -```JSON -{ - "type": "ethereumAddress", - "value": "0x742d35cc6634c0532925a3b844bc454e4438f44e" -} -``` - -### Roles - -Any `Identity` have one and only one `Role` in each request: - -| Role | conditions | -| -------------- | ------------------------------------------------------------ | -| **payee** | the identity is equal to the request payee | -| **payer** | the identity is equal to the request payer | -| **thirdparty** | **every other condition** listed above are **NOT satisfied** | - -### Signature - -this is a json object with two properties: - -- `method`: the method to sign _(e.g.: 'ecdsa' for an Elliptic Curve Signature)_ -- `value`: the signature itself - -Note: Two signatures are equal if and only if their `method` and `value` are equal - -Example - -```JSON -{ - "method": "ecdsa", - "value": "0xe649fdfe25c3ee33061a8159be9b941141121c5bed8d07664cb67b7912819b4539841a206636c190178ac58978926dad1fe3637a10b656705b71bda5e187510c1b" -} -``` - -### SignatureParameters - -this is a json object with two properties: - -- `method`: the method to sign _(e.g.: 'ecdsa' for an Elliptic Curve Signature)_ -- `privateKey`: the private key to sign with - -Example - -```JSON -{ - "method": "ecdsa", - "privateKey": "0x3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266" -} -``` - -### Signature methods supported - -List of the Signature methods supported in this version: - -| Method | Signature Value | SignatureParameter privateKey | Identity Type from recover() | Comment | -| ------------------ | -------------------------------------- | -------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **ecdsa** | hexadecimal string (e.g.: "0xe649f..") | hexadecimal string (e.g.: "0xe649f..") | **EthereumAddress** _(see "Identity types supported" below)_ | -| **ecdsa-ethereum** | hexadecimal string (e.g.: "0xe649f..") | hexadecimal string (e.g.: "0xe649f..") | **EthereumAddress** _(see "Identity types supported" below)_ | Similar to ecdsa but the signature is made with the Ethereum padding `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. Used for signing with the web3 tools (like Metamask). | - -### Identity types supported - -List of the Identity types supported in this version: - -| Type | Identity Value | Signature Method | -| ------------------- | -------------------------------------- | ----------------------------------------------------- | -| **ethereumAddress** | hexadecimal string (e.g.: "0xe649f..") | **ecdsa** _(see "Signature methods supported" above)_ | - -### How to sign a JSON object - -The signature of an JSON object is made on the `keccak256` hash of the object: - -- with its properties deeply alphabetically sorted -- stringified -- lowerCased - -## Note on Version and Backward compatibility - -The versions of the Request logic specifications follow the semver 2.0.0. -Every request is create with one and only one version of the specification. - -By default, an implementation of the specifications will be able to handle only the requests following the specification versions with: - -- The **same `major`** version -- A **`minor`** version **equal or inferior** - -Some exceptions will be done for versions that need to be blacklisted (e.g.: versions with vulnerabilities) - -## Example of a request state actions after actions - -Bob wants to request 0.12 BTC to Alice. - -Bob has his private `Signature parameters` from which it can generate his `Identity`: - -```JSON -{ - "signatureParams": { - "method": "ecdsa", - "privateKey": "0x04674d2e53e0e14653487d7323cc5f0a7959c83067f5654cafe4094bde90fa8a", - }, - "identity": { - "type": "ethereumAddress", - "value": "0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce", - } -} -``` - -Alice has his own `Signature parameters` and `Identity`: - -```JSON -{ - "signatureParams": { - "method": "ecdsa", - "privateKey": "0x0906ff14227cead2b25811514302d57706e7d5013fcc40eca5985b216baeb998", - }, - "identity": { - "type": "ethereumAddress", - "value": "0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6", - } -} -``` - -1. Bob creates a request of 0.1234 ETH to Alice -2. Bob makes a discount of 0.1 ETH -3. Alice accept the request - -We now follow the `state` of the request after each `actions`: - -### 1. Bob creates a request of 0.1234 ETH to Alice - -The action to create the request from Bob to Alice from Bob (signed by Bob): - -```JSON -{ - "data":{ - "name":"create", - "parameters":{ - "currency":"ETH", - "expectedAmount":"123400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030 - }, - "version":"2.0.0" - }, - "signature":{ - "method":"ecdsa", - "value":"0xac9e9e43381d882f3edc506277b8ad74ca3fc0ed2184663b65ccbab921df114807d7e68fd03b668afffee1feb977c9082657f1a05f57c0b1f92e9b46ca22dfc31c" - } -} -``` - -The request state after interpreting the action above: - -```JSON -{ - "currency":"ETH", - "expectedAmount":"123400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030, - "requestId":"0xd251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.0", - "events":[ - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataLength":0, - "isSignedRequest":false - } - } - ], - "state":"created", - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - -### 2. Bob makes a discount of 0.1 ETH - -The action to make a discount (signed by Bob); - -```JSON -{ - "data":{ - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount":"100000000000000000", - "requestId":"0xd251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905" - }, - "version":"2.0.0" - }, - "signature":{ - "method":"ecdsa", - "value":"0x649c5fdd54e781dfb480f9b01cc9ac8cd9d7630e2dd3b9a8443865ee00d5015805e6b3bbce9be35ef124e92afba80db79d913cdc756fe4e911f3413ae6a24b971b" - } -} -``` - -The request state after interpreting the new action with the previous state: - -```JSON -{ - "currency":"ETH", - "expectedAmount":"23400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030, - "requestId":"0xd251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.0", - "events":[ - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataLength":0, - "isSignedRequest":false - } - }, - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount":"100000000000000000", - "extensionsDataLength":0 - } - } - ], - "state":"created", - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - -### 3. Alice accept the request - -The action to accept the request (signed by Alice): - -```JSON -{ - "data":{ - "name":"accept", - "parameters":{ - "requestId":"0xd251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905" - }, - "version":"2.0.0" - }, - "signature":{ - "method":"ecdsa", - "value":"0xf94380c553c90810deb5625571649759f8591bf923f5773e436fec322d01752d676a6f822dee2c2097f4bb70b16273b4826e6026f9f98a31cfafab8f1bdda2eb1b" - } -} -``` - -The request state after interpreting the new action with the previous state: - -```JSON -{ - "currency":"ETH", - "expectedAmount":"23400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030, - "requestId":"0xd251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.0", - "events":[ - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataLength":0, - "isSignedRequest":false - } - }, - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount":"100000000000000000", - "extensionsDataLength":0 - } - }, - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "name":"accept", - "parameters":{ - "extensionsDataLength":0 - } - } - ], - "state":"accepted", - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` diff --git a/packages/request-logic/specs/request-logic-specification-v2.0.1.md b/packages/request-logic/specs/request-logic-specification-v2.0.1.md deleted file mode 100644 index 9be8cc4908..0000000000 --- a/packages/request-logic/specs/request-logic-specification-v2.0.1.md +++ /dev/null @@ -1,1057 +0,0 @@ -# Request logic Specification v2.0.1 - -You can be interested in this document if: - -- You want to create your own implementation of the request protocol -- You are curious enough to dive and see what is under the hood of the request protocol - -You don't need to read this document if: - -- You want to develop an app using the request protocol (see the API library instead [here](/packages/request-client.js)) - -## Content table - -- [Request logic Specification v2.0.1](#request-logic-specification-v200) - - [Content table](#content-table) - - [Request](#request) - - [Properties](#properties) - - [Actions](#actions) - - [List of possible actions](#list-of-possible-actions) - - [Create](#create) - - [Parameters](#parameters) - - [Conditions](#conditions) - - [Result](#result) - - [Example](#example) - - [Accept](#accept) - - [Parameters](#parameters-1) - - [Conditions](#conditions-1) - - [Result](#result-1) - - [Example](#example-1) - - [Cancel](#cancel) - - [Parameters](#parameters-2) - - [Conditions](#conditions-2) - - [Result](#result-2) - - [Example](#example-2) - - [ReduceExpectedAmount](#reduceexpectedamount) - - [Parameters](#parameters-3) - - [Conditions](#conditions-3) - - [Result](#result-3) - - [Example](#example-3) - - [IncreaseExpectedAmount](#increaseexpectedamount) - - [Parameters](#parameters-4) - - [Conditions](#conditions-4) - - [Result](#result-4) - - [Example](#example-4) - - [AddExtensionsData](#addextensionsdata) - - [Parameters](#parameters-5) - - [Conditions](#conditions-5) - - [Result](#result-5) - - [Example](#example-5) - - [Get a request from a list of actions](#get-a-request-from-a-list-of-actions) - - [Types](#types) - - [Identity type](#identity-type) - - [Signature type](#signature-type) - - [Amount type](#amount-type) - - [Identity, Role and Signature](#identity--role-and-signature) - - [Identity](#identity) - - [Roles](#roles) - - [Signature](#signature) - - [SignatureParameters](#signatureparameters) - - [Signature methods supported](#signature-methods-supported) - - [Identity types supported](#identity-types-supported) - - [How to sign a JSON object](#how-to-sign-a-json-object) - - [Note on Version and Backward compatibility](#note-on-version-and-backward-compatibility) - - [Example of a request state actions after actions](#example-of-a-request-state-actions-after-actions) - -## Request - -A request is the JSON object which`properties` returned from a list of `actions` that satisfies the following formats: - -### Properties - -| Property | Type | Description | -| ------------------ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **requestId** | string | ID as unique as possible | -| **creator** | Identity | Identity of the creator of the request | -| **payee** | Identity | Identity of the payee | -| **payer** | Identity | Identity of the payer | -| **expectedAmount** | Amount | Amount expected to be paid | -| **currency** | String | Currency of the expected amount | -| **state** | Enum('created', 'accepted', 'canceled') | State of the request | -| **events** | Array | List of the actions performed | -| **extensionsData** | Array | List of data used by the above layer | -| **version** | String | Specification version by the request _(2.0.1' here)_ | -|  **timestamp** | Number | - Timestamp of the request creation in seconds
    - this timestamp is given by the creator. It is not trustless.
    - This timestamp is also used to differentiate between identical requests | -|  **nonce** | Number | Number to differentiate several identical requests with the same timestamp | - -Example - -```JSON -{ - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "currency":"ETH", - "events":[ - { - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataAddedLength":1, - "isSignedRequest":false - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } - } - ], - "expectedAmount":"123400000000000000", - "extensionsData":[ - { - "id":"extension1", - "value":"whatever1" - } - ], - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "requestId":"011c2610cbc5bee43b6bc9800e69ec832fb7d50ea098a88877a0afdcac5981d3f8", - "state":"created", - "timestamp": 1545224094, - "version":"2.0.1" -} -``` - -### Actions - -An action is a JSON object created to modify the properties of a request. -An action is performed on a `request` under **conditions** specified in the action itself. -An action is ignored if there exists a previous identical action. If their normalized hashes (see [How to sign a JSON object](#how-to-sign-a-json-object)) are identical the second action is ignored. -This mechanism counters a replay attack where one can replay an action already signed (e.g.: reduceExpectedAmount). -If one wants to create two similar actions, he must add an arbitrary number (see nonce later in this document). - -IMPORTANT : - -- The `actions` of a `request` are **ordered** (it means that only one `action` modify a request at a time) -- An `action` which does not satisfy its condition will be simply ignored - -| Property | Type | Description | -| -------------- | --------- | -------------------------------------------------------------- | -| **name** | Enum() | Name of the action to perform _(see list of possible actions)_ | -| **parameters** | Object | Parameters of the actions | -| **signature** | Signature | Signature of the object { name, parameters, version } | - -#### List of possible actions - -| Name | Description | Role Authorized | -| -------------------------- | ---------------------------- | ------------------------- | -| **create** | create a request | payee, payer | -| **accept** | accept a request | payer | -| **cancel** | cancel a request | payee, payer | -| **reduceExpectedAmount** | reduce the expected amount | payee | -| **increaseExpectedAmount** | increase the expected amount | payer | -| **addExtensionsData** | add data for the extensions | payee, payer, third-party | - ---- - -#### Create - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | -| **version** | String | specification version | **Mandatory** | -| **expectedAmount** | Amount | amount expected to be paid | **Mandatory** | -| **currency** | String | Currency of the expected amount | **Mandatory** | -| **timestamp** | Number | - Timestamp of the request creation in seconds
    - this timestamp is given by the creator. It is not trustless.
    - This timestamp is also used to differentiate between identical requests | **Mandatory** | -| **payee** | Identity | identity of the payee | Optional _if payer given_ | -| **payer** | Identity | identity of the payer | Optional _if payee given_ | -| **extensionsData** | Array | list of data used by the above layer | Optional | -| **nonce** | Number | Number to differentiate several identical requests with the same timestamp
    - should be incremented by one for every new identical request | Optional | - -##### Conditions - -This action is valid, if: - -- the Role of the action **signer is payee or payer** - -##### Result - -A request created with the following properties: - -| Property | Value | -| ------------------ | ------------------------------------------------------------------- | -| **requestId** | Hash of the signed action | -| **creator** | Identity of the signer | -| **payee** | payee from parameters if exists, undefined otherwise | -| **payee** | payer from parameters if exists, undefined otherwise | -| **expectedAmount** | expectedAmount from parameters | -| **currency** | currency from parameters | -| **state** | `created` if signer is **payee**, `accepted` if signer is **payer** | -| **version** | versions of Request protocol for which the request has been created | -| **extensionsData** | extensionsData from parameters if exists, [] otherwise | -| **events** | Array with one 'create' event _(see below)_ | - -the 'create' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'create' | -| **parameters** | Object | -| **parameters.isSignedRequest** | false | -| **parameters.ExpectedAmount** | expectedAmount from parameters | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of creation action: - -```JSON -{ - "name":"create", - "parameters":{ - "currency":"ETH", - "expectedAmount":"123400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "timestamp": 1545224094, - }, - "version":"2.0.1", - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'create' event: - -```JSON -{ - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsLength":0, - "isSignedRequest":false - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### Accept - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ------------------------------------ | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payer** -- the Role of the action **signer is the payer** -- the request state is `created` - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | ---------------------------------------------------- | -| **state** | `accepted` | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'accept' event _(see below)_ at its end | - -the 'accept' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'accept' | -| **parameters** | Object | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of action creation: - -```JSON -{ - "name":"accept", - "parameters":{ - "requestId":"01d38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'accept' event: - -```JSON -{ - "name":"accept", - "parameters":{ - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### Cancel - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ------------------------------------ | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payer** -- the Role of the action **signer is the payer** -- the request state is `created` - - **Or, if**: - -- the request **has a payee** -- the Role of the action **signer is the payee** -- the request state is **NOT** `canceled` - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | ---------------------------------------------------- | -| **state** | `canceled` | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'cancel' event _(see below)_ at its end | - -the 'cancel' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'cancel' | -| **parameters** | Object | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of cancel action: - -```JSON -{ - "name":"cancel", - "parameters":{ - "requestId":"01d38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" -} -} -``` - -Example of 'cancel' event: - -```JSON -{ - "name":"cancel", - "parameters":{ - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### ReduceExpectedAmount - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ----------------------------------------- | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **deltaAmount** | Amount | amount to reduce to the expectedAmount | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | -| **nonce** | Number | Number to differentiate identical actions | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payee** -- the Role of the action **signer is the payee** -- the request state is **NOT** `canceled` -- the **deltaAmount is smaller or equal to expectedAmount** - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | ------------------------------------------------------------ | -| **expectedAmount** | expectedAmount **minus** deltaAmount from parameters | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'reduceExpectedAmount' event _(see below)_ at its end | - -the 'reduceExpectedAmount' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'reduceExpectedAmount' | -| **parameters** | Object | -| **parameters.deltaAmount** | deltaAmount from parameters    | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of reduceExpectedAmount action: - -```JSON -{ - "name":"reduceExpectedAmount", - "parameters":{ - "requestId":"01d38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - "deltaAmount": "10000000", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'reduceExpectedAmount' event: - -```JSON -{ - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount": "10000000", - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### IncreaseExpectedAmount - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ----------------------------------------- | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **deltaAmount** | Amount | amount to add to the expectedAmount | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | Optional | -| **nonce** | Number | Number to differentiate identical actions | Optional | - -##### Conditions - -This action is valid, if: - -- the request **has a payer** -- the Role of the action **signer is the payer** -- the request state is **NOT** `canceled` - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | -------------------------------------------------------------- | -| **expectedAmount** | expectedAmount **plus** deltaAmount from parameters | -| **extensionsData** | concat the extensionsData from parameters at its end | -| **events** | add an 'increaseExpectedAmount' event _(see below)_ at its end | - -the 'increaseExpectedAmount' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'increaseExpectedAmount' | -| **parameters** | Object | -| **parameters.deltaAmount** | deltaAmount from parameters    | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of increaseExpectedAmount action: - -```JSON -{ - "name":"increaseExpectedAmount", - "parameters":{ - "requestId":"01d38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - "deltaAmount": "10000000", - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'increaseExpectedAmount' event: - -```JSON -{ - "name":"increaseExpectedAmount", - "parameters":{ - "deltaAmount": "10000000", - "extensionsLength":0, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -#### AddExtensionsData - -##### Parameters - -| | Type | Description | Requirement | -| ------------------ | ------ | ----------------------------------------- | ------------- | -| **requestId** | String | ID of the request | **Mandatory** | -| **extensionsData** | Array | list of data used by the above layer | **Mandatory** | -| **nonce** | Number | Number to differentiate identical actions | Optional | - -##### Conditions - -None. - -##### Result - -Modify the following properties of the request: - -| Property | Value | -| ------------------ | --------------------------------------------------------- | -| **extensionsData** | concatenate with the extensionsData from parameters | -| **events** | add an 'addExtensionsData' event _(see below)_ at its end | - -the 'addExtensionsData' event: - -| Property | Value | -| ---------------------------------------- | ----------------------------------------------- | -| **name** | 'addExtensionsData' | -| **parameters** | Object | -| **parameters.extensionsDataAddedLength** | length of the extensionsData from parameters    | -| **actionSigner** | Identity of the signer | - -##### Example - -Example of addExtensionsData action: - -```JSON -{ - "name":"addExtensionsData", - "parameters":{ - "requestId":"01d38a203d25e91ae0e0d3bcf149c44dac80e0990a812fce5ecd14bd27cb7fed2e", - "extensionsData": [{ - "id": "pn_btc_address_based", - "action": "addPaymentAddress", - "parameters": { - "refundAddress": "2NEH4zBsz3za2hzjyjXsGETz4SpHzhjiSiG" - } - }] - }, - "signature":{ - "method":"ecdsa", - "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" - } -} -``` - -Example of 'addExtensionsData' event: - -```JSON -{ - "name":"addExtensionsData", - "parameters":{ - "extensionsLength":1, - }, - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - ---- - -## Get a request from a list of actions - -As mentioned above a `request` is an JSON object with `properties` modified by `actions`. -From another perspective, you can get the `properties` of a `request` by interpreting a list of `actions`. - ---- - -## Types - -### Identity type - -(see Identity, Role and Signature) - -### Signature type - -(see Identity, Role and Signature) - -### Amount type - -To have a standard format for number that avoid limitation of size number in some languages, a valid amount is a **`string`** representing a **positive `integer`** in **base 10**. - -Examples: - -```JSON -"11235813213455891442333776109871597258441816765" // OK -"0" // OK -0 // not valid -11235813213455891442333776109871597258441816765 // not valid -"-1123" // not valid -"3.14159" // not valid -"NaN" // not valid -"thirteen" // not valid -"0x12324094" // not valid -``` - -**Note:** Decimal number are not allowed for amount. But, each currency will be defined with a number of decimal (e.g.: 2 for USD, 18 for ETH, 8 for BTC ) - -Examples: - -```JSON -"12345667890123456789" -// could mean: -// 123456678901234567.89 USD -// 123456678901.23456789 BTC -// 12.345667890123456789 ETH -``` - ---- - -## Identity, Role and Signature - -To manage low level identity in the request logic we use three different types of objects: - -- `Identity`: a public piece of information an actor uses to identify themself _(e.g.: the address in ethereum)_ -- `Signature`: a proof of acknowledgment from an `Identity` -- `SignatureParameters`: the parameters needed for an `Identity` to create a `Signature` _(e.g.: the privatekey in ethereum)_ - And two methods: -- `sign()`: generate a `Signature` for data from `SignatureParameters` -- `recover()`: get the `Identity` from data and its `Signature` - -``` - +----------------+ - +---------------------+ | Signed data | - |/SignatureParameters/| | | - +-----------+---------+ | +--------+ | - | | | Data | | -+--------+ Sign() | | +--------+ | Recover() +----------+ -| Data +------------------------------->+ +------------------->+/Identity/| -+--------+ | +-----------+ | +----------+ - | |/Signature/| | - | +-----------+ | - +----------------+ -``` - -### Identity - -this is a json object with two properties: - -- `type`: the type of identity _(e.g.: 'ethereumAddress' for an ethereum identity like)_ -- `value`: the actual value to recognize the identity _(e.g.: for 'ethereumAddress' type, it would be something like '0x742d35cc6634c0532925a3b844bc454e4438f44e')_ - -Note: Two identities are equal if and only if their `type` and `value` are equal - -Example - -```JSON -{ - "type": "ethereumAddress", - "value": "0x742d35cc6634c0532925a3b844bc454e4438f44e" -} -``` - -### Roles - -Any `Identity` have one and only one `Role` in each request: - -| Role | conditions | -| -------------- | ------------------------------------------------------------ | -| **payee** | the identity is equal to the request payee | -| **payer** | the identity is equal to the request payer | -| **thirdparty** | **every other condition** listed above are **NOT satisfied** | - -### Signature - -this is a json object with two properties: - -- `method`: the method to sign _(e.g.: 'ecdsa' for an Elliptic Curve Signature)_ -- `value`: the signature itself - -Note: Two signatures are equal if and only if their `method` and `value` are equal - -Example - -```JSON -{ - "method": "ecdsa", - "value": "0xe649fdfe25c3ee33061a8159be9b941141121c5bed8d07664cb67b7912819b4539841a206636c190178ac58978926dad1fe3637a10b656705b71bda5e187510c1b" -} -``` - -### SignatureParameters - -this is a json object with two properties: - -- `method`: the method to sign _(e.g.: 'ecdsa' for an Elliptic Curve Signature)_ -- `privateKey`: the private key to sign with - -Example - -```JSON -{ - "method": "ecdsa", - "privateKey": "0x3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266" -} -``` - -### Signature methods supported - -List of the Signature methods supported in this version: - -| Method | Signature Value | SignatureParameter privateKey | Identity Type from recover() | Comment | -| ------------------ | -------------------------------------- | -------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **ecdsa** | hexadecimal string (e.g.: "0xe649f..") | hexadecimal string (e.g.: "0xe649f..") | **EthereumAddress** _(see "Identity types supported" below)_ | -| **ecdsa-ethereum** | hexadecimal string (e.g.: "0xe649f..") | hexadecimal string (e.g.: "0xe649f..") | **EthereumAddress** _(see "Identity types supported" below)_ | Similar to ecdsa but the signature is made with the Ethereum padding `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. Used for signing with the web3 tools (like Metamask). | - -### Identity types supported - -List of the Identity types supported in this version: - -| Type | Identity Value | Signature Method | -| ------------------- | -------------------------------------- | ----------------------------------------------------- | -| **ethereumAddress** | hexadecimal string (e.g.: "0xe649f..") | **ecdsa** _(see "Signature methods supported" above)_ | - -### How to sign a JSON object - -The signature of an JSON object is made on the `keccak256` hash of the object: - -- with its properties deeply alphabetically sorted -- stringified -- lowerCased - -## Note on Version and Backward compatibility - -The versions of the Request logic specifications follow the semver 2.0.1. -Every request is create with one and only one version of the specification. - -By default, an implementation of the specifications will be able to handle only the requests following the specification versions with: - -- The **same `major`** version -- A **`minor`** version **equal or inferior** - -Some exceptions will be done for versions that need to be blacklisted (e.g.: versions with vulnerabilities) - -## Example of a request state actions after actions - -Bob wants to request 0.12 BTC to Alice. - -Bob has his private `Signature parameters` from which it can generate his `Identity`: - -```JSON -{ - "signatureParams": { - "method": "ecdsa", - "privateKey": "0x04674d2e53e0e14653487d7323cc5f0a7959c83067f5654cafe4094bde90fa8a", - }, - "identity": { - "type": "ethereumAddress", - "value": "0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce", - } -} -``` - -Alice has his own `Signature parameters` and `Identity`: - -```JSON -{ - "signatureParams": { - "method": "ecdsa", - "privateKey": "0x0906ff14227cead2b25811514302d57706e7d5013fcc40eca5985b216baeb998", - }, - "identity": { - "type": "ethereumAddress", - "value": "0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6", - } -} -``` - -1. Bob creates a request of 0.1234 ETH to Alice -2. Bob makes a discount of 0.1 ETH -3. Alice accept the request - -We now follow the `state` of the request after each `actions`: - -### 1. Bob creates a request of 0.1234 ETH to Alice - -The action to create the request from Bob to Alice from Bob (signed by Bob): - -```JSON -{ - "data":{ - "name":"create", - "parameters":{ - "currency":"ETH", - "expectedAmount":"123400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030 - }, - "version":"2.0.1" - }, - "signature":{ - "method":"ecdsa", - "value":"0xac9e9e43381d882f3edc506277b8ad74ca3fc0ed2184663b65ccbab921df114807d7e68fd03b668afffee1feb977c9082657f1a05f57c0b1f92e9b46ca22dfc31c" - } -} -``` - -The request state after interpreting the action above: - -```JSON -{ - "currency":"ETH", - "expectedAmount":"123400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030, - "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.1", - "events":[ - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataLength":0, - "isSignedRequest":false - } - } - ], - "state":"created", - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - -### 2. Bob makes a discount of 0.1 ETH - -The action to make a discount (signed by Bob); - -```JSON -{ - "data":{ - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount":"100000000000000000", - "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905" - }, - "version":"2.0.1" - }, - "signature":{ - "method":"ecdsa", - "value":"0x649c5fdd54e781dfb480f9b01cc9ac8cd9d7630e2dd3b9a8443865ee00d5015805e6b3bbce9be35ef124e92afba80db79d913cdc756fe4e911f3413ae6a24b971b" - } -} -``` - -The request state after interpreting the new action with the previous state: - -```JSON -{ - "currency":"ETH", - "expectedAmount":"23400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030, - "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.1", - "events":[ - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataLength":0, - "isSignedRequest":false - } - }, - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount":"100000000000000000", - "extensionsDataLength":0 - } - } - ], - "state":"created", - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` - -### 3. Alice accept the request - -The action to accept the request (signed by Alice): - -```JSON -{ - "data":{ - "name":"accept", - "parameters":{ - "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905" - }, - "version":"2.0.1" - }, - "signature":{ - "method":"ecdsa", - "value":"0xf94380c553c90810deb5625571649759f8591bf923f5773e436fec322d01752d676a6f822dee2c2097f4bb70b16273b4826e6026f9f98a31cfafab8f1bdda2eb1b" - } -} -``` - -The request state after interpreting the new action with the previous state: - -```JSON -{ - "currency":"ETH", - "expectedAmount":"23400000000000000", - "payee":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "payer":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "timestamp":1544426030, - "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.1", - "events":[ - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"create", - "parameters":{ - "expectedAmount":"123400000000000000", - "extensionsDataLength":0, - "isSignedRequest":false - } - }, - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - }, - "name":"reduceExpectedAmount", - "parameters":{ - "deltaAmount":"100000000000000000", - "extensionsDataLength":0 - } - }, - { - "actionSigner":{ - "type":"ethereumAddress", - "value":"0x740fc87Bd3f41d07d23A01DEc90623eBC5fed9D6" - }, - "name":"accept", - "parameters":{ - "extensionsDataLength":0 - } - } - ], - "state":"accepted", - "creator":{ - "type":"ethereumAddress", - "value":"0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce" - } -} -``` diff --git a/packages/request-logic/specs/request-logic-specification-v2.0.2.md b/packages/request-logic/specs/request-logic-specification.md similarity index 94% rename from packages/request-logic/specs/request-logic-specification-v2.0.2.md rename to packages/request-logic/specs/request-logic-specification.md index 975ae207ff..ed938d7b98 100644 --- a/packages/request-logic/specs/request-logic-specification-v2.0.2.md +++ b/packages/request-logic/specs/request-logic-specification.md @@ -1,4 +1,4 @@ -# Request logic Specification v2.0.2 +# Request logic Specification v2.0.3 You can be interested in this document if: @@ -9,9 +9,17 @@ You don't need to read this document if: - You want to develop an app using the request protocol (see the API library instead [here](/packages/request-client.js)) +## Previous version + +| version | link | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| 2.0.0 | see [8382917](https://github.com/RequestNetwork/requestNetwork/tree/83829178faf717f23f9554fd7266e7cc787e7106/packages/request-logic/specs) | +| 2.0.1 | see [8382917](https://github.com/RequestNetwork/requestNetwork/tree/83829178faf717f23f9554fd7266e7cc787e7106/packages/request-logic/specs) | +| 2.0.2 | see [8382917](https://github.com/RequestNetwork/requestNetwork/tree/83829178faf717f23f9554fd7266e7cc787e7106/packages/request-logic/specs) | + ## Content table -- [Request logic Specification v2.0.2](#request-logic-specification-v200) +- [Request logic Specification v2.0.3](#request-logic-specification-v200) - [Content table](#content-table) - [Request](#request) - [Properties](#properties) @@ -78,9 +86,9 @@ A request is the JSON object which`properties` returned from a list of `actions` | **expectedAmount** | Amount | Amount expected to be paid | | **currency** | Currency | Currency of the expected amount | | **state** | Enum('created', 'accepted', 'canceled') | State of the request | -| **events** | Array | List of the actions performed | +| **events** | Array | History of the actions performed on the request | | **extensionsData** | Array | List of data used by the above layer | -| **version** | String | Specification version by the request _(2.0.2' here)_ | +| **version** | String | Specification version by the request _(2.0.3' here)_ | |  **timestamp** | Number | - Timestamp of the request creation in seconds
    - this timestamp is given by the creator. It is not trustless.
    - This timestamp is also used to differentiate between identical requests | |  **nonce** | Number | Number to differentiate several identical requests with the same timestamp | @@ -129,7 +137,7 @@ Example "requestId":"011c2610cbc5bee43b6bc9800e69ec832fb7d50ea098a88877a0afdcac5981d3f8", "state":"created", "timestamp": 1545224094, - "version":"2.0.2" + "version":"2.0.3" } ``` @@ -234,7 +242,7 @@ Example of creation action: }, "timestamp": 1545224094, }, - "version":"2.0.2", + "version":"2.0.3", "signature":{ "method":"ecdsa", "value":"0x143f0965cb8628c93e6f59f39a7c86163a7de01df42c923e65e109bab336710d7b534615025ed0c285e8dcbba2f4e136afa497af792a63519c486b16f3ccabb41c" @@ -732,17 +740,17 @@ To manage low level identity in the request logic we use three different types o - `recover()`: get the `Identity` from data and its `Signature` ``` - +----------------+ - +---------------------+ | Signed data | - |/SignatureParameters/| | | - +-----------+---------+ | +--------+ | - | | | Data | | -+--------+ Sign() | | +--------+ | Recover() +----------+ -| Data +------------------------------->+ +------------------->+/Identity/| -+--------+ | +-----------+ | +----------+ - | |/Signature/| | - | +-----------+ | - +----------------+ + +----------------+ + | Signed data | + | | + | +--------+ | + | | Data | | ++--------+ Sign() | +--------+ | Recover() +----------+ +| Data +------------->+ +---------------->+/Identity/| ++--------+ | +-----------+ | +----------+ + | |/Signature/| | + | +-----------+ | + +----------------+ ``` ### Identity @@ -791,22 +799,6 @@ Example } ``` -### SignatureParameters - -this is a json object with two properties: - -- `method`: the method to sign _(e.g.: 'ecdsa' for an Elliptic Curve Signature)_ -- `privateKey`: the private key to sign with - -Example - -```JSON -{ - "method": "ecdsa", - "privateKey": "0x3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266" -} -``` - ### Signature methods supported List of the Signature methods supported in this version: @@ -820,9 +812,10 @@ List of the Signature methods supported in this version: List of the Identity types supported in this version: -| Type | Identity Value | Signature Method | -| ------------------- | -------------------------------------- | ----------------------------------------------------- | -| **ethereumAddress** | hexadecimal string (e.g.: "0xe649f..") | **ecdsa** _(see "Signature methods supported" above)_ | +| Type | Identity Value | extra value | Signature Method | +| ------------------------- | -------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------- | +| **ethereumAddress** | hexadecimal string (e.g.: "0xe649f..") | _none_ | **ecdsa** _(see "Signature methods supported" above)_ | +| **ethereumSmartContract** | hexadecimal string (e.g.: "0xe649f..") | optional: network ('mainnet', 'rinkeby', 'private'... ) 'mainnet' if not given |  *none* | ### How to sign a JSON object @@ -834,7 +827,7 @@ The signature of an JSON object is made on the `keccak256` hash of the object: ## Note on Version and Backward compatibility -The versions of the Request logic specifications follow the semver 2.0.2. +The versions of the Request logic specifications follow the semver 2.0.3. Every request is create with one and only one version of the specification. By default, an implementation of the specifications will be able to handle only the requests following the specification versions with: @@ -909,7 +902,7 @@ The action to create the request from Bob to Alice from Bob (signed by Bob): }, "timestamp":1544426030 }, - "version":"2.0.2" + "version":"2.0.3" }, "signature":{ "method":"ecdsa", @@ -938,7 +931,7 @@ The request state after interpreting the action above: }, "timestamp":1544426030, "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.2", + "version":"2.0.3", "events":[ { "actionSigner":{ @@ -973,7 +966,7 @@ The action to make a discount (signed by Bob); "deltaAmount":"100000000000000000", "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905" }, - "version":"2.0.2" + "version":"2.0.3" }, "signature":{ "method":"ecdsa", @@ -1002,7 +995,7 @@ The request state after interpreting the new action with the previous state: }, "timestamp":1544426030, "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.2", + "version":"2.0.3", "events":[ { "actionSigner":{ @@ -1047,7 +1040,7 @@ The action to accept the request (signed by Alice): "parameters":{ "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905" }, - "version":"2.0.2" + "version":"2.0.3" }, "signature":{ "method":"ecdsa", @@ -1076,7 +1069,7 @@ The request state after interpreting the new action with the previous state: }, "timestamp":1544426030, "requestId":"01d251224337a268cc4c6d73e02f883827a35789f6da15050655435348452d8905", - "version":"2.0.2", + "version":"2.0.3", "events":[ { "actionSigner":{ diff --git a/packages/request-logic/specs/signature-provider.md b/packages/request-logic/specs/signature-provider.md index 19c699acf6..8d2b40558e 100644 --- a/packages/request-logic/specs/signature-provider.md +++ b/packages/request-logic/specs/signature-provider.md @@ -1,4 +1,4 @@ -# Signature Provider +# Signature Provider This document specifies what a Signature Provider is, how to use it and how to implement it in the Request Network context. @@ -8,7 +8,7 @@ It uses the Request Network Protocol concepts of `Identity` and `Signature` desc ## Description -The signature provider is an object in charge of signing data. It's used to sign actions in the Request Network Protocol. +The signature provider is an object in charge of signing data. It's used to sign actions in the Request Network Protocol. ## Functions and Properties @@ -16,8 +16,9 @@ The signature provider is an object in charge of signing data. It's used to sign This read-only property lists in an array all the signature methods the signature provider can use. -Possible values: -- `'ecdsa'`: Uses Elliptic Curve Digital Signature Algorithm to sign the data +Possible values: + +- `'ecdsa'`: Uses Elliptic Curve Digital Signature Algorithm to sign the data - `'ecdsa-ethereum'`: Uses Elliptic Curve Digital Signature Algorithm, with [Ethereum padding](https://github.com/ethereum/go-ethereum/pull/2940), to sign the data (allowing data signature using Metamask) ### `supportedIdentityTypes` @@ -37,9 +38,9 @@ This function is used to sign data with the methods listed in `supportedMethods` Returns: A `SignedData` object containing the following properties: -| Property | Type | Description | -| ------------- | --------- | ------------------------------------------------------------ | -| **data** | any | The original data. | +| Property | Type | Description | +| ------------- | --------- | ------------------------------------------------------------------------ | +| **data** | any | The original data. | | **signature** | Signature | The Signature object, containing the signature **method** and **value**. | Throws: The function must `throw` if: diff --git a/packages/request-logic/src/actions/create.ts b/packages/request-logic/src/actions/create.ts index 13e6c53be4..bd0b60c80e 100644 --- a/packages/request-logic/src/actions/create.ts +++ b/packages/request-logic/src/actions/create.ts @@ -36,18 +36,12 @@ function format( throw new Error('expectedAmount must be a positive integer'); } - if ( - requestParameters.payee && - requestParameters.payee.type !== IdentityTypes.TYPE.ETHEREUM_ADDRESS - ) { - throw new Error('payee.type not supported'); + if (requestParameters.payee && Utils.identity.hasError(requestParameters.payee)) { + throw new Error(`payee: ${Utils.identity.hasError(requestParameters.payee)}̀`); } - if ( - requestParameters.payer && - requestParameters.payer.type !== IdentityTypes.TYPE.ETHEREUM_ADDRESS - ) { - throw new Error('payer.type not supported'); + if (requestParameters.payer && Utils.identity.hasError(requestParameters.payer)) { + throw new Error(`payer: ${Utils.identity.hasError(requestParameters.payer)}̀`); } if (!requestParameters.timestamp) { @@ -91,6 +85,14 @@ function createRequest( throw new Error('action.parameters.payee or action.parameters.payer must be given'); } + if (action.data.parameters.payee && Utils.identity.hasError(action.data.parameters.payee)) { + throw new Error(`payee: ${Utils.identity.hasError(action.data.parameters.payee)}̀`); + } + + if (action.data.parameters.payer && Utils.identity.hasError(action.data.parameters.payer)) { + throw new Error(`payer: ${Utils.identity.hasError(action.data.parameters.payer)}̀`); + } + if ( !Utils.isString(action.data.parameters.expectedAmount) || !Utils.amount.isValid(action.data.parameters.expectedAmount) @@ -100,11 +102,6 @@ function createRequest( ); } - // If we're creating an older version of a request, we convert the string currency type to the new ICurrency one - if (Semver.lt(action.data.version, '2.0.2')) { - action.data.parameters.currency = legacyEnumToICurrencyConvert(action.data.parameters.currency); - } - const signer: IdentityTypes.IIdentity = Action.getSignerIdentityFromAction(action); // Copy to not modify the action itself @@ -114,6 +111,11 @@ function createRequest( request.version = Action.getVersionFromAction(action); request.events = [generateEvent(action, timestamp, signer)]; + // If we're creating an older version of a request, we convert the string currency type to the new ICurrency one + if (Semver.lt(action.data.version, '2.0.2')) { + request.currency = legacyEnumToICurrencyConvert(action.data.parameters.currency); + } + const signerRole = Action.getRoleInAction(signer, action); if (signerRole === RequestLogicTypes.ROLE.PAYEE) { request.state = RequestLogicTypes.STATE.CREATED; @@ -187,7 +189,7 @@ function legacyEnumToICurrencyConvert(currency: string): RequestLogicTypes.ICurr type: RequestLogicTypes.CURRENCY.ISO4217, value: 'USD', }; - case 'SAI': + case 'DAI': // DON'T RENAME: DAI was the name used by the legacy requests for SAI return { type: RequestLogicTypes.CURRENCY.ERC20, value: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359', // SAI diff --git a/packages/request-logic/src/config.ts b/packages/request-logic/src/config.ts index 75b3c8e589..48848461e1 100644 --- a/packages/request-logic/src/config.ts +++ b/packages/request-logic/src/config.ts @@ -1,6 +1,6 @@ export default { specificationVersion: { - current: '2.0.2', + current: '2.0.3', exceptions: [], }, }; diff --git a/packages/request-logic/src/request-logic.ts b/packages/request-logic/src/request-logic.ts index fe3c8d2e8a..54fbbaf85b 100644 --- a/packages/request-logic/src/request-logic.ts +++ b/packages/request-logic/src/request-logic.ts @@ -1,3 +1,5 @@ +import { EventEmitter } from 'events'; + import MultiFormat from '@requestnetwork/multi-format'; import { AdvancedLogicTypes, @@ -57,10 +59,24 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { hashedTopics, ); - return { + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, result: { requestId }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + result: { requestId }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -100,10 +116,25 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { hashedTopics, encryptionParams, ); - return { + + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, result: { requestId }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + result: { requestId }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -147,7 +178,7 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestParameters: RequestLogicTypes.IAcceptParameters, signerIdentity: IdentityTypes.IIdentity, validate: boolean = false, - ): Promise { + ): Promise { if (!this.signatureProvider) { throw new Error('You must give a signature provider to create actions'); } @@ -166,9 +197,22 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestId, ); - return { + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -184,7 +228,7 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestParameters: RequestLogicTypes.ICancelParameters, signerIdentity: IdentityTypes.IIdentity, validate: boolean = false, - ): Promise { + ): Promise { if (!this.signatureProvider) { throw new Error('You must give a signature provider to create actions'); } @@ -202,9 +246,23 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { JSON.stringify(action), requestId, ); - return { + + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -220,7 +278,7 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestParameters: RequestLogicTypes.IIncreaseExpectedAmountParameters, signerIdentity: IdentityTypes.IIdentity, validate: boolean = false, - ): Promise { + ): Promise { if (!this.signatureProvider) { throw new Error('You must give a signature provider to create actions'); } @@ -238,9 +296,23 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { JSON.stringify(action), requestId, ); - return { + + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -256,7 +328,7 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestParameters: RequestLogicTypes.IReduceExpectedAmountParameters, signerIdentity: IdentityTypes.IIdentity, validate: boolean = false, - ): Promise { + ): Promise { if (!this.signatureProvider) { throw new Error('You must give a signature provider to create actions'); } @@ -274,9 +346,23 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { JSON.stringify(action), requestId, ); - return { + + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -292,7 +378,7 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestParameters: RequestLogicTypes.IAddExtensionsDataParameters, signerIdentity: IdentityTypes.IIdentity, validate: boolean = false, - ): Promise { + ): Promise { if (!this.signatureProvider) { throw new Error('You must give a signature provider to create actions'); } @@ -311,9 +397,23 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { JSON.stringify(action), requestId, ); - return { + + const result = Object.assign(new EventEmitter(), { meta: { transactionManagerMeta: resultPersistTx.meta }, - }; + }); + + // When receive the confirmation from transaction manager propagate it + resultPersistTx + .on('confirmed', (resultPersistTxConfirmed: TransactionTypes.IReturnPersistTransaction) => { + result.emit('confirmed', { + meta: { transactionManagerMeta: resultPersistTxConfirmed.meta }, + }); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** @@ -326,51 +426,24 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { public async getRequestFromId( requestId: string, ): Promise { - const resultGetTx = await this.transactionManager.getTransactionsByChannelId(requestId); - const actions = resultGetTx.result.transactions - // filter the actions ignored by the previous layers - .filter(action => action !== null); - let ignoredTransactions: any[] = []; - - // array of transaction without duplicates to avoid replay attack - const actionsConfirmedWithoutDuplicates = Utils.uniqueByProperty( - actions - .map((t: any) => { - try { - return { action: JSON.parse(t.transaction.data), timestamp: t.timestamp }; - } catch (e) { - // We ignore the transaction.data that cannot be parsed - ignoredTransactions.push({ - reason: 'JSON parsing error', - transaction: t, - }); - return; - } - }) - .filter((elem: any) => elem !== undefined), - 'action', - ); - // Keeps the transaction ignored - ignoredTransactions = ignoredTransactions.concat( - actionsConfirmedWithoutDuplicates.duplicates.map(tx => { - return { - reason: 'Duplicated transaction', - transaction: tx, - }; - }), - ); - - const { request, ignoredTransactionsByApplication } = await this.computeRequestFromTransactions( - actionsConfirmedWithoutDuplicates.uniqueItems, + const { + ignoredTransactions, + confirmedRequestState, + pendingRequestState, + transactionManagerMeta, + } = await this.computeRequestFromRequestId(requestId); + + const pending = this.computeDiffBetweenPendingAndConfirmedRequestState( + confirmedRequestState, + pendingRequestState, ); - ignoredTransactions = ignoredTransactions.concat(ignoredTransactionsByApplication); return { meta: { ignoredTransactions, - transactionManagerMeta: resultGetTx.meta, + transactionManagerMeta, }, - result: { request }, + result: { request: confirmedRequestState, pending }, }; } @@ -456,6 +529,77 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { }; } + /** + * Interprets a request from requestId + * + * @param requestId the requestId of the request to compute + * @returns the request, the pending state of the request and the ignored transactions + */ + private async computeRequestFromRequestId( + requestId: RequestLogicTypes.RequestId, + ): Promise<{ + confirmedRequestState: RequestLogicTypes.IRequest | null; + pendingRequestState: RequestLogicTypes.IRequest | null; + ignoredTransactions: any[]; + transactionManagerMeta: any; + }> { + const resultGetTx = await this.transactionManager.getTransactionsByChannelId(requestId); + const actions = resultGetTx.result.transactions + // filter the actions ignored by the previous layers + .filter(action => action !== null) + .sort((a: any, b: any) => a.timestamp - b.timestamp); + + // tslint:disable-next-line:prefer-const + let { ignoredTransactions, keptTransactions } = this.removeOldPendingTransactions(actions); + + // array of transaction without duplicates to avoid replay attack + const timestampedActionsWithoutDuplicates = Utils.uniqueByProperty( + keptTransactions + .map((t: any) => { + try { + return { + action: JSON.parse(t.transaction.data), + state: t.state, + timestamp: t.timestamp, + }; + } catch (e) { + // We ignore the transaction.data that cannot be parsed + ignoredTransactions.push({ + reason: 'JSON parsing error', + transaction: t, + }); + return; + } + }) + .filter((elem: any) => elem !== undefined), + 'action', + ); + + // Keeps the transaction ignored + ignoredTransactions = ignoredTransactions.concat( + timestampedActionsWithoutDuplicates.duplicates.map(tx => { + return { + reason: 'Duplicated transaction', + transaction: tx, + }; + }), + ); + + const { + confirmedRequestState, + pendingRequestState, + ignoredTransactionsByApplication, + } = await this.computeRequestFromTransactions(timestampedActionsWithoutDuplicates.uniqueItems); + ignoredTransactions = ignoredTransactions.concat(ignoredTransactionsByApplication); + + return { + confirmedRequestState, + ignoredTransactions, + pendingRequestState, + transactionManagerMeta: resultGetTx.meta, + }; + } + /** * Interprets a request from transactions * @@ -463,30 +607,59 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { * @returns the request and the ignoredTransactions */ private async computeRequestFromTransactions( - transactions: TransactionTypes.IConfirmedTransaction[], + transactions: TransactionTypes.ITimestampedTransaction[], ): Promise<{ - request: RequestLogicTypes.IRequest | null; + confirmedRequestState: RequestLogicTypes.IRequest | null; + pendingRequestState: RequestLogicTypes.IRequest | null; ignoredTransactionsByApplication: any[]; }> { const ignoredTransactionsByApplication: any[] = []; - // second parameter is null, because the first action must be a creation (no state expected) - const request = transactions.reduce((requestState: any, actionConfirmed: any) => { - try { - return RequestLogicCore.applyActionToRequest( - requestState, - actionConfirmed.action, - actionConfirmed.timestamp, - this.advancedLogic, - ); - } catch (e) { - // if an error occurs while applying we ignore the action - ignoredTransactionsByApplication.push({ reason: e.message, transaction: actionConfirmed }); - return requestState; - } - }, null); + const confirmedRequestState = transactions + .filter(action => action.state === TransactionTypes.TransactionState.CONFIRMED) + .reduce((requestState: any, actionConfirmed: any) => { + try { + return RequestLogicCore.applyActionToRequest( + requestState, + actionConfirmed.action, + actionConfirmed.timestamp, + this.advancedLogic, + ); + } catch (e) { + // if an error occurs while applying we ignore the action + ignoredTransactionsByApplication.push({ + reason: e.message, + transaction: actionConfirmed, + }); + return requestState; + } + }, null); + + const pendingRequestState = transactions + .filter(action => action.state === TransactionTypes.TransactionState.PENDING) + .reduce((requestState: any, actionConfirmed: any) => { + try { + return RequestLogicCore.applyActionToRequest( + requestState, + actionConfirmed.action, + actionConfirmed.timestamp, + this.advancedLogic, + ); + } catch (e) { + // if an error occurs while applying we ignore the action + ignoredTransactionsByApplication.push({ + reason: e.message, + transaction: actionConfirmed, + }); + return requestState; + } + }, confirmedRequestState); - return { ignoredTransactionsByApplication, request }; + return { + confirmedRequestState, + ignoredTransactionsByApplication, + pendingRequestState, + }; } /** @@ -505,15 +678,22 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { const allRequestAndMetaPromises = Object.keys(channelsRawData.result.transactions).map( // Parses and removes corrupted or duplicated transactions async channelId => { - let ignoredTransactions: any[] = []; + // tslint:disable-next-line:prefer-const + let { ignoredTransactions, keptTransactions } = this.removeOldPendingTransactions( + transactionsByChannel[channelId], + ); - const actionsConfirmedWithoutDuplicates = Utils.uniqueByProperty( - transactionsByChannel[channelId] + const timestampedActionsWithoutDuplicates = Utils.uniqueByProperty( + keptTransactions // filter the actions ignored by the previous layers .filter(action => action !== null) .map((t: any) => { try { - return { action: JSON.parse(t.transaction.data), timestamp: t.timestamp }; + return { + action: JSON.parse(t.transaction.data), + state: t.state, + timestamp: t.timestamp, + }; } catch (e) { // We ignore the transaction.data that cannot be parsed ignoredTransactions.push({ @@ -529,7 +709,7 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { // Keeps the ignored transactions ignoredTransactions = ignoredTransactions.concat( - actionsConfirmedWithoutDuplicates.duplicates.map(tx => ({ + timestampedActionsWithoutDuplicates.duplicates.map(tx => ({ reason: 'Duplicated transaction', transaction: tx, })), @@ -537,16 +717,23 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { // Computes the request from the transactions const { - request, + confirmedRequestState, + pendingRequestState, ignoredTransactionsByApplication, } = await this.computeRequestFromTransactions( - actionsConfirmedWithoutDuplicates.uniqueItems, + timestampedActionsWithoutDuplicates.uniqueItems, ); ignoredTransactions = ignoredTransactions.concat(ignoredTransactionsByApplication); + const pending = this.computeDiffBetweenPendingAndConfirmedRequestState( + confirmedRequestState, + pendingRequestState, + ); + return { ignoredTransactions, - request, + pending, + request: confirmedRequestState, transactionManagerMeta: transactionManagerMeta[channelId], }; }, @@ -557,8 +744,11 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { // Merge all the requests and meta in one object return allRequestAndMeta.reduce( (finalResult: RequestLogicTypes.IReturnGetRequestsByTopic, requestAndMeta: any) => { - if (requestAndMeta.request) { - finalResult.result.requests.push(requestAndMeta.request); + if (requestAndMeta.request || requestAndMeta.pending) { + finalResult.result.requests.push({ + pending: requestAndMeta.pending, + request: requestAndMeta.request, + }); // workaround to quiet the error "finalResult.meta.ignoredTransactions can be undefined" (but defined in the initialization value of the accumulator) (finalResult.meta.ignoredTransactions || []).push(requestAndMeta.ignoredTransactions); @@ -593,12 +783,115 @@ export default class RequestLogic implements RequestLogicTypes.IRequestLogic { requestId: RequestLogicTypes.RequestId, action: RequestLogicTypes.IAction, ): Promise { - const request = await this.getRequestFromId(requestId); - RequestLogicCore.applyActionToRequest( - request.result.request, - action, - Date.now(), - this.advancedLogic, + const { confirmedRequestState, pendingRequestState } = await this.computeRequestFromRequestId( + requestId, ); + + try { + // Check if the action doesn't fail with the request state + RequestLogicCore.applyActionToRequest( + confirmedRequestState, + action, + Date.now(), + this.advancedLogic, + ); + } catch (error) { + // Check if the action works with the pending state + if (pendingRequestState) { + RequestLogicCore.applyActionToRequest( + pendingRequestState, + action, + Date.now(), + this.advancedLogic, + ); + } + } + } + + /** + * Computes the diff between the confirmed and pending request + * + * @param confirmedRequestState the confirmed request state + * @param pendingRequestState the pending request state + * @returns an object with the pending state attributes that are different from the confirmed one + */ + private computeDiffBetweenPendingAndConfirmedRequestState( + confirmedRequestState: any, + pendingRequestState: any, + ): RequestLogicTypes.IPendingRequest | null { + // Compute the diff between the confirmed and pending request + let pending: any = null; + if (!confirmedRequestState) { + pending = pendingRequestState; + } else if (pendingRequestState) { + for (const key in pendingRequestState) { + if (pendingRequestState.hasOwnProperty(key)) { + // TODO: Should find a better way to do that + if ( + Utils.crypto.normalizeKeccak256Hash(pendingRequestState[key]).value !== + Utils.crypto.normalizeKeccak256Hash(confirmedRequestState[key]).value + ) { + if (!pending) { + pending = {}; + } + // tslint:disable-next-line:prefer-conditional-expression + if (key === 'events') { + // keep only the new events in pending + pending[key] = pendingRequestState[key].slice(confirmedRequestState[key].length); + } else { + pending[key] = pendingRequestState[key]; + } + } + } + } + } + return pending as RequestLogicTypes.IPendingRequest | null; + } + + /** + * Sorts out the transactions pending older than confirmed ones + * + * @param actions list of the actions + * @returns an object with the ignoredTransactions and the kept actions + */ + private removeOldPendingTransactions( + transactions: Array, + ): { + ignoredTransactions: any[]; + keptTransactions: Array; + } { + const ignoredTransactions: any[] = []; + + // ignored the transactions pending older than confirmed ones + let confirmedFound = false; + const keptTransactions = transactions + .reverse() + .filter(action => { + if (!action) { + return false; + } + + // Have we already found confirmed transactions + confirmedFound = + confirmedFound || action.state === TransactionTypes.TransactionState.CONFIRMED; + + // keep the transaction if confirmed or pending but no confirmed found before + if ( + action.state === TransactionTypes.TransactionState.CONFIRMED || + (action.state === TransactionTypes.TransactionState.PENDING && !confirmedFound) + ) { + return true; + } else { + // Keeps the ignored transactions + ignoredTransactions.push({ + reason: 'Confirmed transaction newer than this pending transaction', + transaction: action, + }); + return false; + } + }) + .reverse(); + + return { ignoredTransactions, keptTransactions }; } } diff --git a/packages/request-logic/test/index.test.ts b/packages/request-logic/test/index.test.ts index cd6e7baa20..144216ad6d 100644 --- a/packages/request-logic/test/index.test.ts +++ b/packages/request-logic/test/index.test.ts @@ -1,9 +1,12 @@ import 'mocha'; +import * as sinon from 'sinon'; + +import { EventEmitter } from 'events'; + import MultiFormat from '@requestnetwork/multi-format'; import { AdvancedLogicTypes, RequestLogicTypes, TransactionTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; -import * as chaiAsPromised from 'chai-as-promised'; import { RequestLogic } from '../src/index'; import * as TestData from './unit/utils/test-data-generator'; @@ -12,12 +15,13 @@ import Version from '../src/version'; const CURRENT_VERSION = Version.currentVersion; -const chai = require('chai'); -const spies = require('chai-spies'); -const expect = chai.expect; +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-spies'; -chai.use(spies); chai.use(chaiAsPromised); +chai.use(spies); +const expect = chai.expect; const createParams: RequestLogicTypes.ICreateParameters = { currency: { @@ -49,10 +53,30 @@ let fakeTransactionManager: TransactionTypes.ITransactionManager; describe('index', () => { beforeEach(() => { fakeTransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), - getTransactionsByChannelId: chai.spy(), - persistTransaction: chai.spy.returns(fakeMetaTransactionManager), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, + getTransactionsByChannelId: chai.spy() as any, + persistTransaction: chai.spy(() => { + const fakeMetaTransactionManagerWithEvent = Object.assign( + new EventEmitter(), + fakeMetaTransactionManager, + ); + setTimeout(() => { + fakeMetaTransactionManagerWithEvent.emit( + 'confirmed', + { + meta: { storageDataId: 'fakeDataId' }, + result: { topics: [fakeTxHash] }, + }, + // tslint:disable-next-line:no-magic-numbers + 100, + ); + }); + + return fakeMetaTransactionManagerWithEvent; + }) as any, + + // chai.spy.returns(fakeMetaTransactionManager) as any, }; }); @@ -60,14 +84,9 @@ describe('index', () => { it('cannot createRequest without signature provider', async () => { const requestLogic = new RequestLogic(fakeTransactionManager); - try { - await requestLogic.createRequest(createParams, TestData.payeeRaw.identity); - expect(false, 'must have thrown').to.be.true; - } catch (e) { - expect(e.message, 'wrong exception').to.equal( - 'You must give a signature provider to create actions', - ); - } + await expect( + requestLogic.createRequest(createParams, TestData.payeeRaw.identity), + ).to.eventually.be.rejectedWith('You must give a signature provider to create actions'); }); it('cannot createRequest if apply fails in the advanced request logic', async () => { @@ -105,6 +124,19 @@ describe('index', () => { const requestLogic = new RequestLogic(fakeTransactionManager, TestData.fakeSignatureProvider); const ret = await requestLogic.createRequest(createParams, TestData.payeeRaw.identity); + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + result: { + requestId: '010246b8aeb3aa72f4c7039284bf7307c3d543541ff309ee52e9361f4bd2c89c9c', + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.deep.equal({ requestId }); expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -137,6 +169,51 @@ describe('index', () => { ], ); }); + + it('can createRequest if persist emit error', async () => { + const clock = sinon.useFakeTimers(); + const fakeTransactionManagerEmittingError = Object.assign({}, fakeTransactionManager); + fakeTransactionManagerEmittingError.persistTransaction = chai.spy( + (): any => { + const fakeTransactionManagerWithEvent = Object.assign( + new EventEmitter(), + fakeMetaTransactionManager, + ); + setTimeout(() => { + // tslint:disable-next-line:no-magic-numbers + fakeTransactionManagerWithEvent.emit('error', 'error for test purpose', 10); + }); + return fakeTransactionManagerWithEvent; + }, + ); + + const requestLogic = new RequestLogic( + fakeTransactionManagerEmittingError, + TestData.fakeSignatureProvider, + ); + const ret = await requestLogic.createRequest(createParams, TestData.payeeRaw.identity); + + // tslint:disable-next-line:typedef + const handleError = chai.spy((error: any) => { + expect(error, 'error wrong').to.deep.equal('error for test purpose'); + }); + ret.on('error', handleError); + + clock.tick(11); + + expect(handleError, 'error must be emitted').to.have.called(); + + expect(ret.result, 'ret.result is wrong').to.be.deep.equal({ requestId }); + expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ + transactionManagerMeta: fakeMetaTransactionManager.meta, + }); + + expect(fakeTransactionManagerEmittingError.persistTransaction).to.have.been.called.with( + JSON.stringify(action), + requestId, + ); + sinon.restore(); + }); }); describe('createEncryptedRequest', () => { @@ -193,6 +270,20 @@ describe('index', () => { TestData.payeeRaw.identity, [TestData.payeeRaw.encryptionParams, TestData.payerRaw.encryptionParams], ); + + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + result: { + requestId: '010246b8aeb3aa72f4c7039284bf7307c3d543541ff309ee52e9361f4bd2c89c9c', + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.deep.equal({ requestId }); expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -238,6 +329,55 @@ describe('index', () => { 'You must give at least one encryption parameter to create an encrypted request', ); }); + + it('can createEncryptedRequest if persist emit error', async () => { + const clock = sinon.useFakeTimers(); + const fakeTransactionManagerEmittingError = Object.assign({}, fakeTransactionManager); + fakeTransactionManagerEmittingError.persistTransaction = chai.spy( + (): any => { + const fakeTransactionManagerWithEvent = Object.assign( + new EventEmitter(), + fakeMetaTransactionManager, + ); + setTimeout(() => { + // tslint:disable-next-line:no-magic-numbers + fakeTransactionManagerWithEvent.emit('error', 'error for test purpose', 10); + }); + return fakeTransactionManagerWithEvent; + }, + ); + + const requestLogic = new RequestLogic( + fakeTransactionManagerEmittingError, + TestData.fakeSignatureProvider, + ); + const ret = await requestLogic.createEncryptedRequest( + createParams, + TestData.payeeRaw.identity, + [TestData.payeeRaw.encryptionParams, TestData.payerRaw.encryptionParams], + ); + + // tslint:disable-next-line:typedef + const handleError = chai.spy((error: any) => { + expect(error, 'error wrong').to.deep.equal('error for test purpose'); + }); + ret.on('error', handleError); + + clock.tick(11); + + expect(handleError, 'error must be emitted').to.have.called(); + + expect(ret.result, 'ret.result is wrong').to.be.deep.equal({ requestId }); + expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ + transactionManagerMeta: fakeMetaTransactionManager.meta, + }); + + expect(fakeTransactionManagerEmittingError.persistTransaction).to.have.been.called.with( + JSON.stringify(action), + requestId, + ); + sinon.restore(); + }); }); describe('computeRequestId', () => { @@ -301,6 +441,16 @@ describe('index', () => { const requestLogic = new RequestLogic(fakeTransactionManager, TestData.fakeSignatureProvider); const ret = await requestLogic.acceptRequest(acceptParams, TestData.payerRaw.identity); + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.undefined; expect(ret.meta).to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -348,14 +498,15 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); const transactionManager: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: chai.spy.returns( Promise.resolve({ meta: { ignoredTransactions: [] }, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, @@ -363,7 +514,7 @@ describe('index', () => { }, }), ), - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const acceptParams = { requestId, @@ -383,6 +534,17 @@ describe('index', () => { }; const requestLogic = new RequestLogic(fakeTransactionManager, TestData.fakeSignatureProvider); const ret = await requestLogic.cancelRequest(cancelRequest, TestData.payeeRaw.identity); + + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.undefined; expect(ret.meta).to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -430,14 +592,15 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); const transactionManager: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: chai.spy.returns( Promise.resolve({ meta: { ignoredTransactions: [] }, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, @@ -445,7 +608,7 @@ describe('index', () => { }, }), ), - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const cancelParams = { requestId, @@ -470,6 +633,17 @@ describe('index', () => { increaseRequest, TestData.payerRaw.identity, ); + + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.undefined; expect(ret.meta).to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -517,14 +691,15 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); const transactionManager: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: chai.spy.returns( Promise.resolve({ meta: { ignoredTransactions: [] }, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, @@ -532,7 +707,7 @@ describe('index', () => { }, }), ), - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const increaseRequest = { deltaAmount: '1000', @@ -562,6 +737,17 @@ describe('index', () => { reduceRequest, TestData.payeeRaw.identity, ); + + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.undefined; expect(ret.meta).to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -609,14 +795,15 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); const transactionManager: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: chai.spy.returns( Promise.resolve({ meta: { ignoredTransactions: [] }, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, @@ -624,7 +811,7 @@ describe('index', () => { }, }), ), - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const increaseRequest = { deltaAmount: '1000', @@ -650,6 +837,17 @@ describe('index', () => { addExtRequest, TestData.payeeRaw.identity, ); + + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + transactionManagerMeta: { + storageDataId: 'fakeDataId', + }, + }, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.undefined; expect(ret.meta).to.be.deep.equal({ transactionManagerMeta: fakeMetaTransactionManager.meta, @@ -704,14 +902,15 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); const transactionManager: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: chai.spy.returns( Promise.resolve({ meta: { ignoredTransactions: [] }, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, @@ -719,7 +918,7 @@ describe('index', () => { }, }), ), - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const addExtensionParams = { extensionsData: ['whatever'], @@ -786,14 +985,17 @@ describe('index', () => { result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: JSON.stringify(actionAccept) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 3, transaction: { data: JSON.stringify(rxReduce) }, }, @@ -802,11 +1004,11 @@ describe('index', () => { }); const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: (): Promise => listActions, - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const requestLogic = new RequestLogic( fakeTransactionManagerGet, @@ -821,6 +1023,7 @@ describe('index', () => { transactionManagerMeta: meta, }, result: { + pending: null, request: { creator: TestData.payeeRaw.identity, currency: { @@ -869,7 +1072,7 @@ describe('index', () => { }); }); - it('can getRequestFromId ignore the same transactions even with different case', async () => { + it('can getRequestFromId ignore old pending transaction', async () => { const actionCreate: RequestLogicTypes.IAction = Utils.signature.sign( { name: RequestLogicTypes.ACTION_NAME.CREATE, @@ -899,7 +1102,7 @@ describe('index', () => { TestData.payerRaw.signatureParams, ); - const actionReduce: RequestLogicTypes.IAction = Utils.signature.sign( + const rxReduce: RequestLogicTypes.IAction = Utils.signature.sign( { name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, parameters: { @@ -911,49 +1114,36 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); - const actionReduce2: RequestLogicTypes.IAction = Utils.signature.sign( - { - name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, - parameters: { - deltaAmount: '1000', - requestId: requestId.toUpperCase(), - }, - version: CURRENT_VERSION, - }, - TestData.payeeRaw.signatureParams, - ); - const meta = { ignoredTransactions: [] }; const listActions: Promise = Promise.resolve({ meta, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, { + state: TransactionTypes.TransactionState.PENDING, timestamp: 2, transaction: { data: JSON.stringify(actionAccept) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 3, - transaction: { data: JSON.stringify(actionReduce) }, - }, - { - timestamp: 4, - transaction: { data: JSON.stringify(actionReduce2) }, + transaction: { data: JSON.stringify(rxReduce) }, }, ], }, }); const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: (): Promise => listActions, - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const requestLogic = new RequestLogic( fakeTransactionManagerGet, @@ -966,13 +1156,21 @@ describe('index', () => { meta: { ignoredTransactions: [ { - reason: 'Duplicated transaction', - transaction: { action: actionReduce2, timestamp: 4 }, + reason: 'Confirmed transaction newer than this pending transaction', + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { + data: + '{"data":{"name":"accept","parameters":{"requestId":"010246b8aeb3aa72f4c7039284bf7307c3d543541ff309ee52e9361f4bd2c89c9c"},"version":"2.0.3"},"signature":{"method":"ecdsa","value":"0xe53448080b32927c66827f3d946e988f18cfa4dfa640e15563eb4c266ab65e3932df94fdab3e3625da4f41b8ce8ef56c3ae39d89189859c3d3090ca4503247141b"}}', + }, + }, }, ], transactionManagerMeta: meta, }, result: { + pending: null, request: { creator: TestData.payeeRaw.identity, currency: { @@ -990,14 +1188,6 @@ describe('index', () => { }, timestamp: 1, }, - { - actionSigner: TestData.payerRaw.identity, - name: RequestLogicTypes.ACTION_NAME.ACCEPT, - parameters: { - extensionsDataLength: 0, - }, - timestamp: 2, - }, { actionSigner: TestData.payeeRaw.identity, name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, @@ -1013,7 +1203,7 @@ describe('index', () => { payee: TestData.payeeRaw.identity, payer: TestData.payerRaw.identity, requestId, - state: RequestLogicTypes.STATE.ACCEPTED, + state: RequestLogicTypes.STATE.CREATED, timestamp: 1544426030, version: CURRENT_VERSION, }, @@ -1021,7 +1211,7 @@ describe('index', () => { }); }); - it('can getRequestFromId do not ignore the same transactions if different nonces', async () => { + it('can getRequestFromId with pending transaction', async () => { const actionCreate: RequestLogicTypes.IAction = Utils.signature.sign( { name: RequestLogicTypes.ACTION_NAME.CREATE, @@ -1051,7 +1241,7 @@ describe('index', () => { TestData.payerRaw.signatureParams, ); - const actionReduce: RequestLogicTypes.IAction = Utils.signature.sign( + const rxReduce: RequestLogicTypes.IAction = Utils.signature.sign( { name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, parameters: { @@ -1063,56 +1253,41 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); - const actionReduce2: RequestLogicTypes.IAction = Utils.signature.sign( - { - name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, - parameters: { - deltaAmount: '1000', - nonce: 1, - requestId: requestId.toUpperCase(), - }, - version: CURRENT_VERSION, - }, - TestData.payeeRaw.signatureParams, - ); - const meta = { ignoredTransactions: [] }; const listActions: Promise = Promise.resolve({ meta, result: { transactions: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: JSON.stringify(actionAccept) }, }, { + state: TransactionTypes.TransactionState.PENDING, timestamp: 3, - transaction: { data: JSON.stringify(actionReduce) }, - }, - { - timestamp: 4, - transaction: { data: JSON.stringify(actionReduce2) }, + transaction: { data: JSON.stringify(rxReduce) }, }, ], }, }); const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, getTransactionsByChannelId: (): Promise => listActions, - persistTransaction: chai.spy(), + persistTransaction: chai.spy() as any, }; const requestLogic = new RequestLogic( fakeTransactionManagerGet, TestData.fakeSignatureProvider, ); - const request = await requestLogic.getRequestFromId(requestId); expect(request, 'request result is wrong').to.deep.equal({ @@ -1121,6 +1296,20 @@ describe('index', () => { transactionManagerMeta: meta, }, result: { + pending: { + events: [ + { + actionSigner: TestData.payeeRaw.identity, + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + extensionsDataLength: 0, + }, + timestamp: 3, + }, + ], + expectedAmount: '123399999999999000', + }, request: { creator: TestData.payeeRaw.identity, currency: { @@ -1146,26 +1335,8 @@ describe('index', () => { }, timestamp: 2, }, - { - actionSigner: TestData.payeeRaw.identity, - name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, - parameters: { - deltaAmount: '1000', - extensionsDataLength: 0, - }, - timestamp: 3, - }, - { - actionSigner: TestData.payeeRaw.identity, - name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, - parameters: { - deltaAmount: '1000', - extensionsDataLength: 0, - }, - timestamp: 4, - }, ], - expectedAmount: '123399999999998000', + expectedAmount: '123400000000000000', extensions: {}, payee: TestData.payeeRaw.identity, payer: TestData.payerRaw.identity, @@ -1178,45 +1349,8 @@ describe('index', () => { }); }); - it('should ignored the corrupted data (not parsable JSON)', async () => { - const transactionNotParsable = { - timestamp: 2, - transaction: { data: '{NOT parsable}' }, - }; - const listActions: Promise = Promise.resolve({ - meta: { ignoredTransactions: [] }, - result: { - transactions: [transactionNotParsable], - }, - }); - - const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), - getTransactionsByChannelId: (): Promise => - listActions, - persistTransaction: chai.spy(), - }; - const requestLogic = new RequestLogic( - fakeTransactionManagerGet, - TestData.fakeSignatureProvider, - ); - - const request = await requestLogic.getRequestFromId(requestId); - expect( - request.meta.ignoredTransactions && request.meta.ignoredTransactions.length, - ).to.be.equal(1); - expect( - request.meta.ignoredTransactions && request.meta.ignoredTransactions[0], - ).to.be.deep.equal({ - reason: 'JSON parsing error', - transaction: transactionNotParsable, - }); - expect(request.result.request, 'request should be null').to.be.null; - }); - - it('should ignored the corrupted data (e.g: wrong properties)', async () => { - const actionCorrupted: RequestLogicTypes.IAction = Utils.signature.sign( + it('can getRequestFromId ignore the same transactions even with different case', async () => { + const actionCreate: RequestLogicTypes.IAction = Utils.signature.sign( { name: RequestLogicTypes.ACTION_NAME.CREATE, parameters: { @@ -1224,7 +1358,7 @@ describe('index', () => { type: RequestLogicTypes.CURRENCY.ETH, value: 'ETH', }, - expectedAmount: 'NOT A NUMBER', + expectedAmount: '123400000000000000', payee: TestData.payeeRaw.identity, payer: TestData.payerRaw.identity, timestamp: 1544426030, @@ -1234,31 +1368,393 @@ describe('index', () => { TestData.payeeRaw.signatureParams, ); - const listActions: Promise = Promise.resolve({ - meta: { ignoredTransactions: [] }, - result: { - transactions: [ - { - timestamp: 2, - transaction: { data: JSON.stringify(actionCorrupted) }, - }, - ], + const actionAccept: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.ACCEPT, + parameters: { + requestId, + }, + version: CURRENT_VERSION, }, - }); - - const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), - getChannelsByTopic: chai.spy(), - getTransactionsByChannelId: (): Promise => - listActions, - persistTransaction: chai.spy(), - }; - const requestLogic = new RequestLogic( - fakeTransactionManagerGet, - TestData.fakeSignatureProvider, + TestData.payerRaw.signatureParams, ); - const request = await requestLogic.getRequestFromId(requestId); + const actionReduce: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + requestId, + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const actionReduce2: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + requestId: requestId.toUpperCase(), + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const meta = { ignoredTransactions: [] }; + const listActions: Promise = Promise.resolve({ + meta, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 1, + transaction: { data: JSON.stringify(actionCreate) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 2, + transaction: { data: JSON.stringify(actionAccept) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 3, + transaction: { data: JSON.stringify(actionReduce) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 4, + transaction: { data: JSON.stringify(actionReduce2) }, + }, + ], + }, + }); + + const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, + getTransactionsByChannelId: (): Promise => + listActions, + persistTransaction: chai.spy() as any, + }; + const requestLogic = new RequestLogic( + fakeTransactionManagerGet, + TestData.fakeSignatureProvider, + ); + + const request = await requestLogic.getRequestFromId(requestId); + + expect(request, 'request result is wrong').to.deep.equal({ + meta: { + ignoredTransactions: [ + { + reason: 'Duplicated transaction', + transaction: { + action: actionReduce2, + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 4, + }, + }, + ], + transactionManagerMeta: meta, + }, + result: { + pending: null, + request: { + creator: TestData.payeeRaw.identity, + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + events: [ + { + actionSigner: TestData.payeeRaw.identity, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 0, + isSignedRequest: false, + }, + timestamp: 1, + }, + { + actionSigner: TestData.payerRaw.identity, + name: RequestLogicTypes.ACTION_NAME.ACCEPT, + parameters: { + extensionsDataLength: 0, + }, + timestamp: 2, + }, + { + actionSigner: TestData.payeeRaw.identity, + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + extensionsDataLength: 0, + }, + timestamp: 3, + }, + ], + expectedAmount: '123399999999999000', + extensions: {}, + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + requestId, + state: RequestLogicTypes.STATE.ACCEPTED, + timestamp: 1544426030, + version: CURRENT_VERSION, + }, + }, + }); + }); + + it('can getRequestFromId do not ignore the same transactions if different nonces', async () => { + const actionCreate: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: '123400000000000000', + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + timestamp: 1544426030, + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const actionAccept: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.ACCEPT, + parameters: { + requestId, + }, + version: CURRENT_VERSION, + }, + TestData.payerRaw.signatureParams, + ); + + const actionReduce: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + requestId, + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const actionReduce2: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + nonce: 1, + requestId: requestId.toUpperCase(), + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const meta = { ignoredTransactions: [] }; + const listActions: Promise = Promise.resolve({ + meta, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 1, + transaction: { data: JSON.stringify(actionCreate) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 2, + transaction: { data: JSON.stringify(actionAccept) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 3, + transaction: { data: JSON.stringify(actionReduce) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 4, + transaction: { data: JSON.stringify(actionReduce2) }, + }, + ], + }, + }); + + const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, + getTransactionsByChannelId: (): Promise => + listActions, + persistTransaction: chai.spy() as any, + }; + const requestLogic = new RequestLogic( + fakeTransactionManagerGet, + TestData.fakeSignatureProvider, + ); + + const request = await requestLogic.getRequestFromId(requestId); + + expect(request, 'request result is wrong').to.deep.equal({ + meta: { + ignoredTransactions: [], + transactionManagerMeta: meta, + }, + result: { + pending: null, + request: { + creator: TestData.payeeRaw.identity, + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + events: [ + { + actionSigner: TestData.payeeRaw.identity, + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + expectedAmount: '123400000000000000', + extensionsDataLength: 0, + isSignedRequest: false, + }, + timestamp: 1, + }, + { + actionSigner: TestData.payerRaw.identity, + name: RequestLogicTypes.ACTION_NAME.ACCEPT, + parameters: { + extensionsDataLength: 0, + }, + timestamp: 2, + }, + { + actionSigner: TestData.payeeRaw.identity, + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + extensionsDataLength: 0, + }, + timestamp: 3, + }, + { + actionSigner: TestData.payeeRaw.identity, + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + extensionsDataLength: 0, + }, + timestamp: 4, + }, + ], + expectedAmount: '123399999999998000', + extensions: {}, + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + requestId, + state: RequestLogicTypes.STATE.ACCEPTED, + timestamp: 1544426030, + version: CURRENT_VERSION, + }, + }, + }); + }); + + it('should ignored the corrupted data (not parsable JSON)', async () => { + const transactionNotParsable = { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 2, + transaction: { data: '{NOT parsable}' }, + }; + const listActions: Promise = Promise.resolve({ + meta: { ignoredTransactions: [] }, + result: { + transactions: [transactionNotParsable], + }, + }); + + const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, + getTransactionsByChannelId: (): Promise => + listActions, + persistTransaction: chai.spy() as any, + }; + const requestLogic = new RequestLogic( + fakeTransactionManagerGet, + TestData.fakeSignatureProvider, + ); + + const request = await requestLogic.getRequestFromId(requestId); + expect( + request.meta.ignoredTransactions && request.meta.ignoredTransactions.length, + ).to.be.equal(1); + expect( + request.meta.ignoredTransactions && request.meta.ignoredTransactions[0], + ).to.be.deep.equal({ + reason: 'JSON parsing error', + transaction: transactionNotParsable, + }); + expect(request.result.request, 'request should be null').to.be.null; + }); + + it('should ignored the corrupted data (e.g: wrong properties)', async () => { + const actionCorrupted: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: 'NOT A NUMBER', + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + timestamp: 1544426030, + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const listActions: Promise = Promise.resolve({ + meta: { ignoredTransactions: [] }, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 2, + transaction: { data: JSON.stringify(actionCorrupted) }, + }, + ], + }, + }); + + const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: chai.spy() as any, + getTransactionsByChannelId: (): Promise => + listActions, + persistTransaction: chai.spy() as any, + }; + const requestLogic = new RequestLogic( + fakeTransactionManagerGet, + TestData.fakeSignatureProvider, + ); + + const request = await requestLogic.getRequestFromId(requestId); expect( request.meta.ignoredTransactions && request.meta.ignoredTransactions.length, @@ -1269,6 +1765,7 @@ describe('index', () => { reason: 'action.parameters.expectedAmount must be a string representing a positive integer', transaction: { action: actionCorrupted, + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, }, }); @@ -1390,30 +1887,205 @@ describe('index', () => { transactions: { [requestId]: [ { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 0, + transaction: { data: JSON.stringify(actionCreate) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 2, + transaction: { data: JSON.stringify(actionAccept) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 3, + transaction: { data: JSON.stringify(rxReduce) }, + }, + ], + [newRequestId2]: [ + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 1, + transaction: { data: JSON.stringify(actionCreate2) }, + }, + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 2, + transaction: { data: JSON.stringify(actionCancel2) }, + }, + ], + [newRequestId3]: [ + { + state: TransactionTypes.TransactionState.CONFIRMED, + timestamp: 4, + transaction: { data: JSON.stringify(actionCreate3) }, + }, + ], + }, + }, + }); + + const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { + getChannelsByMultipleTopics: chai.spy() as any, + getChannelsByTopic: (): Promise => { + return listAllActions; + }, + getTransactionsByChannelId: chai.spy() as any, + persistTransaction: chai.spy() as any, + }; + const requestLogic = new RequestLogic( + fakeTransactionManagerGet, + TestData.fakeSignatureProvider, + ); + + const requests = await requestLogic.getRequestsByTopic('fakeTopicForAll'); + + expect(requests.result.requests.length, 'requests result is wrong').to.equal(3); + }); + + it('can getRequestsByTopic with pending transactions', async () => { + const unsignedActionCreation = { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: '123400000000000000', + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + timestamp: 1544426030, + }, + version: CURRENT_VERSION, + }; + const actionCreate: RequestLogicTypes.IAction = Utils.signature.sign( + unsignedActionCreation, + TestData.payeeRaw.signatureParams, + ); + const newRequestId = MultiFormat.serialize( + Utils.crypto.normalizeKeccak256Hash(unsignedActionCreation), + ); + + const actionAccept: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.ACCEPT, + parameters: { + requestId: newRequestId, + }, + version: CURRENT_VERSION, + }, + TestData.payerRaw.signatureParams, + ); + + const rxReduce: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.REDUCE_EXPECTED_AMOUNT, + parameters: { + deltaAmount: '1000', + requestId: newRequestId, + }, + version: CURRENT_VERSION, + }, + TestData.payeeRaw.signatureParams, + ); + + const unsignedActionCreation2 = { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: { + type: RequestLogicTypes.CURRENCY.BTC, + value: 'BTC', + }, + expectedAmount: '10', + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + timestamp: 1544411111, + }, + version: CURRENT_VERSION, + }; + const actionCreate2: RequestLogicTypes.IAction = Utils.signature.sign( + unsignedActionCreation2, + TestData.payeeRaw.signatureParams, + ); + const newRequestId2 = MultiFormat.serialize( + Utils.crypto.normalizeKeccak256Hash(unsignedActionCreation2), + ); + + const actionCancel2: RequestLogicTypes.IAction = Utils.signature.sign( + { + name: RequestLogicTypes.ACTION_NAME.CANCEL, + parameters: { + requestId: newRequestId2, + }, + version: CURRENT_VERSION, + }, + TestData.payerRaw.signatureParams, + ); + + const unsignedActionCreation3 = { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: { + type: RequestLogicTypes.CURRENCY.BTC, + value: 'BTC', + }, + expectedAmount: '666', + payee: TestData.payeeRaw.identity, + payer: TestData.payerRaw.identity, + timestamp: 1544433333, + }, + version: CURRENT_VERSION, + }; + const actionCreate3: RequestLogicTypes.IAction = Utils.signature.sign( + unsignedActionCreation3, + TestData.payeeRaw.signatureParams, + ); + const newRequestId3 = MultiFormat.serialize( + Utils.crypto.normalizeKeccak256Hash(unsignedActionCreation3), + ); + + const meta = { + dataAccessMeta: { [requestId]: [], [newRequestId2]: [], [newRequestId3]: [] }, + ignoredTransactions: {}, + }; + const listAllActions: Promise< + TransactionTypes.IReturnGetTransactionsByChannels + > = Promise.resolve({ + meta, + result: { + transactions: { + [requestId]: [ + { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 0, transaction: { data: JSON.stringify(actionCreate) }, }, { + state: TransactionTypes.TransactionState.PENDING, timestamp: 2, transaction: { data: JSON.stringify(actionAccept) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 3, transaction: { data: JSON.stringify(rxReduce) }, }, ], [newRequestId2]: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate2) }, }, { + state: TransactionTypes.TransactionState.PENDING, timestamp: 2, transaction: { data: JSON.stringify(actionCancel2) }, }, ], [newRequestId3]: [ { + state: TransactionTypes.TransactionState.PENDING, timestamp: 4, transaction: { data: JSON.stringify(actionCreate3) }, }, @@ -1423,12 +2095,12 @@ describe('index', () => { }); const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, getChannelsByTopic: (): Promise => { return listAllActions; }, - getTransactionsByChannelId: chai.spy(), - persistTransaction: chai.spy(), + getTransactionsByChannelId: chai.spy() as any, + persistTransaction: chai.spy() as any, }; const requestLogic = new RequestLogic( fakeTransactionManagerGet, @@ -1438,6 +2110,30 @@ describe('index', () => { const requests = await requestLogic.getRequestsByTopic('fakeTopicForAll'); expect(requests.result.requests.length, 'requests result is wrong').to.equal(3); + + const firstRequest = requests.result.requests[0]; + expect(firstRequest.pending, 'first pending wrong').to.be.null; + expect( + firstRequest.request!.expectedAmount, + 'first request expectedAmount wrong', + ).to.deep.equal('123399999999999000'); + expect(firstRequest.request!.state, 'first request state wrong').to.deep.equal( + RequestLogicTypes.STATE.CREATED, + ); + + const secondRequest = requests.result.requests[1]; + expect(secondRequest.request!.state, 'second pending wrong').to.deep.equal( + RequestLogicTypes.STATE.CREATED, + ); + expect(secondRequest.pending!.state, 'second pending wrong').to.deep.equal( + RequestLogicTypes.STATE.CANCELED, + ); + + const thirdRequest = requests.result.requests[2]; + expect(thirdRequest.request, 'third pending wrong').to.be.null; + expect(thirdRequest.pending!.state, 'third pending wrong').to.deep.equal( + RequestLogicTypes.STATE.CREATED, + ); }); it('should ignore the transaction none parsable and the rejected action', async () => { @@ -1482,14 +2178,17 @@ describe('index', () => { transactions: { [requestId]: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: JSON.stringify(actionCreate) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: 'Not a json' }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: JSON.stringify(acceptNotValid) }, }, @@ -1499,12 +2198,12 @@ describe('index', () => { }); const fakeTransactionManagerGet: TransactionTypes.ITransactionManager = { - getChannelsByMultipleTopics: chai.spy(), + getChannelsByMultipleTopics: chai.spy() as any, getChannelsByTopic: (): Promise => { return listActions; }, - getTransactionsByChannelId: chai.spy(), - persistTransaction: chai.spy(), + getTransactionsByChannelId: chai.spy() as any, + persistTransaction: chai.spy() as any, }; const requestLogic = new RequestLogic( fakeTransactionManagerGet, @@ -1631,30 +2330,36 @@ describe('index', () => { transactions: { [requestId]: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 0, transaction: { data: JSON.stringify(actionCreate) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: JSON.stringify(actionAccept) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 3, transaction: { data: JSON.stringify(rxReduce) }, }, ], [newRequestId2]: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 1, transaction: { data: JSON.stringify(actionCreate2) }, }, { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 2, transaction: { data: JSON.stringify(actionCancel2) }, }, ], [newRequestId3]: [ { + state: TransactionTypes.TransactionState.CONFIRMED, timestamp: 4, transaction: { data: JSON.stringify(actionCreate3) }, }, @@ -1669,9 +2374,9 @@ describe('index', () => { > => { return listAllActions; }, - getChannelsByTopic: chai.spy(), - getTransactionsByChannelId: chai.spy(), - persistTransaction: chai.spy(), + getChannelsByTopic: chai.spy() as any, + getTransactionsByChannelId: chai.spy() as any, + persistTransaction: chai.spy() as any, }; const requestLogic = new RequestLogic( fakeTransactionManagerGet, diff --git a/packages/request-logic/test/unit/action.test.ts b/packages/request-logic/test/unit/action.test.ts index bf4408397a..68b198808e 100644 --- a/packages/request-logic/test/unit/action.test.ts +++ b/packages/request-logic/test/unit/action.test.ts @@ -11,7 +11,7 @@ import { import Utils from '@requestnetwork/utils'; import Action from '../../src/action'; - +import CreateAction from '../../src/actions/create'; import Version from '../../src/version'; const CURRENT_VERSION = Version.currentVersion; @@ -141,3 +141,28 @@ describe('Action', () => { ); }); }); + +describe('actions retrocompatibility', () => { + it('old format requestId match', async () => { + const actionData = { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: 'ETH', + expectedAmount: '123400000000000000', + payee: TestData.payeeRaw.identity, + timestamp: 1, + }, + version: '2.0.1', + }; + + const action = await Action.createAction( + actionData, + TestData.payeeRaw.identity, + TestData.fakeSignatureProvider, + ); + + const request = CreateAction.createRequest(action, 2); + + expect(Action.getRequestId(action)).equals(request.requestId); + }); +}); diff --git a/packages/request-logic/test/unit/actions/create.test.ts b/packages/request-logic/test/unit/actions/create.test.ts index 92b5a06a81..e6b01e548c 100644 --- a/packages/request-logic/test/unit/actions/create.test.ts +++ b/packages/request-logic/test/unit/actions/create.test.ts @@ -1,6 +1,5 @@ import { expect } from 'chai'; import 'mocha'; -const bigNumber: any = require('bn.js'); import MultiFormat from '@requestnetwork/multi-format'; import { IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; @@ -288,6 +287,103 @@ describe('CreateAction', () => { 'actionCreation.data.parameters.payer.value is wrong', ).to.equal(TestData.payerRaw.address); }); + + it('can create with the payee but the payer is a smartcontract', async () => { + const actionCreation = await CreateAction.format( + { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: TestData.arbitraryExpectedAmount, + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: TestData.payeeRaw.address, + }, + payer: { + network: 'private', + type: IdentityTypes.TYPE.ETHEREUM_SMART_CONTRACT, + value: TestData.payerRaw.address, + } as IdentityTypes.ISmartContractIdentity, + timestamp: TestData.arbitraryTimestamp, + }, + TestData.payeeRaw.identity, + TestData.fakeSignatureProvider, + ); + expect(actionCreation.data.name, 'action is wrong').to.equal( + RequestLogicTypes.ACTION_NAME.CREATE, + ); + expect(actionCreation.data.version, 'actionCreation.data.version is wrong').to.equal( + CURRENT_VERSION, + ); + + expect(actionCreation.data.parameters.currency, 'currency is wrong').to.deep.equal({ + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }); + expect(actionCreation.data.parameters.expectedAmount, 'expectedAmount is wrong').to.equal( + TestData.arbitraryExpectedAmount, + ); + expect(actionCreation.data.parameters.timestamp, 'timestamp is wrong').to.equal( + TestData.arbitraryTimestamp, + ); + expect(actionCreation.data.parameters.nonce, 'nonce is wrong').to.be.undefined; + expect(actionCreation.data.parameters.extensionsData, 'extensionsData is wrong').to.be + .undefined; + + expect( + actionCreation.data.parameters, + 'actionCreation.data.parameters.payer is wrong', + ).to.have.property('payer'); + expect( + actionCreation.data.parameters.payer.type, + 'actionCreation.data.parameters.payer.type is wrong', + ).to.equal(IdentityTypes.TYPE.ETHEREUM_SMART_CONTRACT); + expect( + actionCreation.data.parameters.payer.value, + 'actionCreation.data.parameters.payer.value is wrong', + ).to.equal(TestData.payerRaw.address); + expect( + actionCreation.data.parameters.payer.network, + 'actionCreation.data.parameters.payer.network is wrong', + ).to.equal('private'); + + expect( + actionCreation.data.parameters, + 'actionCreation.data.parameters.payee is wrong', + ).to.have.property('payee'); + expect( + actionCreation.data.parameters.payee.type, + 'actionCreation.data.parameters.payee.type is wrong', + ).to.equal(IdentityTypes.TYPE.ETHEREUM_ADDRESS); + expect( + actionCreation.data.parameters.payee.value, + 'actionCreation.data.parameters.payee.value is wrong', + ).to.equal(TestData.payeeRaw.address); + }); + + it('cannot create with a smartcontract', () => { + expect(() => + CreateAction.format( + { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: TestData.arbitraryExpectedAmount, + payee: { + network: 'rinkeby', + type: IdentityTypes.TYPE.ETHEREUM_SMART_CONTRACT, + value: TestData.payeeRaw.address, + } as IdentityTypes.ISmartContractIdentity, + timestamp: TestData.arbitraryTimestamp, + }, + TestData.payeeRaw.identity, + TestData.fakeSignatureProvider, + ), + ).to.throw('Signer must be the payee or the payer'); + }); + it('cannot create without payee and payer', () => { expect(() => CreateAction.format( @@ -474,7 +570,7 @@ describe('CreateAction', () => { ).to.throw('Signer must be the payee or the payer'); }); - it('can create with amount as integer, bigNumber or zero', async () => { + it('can create with amount as integer or zero', async () => { let actionCreation = await CreateAction.format( { currency: { @@ -494,25 +590,6 @@ describe('CreateAction', () => { '10000', ); - actionCreation = await CreateAction.format( - { - currency: { - type: RequestLogicTypes.CURRENCY.ETH, - value: 'ETH', - }, - expectedAmount: new bigNumber(TestData.arbitraryExpectedAmount), - payee: { - type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, - value: TestData.payeeRaw.address, - }, - }, - TestData.payeeRaw.identity, - TestData.fakeSignatureProvider, - ); - expect(actionCreation.data.parameters.expectedAmount, 'expectedAmount is wrong').to.equal( - TestData.arbitraryExpectedAmount, - ); - actionCreation = await CreateAction.format( { currency: { @@ -563,10 +640,11 @@ describe('CreateAction', () => { type: 'not_ethereumAddress', value: '0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce', }, + payer: TestData.payerRaw.identity, }; expect(() => - CreateAction.format(params, TestData.payeeRaw.identity, TestData.fakeSignatureProvider), - ).to.throw('payee.type not supported'); + CreateAction.format(params, TestData.payerRaw.identity, TestData.fakeSignatureProvider), + ).to.throw('payee: identity type not supported'); }); it('does not support other identity type than "ethereumAddress" for Payer', () => { const params: any = { @@ -575,6 +653,7 @@ describe('CreateAction', () => { value: 'ETH', }, expectedAmount: '1000', + payee: TestData.payeeRaw.identity, payer: { type: 'not_ethereumAddress', value: '0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce', @@ -582,7 +661,41 @@ describe('CreateAction', () => { }; expect(() => CreateAction.format(params, TestData.payeeRaw.identity, TestData.fakeSignatureProvider), - ).to.throw('payer.type not supported'); + ).to.throw('payer: identity type not supported'); + }); + + it('does not support other identity value not ethereum for Payee', () => { + const params: any = { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: '1000', + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: 'not valid ethereum', + }, + }; + expect(() => + CreateAction.format(params, TestData.payeeRaw.identity, TestData.fakeSignatureProvider), + ).to.throw('payee: identity value must be an ethereum address'); + }); + + it('does not support other identity value not ethereum for Payer', () => { + const params: any = { + currency: { + type: RequestLogicTypes.CURRENCY.ETH, + value: 'ETH', + }, + expectedAmount: '1000', + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: 'not valid ethereum', + }, + }; + expect(() => + CreateAction.format(params, TestData.payeeRaw.identity, TestData.fakeSignatureProvider), + ).to.throw('payer: identity value must be an ethereum address'); }); }); @@ -1068,7 +1181,73 @@ describe('CreateAction', () => { signature: TestData.fakeSignature, }; expect(() => CreateAction.createRequest(action, 2)).to.throw( - 'Signer must be the payee or the payer', + 'payee: identity type not supported', + ); + }); + + it('does not support other identity type than "ethereumAddress" for Payer', () => { + const action = { + data: { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: 'ETH', + expectedAmount: TestData.arbitraryExpectedAmount, + extensionsData: [{ id: 'extension1', value: 'whatever' }], + payer: { + type: 'not_ethereumAddress', + value: '0xAf083f77F1fFd54218d91491AFD06c9296EaC3ce', + }, + }, + version: CURRENT_VERSION, + }, + signature: TestData.fakeSignature, + }; + expect(() => CreateAction.createRequest(action, 2)).to.throw( + 'payer: identity type not supported', + ); + }); + + it('does not support identity value not being an "ethereumAddress" for Payee', () => { + const action = { + data: { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: 'ETH', + expectedAmount: TestData.arbitraryExpectedAmount, + extensionsData: [{ id: 'extension1', value: 'whatever' }], + payee: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: 'not an address', + }, + }, + version: CURRENT_VERSION, + }, + signature: TestData.fakeSignature, + }; + expect(() => CreateAction.createRequest(action, 2)).to.throw( + 'payee: identity value must be an ethereum address', + ); + }); + + it('does not support identity value not being an "ethereumAddress" for Payer', () => { + const action = { + data: { + name: RequestLogicTypes.ACTION_NAME.CREATE, + parameters: { + currency: 'ETH', + expectedAmount: TestData.arbitraryExpectedAmount, + extensionsData: [{ id: 'extension1', value: 'whatever' }], + payer: { + type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, + value: 'not an address', + }, + }, + version: CURRENT_VERSION, + }, + signature: TestData.fakeSignature, + }; + expect(() => CreateAction.createRequest(action, 2)).to.throw( + 'payer: identity value must be an ethereum address', ); }); }); diff --git a/packages/request-node/.vscode/settings.json b/packages/request-node/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/request-node/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/request-node/CHANGELOG.md b/packages/request-node/CHANGELOG.md index a7ec2686f2..38fac128d0 100644 --- a/packages/request-node/CHANGELOG.md +++ b/packages/request-node/CHANGELOG.md @@ -3,6 +3,338 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.11.0) (2020-06-29) + + +### Bug Fixes + +* intercept error and log the missed transaction ([#230](https://github.com/RequestNetwork/requestNetwork/issues/230)) ([90f5fdc](https://github.com/RequestNetwork/requestNetwork/commit/90f5fdc814b1e53698be294e1b138e2ea7276794)) + + +### Features + +* resubmit stuck transaction with more gas ([#239](https://github.com/RequestNetwork/requestNetwork/issues/239)) ([cf7f92e](https://github.com/RequestNetwork/requestNetwork/commit/cf7f92eb6ee9f0c5da427f37fa5f12f56812a221)) +* store confirmed transactions ([#235](https://github.com/RequestNetwork/requestNetwork/issues/235)) ([f2d10fc](https://github.com/RequestNetwork/requestNetwork/commit/f2d10fc6af098fec4b8585ffea5e101c256f6a35)) + + + +# 0.16.0 (2020-04-21) + + +### Bug Fixes + +* hide infura token in the status ([#197](https://github.com/RequestNetwork/requestNetwork/issues/197)) ([4d154d7](https://github.com/RequestNetwork/requestNetwork/commit/4d154d717a37bd9212dfec5ee44ff1541453018a)) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) +* **request-node:** Add Request Node version and Request Client version to requests header ([#192](https://github.com/RequestNetwork/requestNetwork/issues/192)) ([20ad94b](https://github.com/RequestNetwork/requestNetwork/commit/20ad94b7679b5c08a3951329b1fa8a58c8a3e2df)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.10.0) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + +### Bug Fixes + +* hide infura token in the status ([#197](https://github.com/RequestNetwork/requestNetwork/issues/197)) ([4d154d7](https://github.com/RequestNetwork/requestNetwork/commit/4d154d717a37bd9212dfec5ee44ff1541453018a)) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) +* **request-node:** Add Request Node version and Request Client version to requests header ([#192](https://github.com/RequestNetwork/requestNetwork/issues/192)) ([20ad94b](https://github.com/RequestNetwork/requestNetwork/commit/20ad94b7679b5c08a3951329b1fa8a58c8a3e2df)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.9.0) (2020-04-21) + + +### Bug Fixes + +* hide infura token in the status ([#197](https://github.com/RequestNetwork/requestNetwork/issues/197)) ([4d154d7](https://github.com/RequestNetwork/requestNetwork/commit/4d154d717a37bd9212dfec5ee44ff1541453018a)) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) +* **request-node:** Add Request Node version and Request Client version to requests header ([#192](https://github.com/RequestNetwork/requestNetwork/issues/192)) ([20ad94b](https://github.com/RequestNetwork/requestNetwork/commit/20ad94b7679b5c08a3951329b1fa8a58c8a3e2df)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.8.0) (2020-04-06) + + + +# 0.14.0 (2020-03-19) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.7.0) (2020-03-23) + + +### Bug Fixes + +* block parsing with encrypted transaction ([#176](https://github.com/RequestNetwork/requestNetwork/issues/176)) ([de86f43](https://github.com/RequestNetwork/requestNetwork/commit/de86f43d7f2886673364bded70ab6a4f8acf4711)) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.6.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.5.7) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/request-node + + + + + +## [0.5.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.5.6) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/request-node + + + + + +## [0.5.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.4...@requestnetwork/request-node@0.5.5) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/request-node + + + + + ## [0.5.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/request-node@0.5.3...@requestnetwork/request-node@0.5.4) (2019-11-20) **Note:** Version bump only for package @requestnetwork/request-node diff --git a/packages/request-node/README.md b/packages/request-node/README.md index 0b5d889ecf..6286a16f8b 100644 --- a/packages/request-node/README.md +++ b/packages/request-node/README.md @@ -254,6 +254,8 @@ Default values correspond to the basic configuration used to run a server in a t - `--persistTransactionTimeout` Defines the delay in seconds to wait before sending a timeout when creating or updating a request - Default value: 600 - Environment variable name: `$PERSIST_TRANSACTION_TIMEOUT` +- `--externalUrl` External url of the node (used to identified where the buffer data are stored before being broadcasted on ethereum) + - Environment variable name: `$EXTERNAL_URL` #### Mnemonic @@ -329,7 +331,8 @@ yarn install yarn build ``` -#### 3. On a new terminal, launch a local [IPFS node](https://docs.ipfs.io/introduction/install/) +#### 3. On a new terminal, launch a local IPFS node +Note: only IPFS v0.4.* supported, from the [IPFS Installation docs](https://docs.ipfs.io/install/), replace the binary URL with the good one from the following list: https://github.com/ipfs/go-ipfs/releases/tag/v0.4.23 ```bash ipfs daemon @@ -351,7 +354,7 @@ ganache-cli -l 90000000 -p 8545 -m \"candy maple cake sugar pudding cream honey #### 6. Deploy the smart contracts on ganache ```bash -cd packages/ethereum-storage +cd packages/smart-contracts yarn deploy ``` @@ -362,6 +365,11 @@ cd ../packages/request-node yarn start ``` +#### 8. Test + +Open a browser and navigate towards: http://localhost:3000/status +You can see the details of your local Request & IPFS nodes. + ## Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. diff --git a/packages/request-node/package.json b/packages/request-node/package.json index bf0c993800..6606454d36 100644 --- a/packages/request-node/package.json +++ b/packages/request-node/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/request-node", - "version": "0.5.4", + "version": "0.11.0", "publishConfig": { "access": "public" }, @@ -31,26 +31,27 @@ "scripts": { "build": "tsc -b", "build:watch": "tsc -b --watch", - "test": "nyc mocha --timeout=10000 --exit --require ts-node/register --require source-map-support/register test/**/*.ts", - "test:watch": "nyc mocha --watch --watch-extensions ts --timeout=10000 --exit --require ts-node/register --require source-map-support/register test/**/*.ts", + "test": "nyc mocha --timeout=10000 --exit --extension ts --require source-map-support/register test/**/*.ts", + "test:watch": "yarn test --watch", "start": "ts-node src/server.ts", "start:watch": "ts-node-dev src/server.ts", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint-staged": "lint-staged", "lint": "tslint --project .", "init-ipfs": "node init-ipfs.js" }, "dependencies": { - "@requestnetwork/data-access": "0.5.2", - "@requestnetwork/ethereum-storage": "0.4.5", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/data-access": "0.11.0", + "@requestnetwork/ethereum-storage": "0.10.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "@truffle/hdwallet-provider": "1.0.18", "chalk": "2.4.2", "cors": "2.8.5", "dotenv": "8.0.0", "express": "4.17.1", "http-status-codes": "1.3.0", + "keyv": "4.0.0", "keyv-file": "0.1.13", "shelljs": "0.8.3", "yargs": "12.0.5" @@ -63,12 +64,12 @@ "chai": "4.2.0", "chai-as-promised": "7.1.1", "lint-staged": "8.1.3", - "mocha": "5.2.0", + "mocha": "6.2.2", "prettier": "1.16.4", "shx": "0.3.2", "source-map-support": "0.5.13", "supertest": "3.4.2", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "ts-node-dev": "1.0.0-pre.39", "tslint": "5.12.1", "typescript": "3.7.2" diff --git a/packages/request-node/src/config.ts b/packages/request-node/src/config.ts index 6f04501c2d..5efee9fd0f 100644 --- a/packages/request-node/src/config.ts +++ b/packages/request-node/src/config.ts @@ -31,6 +31,7 @@ const defaultValues: any = { mode: modeType.human, }, server: { + externalUrl: 'localhost', headers: '{}', port: 3000, }, @@ -39,6 +40,14 @@ const defaultValues: any = { }, }; +/** + * Get the external url of the node (used to identified where the buffer data are stored before being broadcasted) + * @returns the external url + */ +export function getServerExternalUrl(): string { + return argv.externalUrl || process.env.EXTERNAL_URL || defaultValues.server.externalUrl; +} + /** * Get the port from command line argument, environment variables or default values to allow user to connect to the server * @returns the port to listen to connection on the server @@ -253,6 +262,9 @@ export function getHelpMessage(): string { headers (${ defaultValues.server.headers })\t\t\t\tCustom headers to send with the API responses + externalUrl (${ + defaultValues.server.externalUrl + })\t\t\t\tExternal url of the node (used to identified where the buffer data are stored before being broadcasted) ETHEREUM OPTIONS networkId (${ diff --git a/packages/request-node/src/request/confirmedTransactionStore.ts b/packages/request-node/src/request/confirmedTransactionStore.ts new file mode 100644 index 0000000000..a1c9acc317 --- /dev/null +++ b/packages/request-node/src/request/confirmedTransactionStore.ts @@ -0,0 +1,73 @@ +import { DataAccessTypes, LogTypes } from '@requestnetwork/types'; +import * as httpStatus from 'http-status-codes'; + +import * as Keyv from 'keyv'; +import KeyvFile from 'keyv-file'; + +/** + * Class for storing confirmed transactions information + * When 'confirmed' event is receive from a 'persistTransaction', the event data are stored. + * The client can call the getConfirmed entry point, to get the confirmed event. + */ +export default class ConfirmedTransactionStore { + public store: Keyv; + + /** + * Confirmed transactions store constructor + */ + constructor(store?: KeyvFile) { + this.store = new Keyv({ + namespace: 'ConfirmedTransactions', + store, + }); + } + + /** + * Returns the information of a confirmed transaction + * + * @param clientRequest http client request object + * @param serverResponse http server response object + * @param logger logger + */ + public async getConfirmedTransaction( + clientRequest: any, + serverResponse: any, + logger: LogTypes.ILogger, + ): Promise { + if (!clientRequest.query.transactionHash) { + serverResponse + .status(httpStatus.UNPROCESSABLE_ENTITY) + .send('transactionHash missing in the query'); + } else { + try { + const result: DataAccessTypes.IReturnPersistTransaction | undefined = await this.store.get( + clientRequest.query.transactionHash, + ); + + if (result) { + return serverResponse.status(httpStatus.OK).send(result); + } + + return serverResponse.status(httpStatus.NOT_FOUND).send(); + } catch (e) { + logger.error(`getConfirmedTransaction error: ${e}`); + logger.debug(`getConfirmedTransaction fail`, ['metric', 'successRate']); + + serverResponse.status(httpStatus.INTERNAL_SERVER_ERROR).send(e); + } + } + } + + /** + * Stores the result of a transaction confirmation + * + * @param transactionHash hash of the transaction + * @param result result of the event "confirmed" + */ + public async addConfirmedTransaction( + transactionHash: string, + result: DataAccessTypes.IReturnPersistTransaction, + ): Promise { + await this.store.set(transactionHash, result); + } +} diff --git a/packages/request-node/src/request/getStatus.ts b/packages/request-node/src/request/getStatus.ts new file mode 100644 index 0000000000..23572e4ffe --- /dev/null +++ b/packages/request-node/src/request/getStatus.ts @@ -0,0 +1,86 @@ +import { DataAccess } from '@requestnetwork/data-access'; +import { LogTypes } from '@requestnetwork/types'; +import * as httpStatus from 'http-status-codes'; +import * as config from '../config'; + +const packageJson = require('../../package.json'); + +const GET_CHANNELS_TIMEOUT: number = 600000; + +/** + * Handles getStatus of data-access layer. + * + * @param clientRequest http client request object + * @param serverResponse http server response object + * @param dataAccess data access layer + */ +export default async function getStatus( + clientRequest: any, + serverResponse: any, + dataAccess: DataAccess, + logger: LogTypes.ILogger, +): Promise { + // Used to compute request time + const requestStartTime = Date.now(); + + // As the Node doesn't implement a cache, all transactions have to be retrieved directly on IPFS + // This operation can take a long time and then the timeout of the request should be increase + // PROT-187: Decrease or remove this value + clientRequest.setTimeout(GET_CHANNELS_TIMEOUT); + + if (!dataAccess._getStatus) { + return serverResponse + .status(httpStatus.INTERNAL_SERVER_ERROR) + .send('The node do not support this feature'); + } + + try { + const dataAccessStatus = await dataAccess._getStatus(clientRequest.query.detailed); + let providerUrl: string = ''; + + // let's extract only the hostname to hide any token or sensible key + try { + const providerUrlObject: URL = new URL(config.getStorageWeb3ProviderUrl()); + providerUrl = providerUrlObject.hostname; + } catch (e) { + providerUrl = 'Error: not an URL format'; + } + + const status = { + dataAccess: dataAccessStatus, + node: { + customHeaders: config.getCustomHeaders(), + ethereum: { + concurrency: config.getStorageConcurrency(), + lastBlockNumberDelay: config.getLastBlockNumberDelay(), + networkId: config.getStorageNetworkId(), + providerUrl, + retryDelay: config.getEthereumRetryDelay(), + }, + ipfs: { + host: config.getIpfsHost(), + port: config.getIpfsPort(), + protocol: config.getIpfsProtocol(), + timeout: config.getIpfsTimeout(), + }, + persistTransactionTimeout: config.getPersistTransactionTimeout(), + port: config.getServerPort(), + serverExternalUrl: config.getServerExternalUrl(), + version: packageJson.version, + }, + }; + + // Log the request time + const requestEndTime = Date.now(); + logger.debug(`getStatus latency: ${requestEndTime - requestStartTime}ms`, [ + 'metric', + 'latency', + ]); + + serverResponse.status(httpStatus.OK).send(status); + } catch (e) { + logger.error(`getStatus error: ${e}`); + + serverResponse.status(httpStatus.INTERNAL_SERVER_ERROR).send(e); + } +} diff --git a/packages/request-node/src/request/ipfsAdd.ts b/packages/request-node/src/request/ipfsAdd.ts new file mode 100644 index 0000000000..44e450b187 --- /dev/null +++ b/packages/request-node/src/request/ipfsAdd.ts @@ -0,0 +1,69 @@ +import { Block } from '@requestnetwork/data-access'; +import { LogTypes, StorageTypes } from '@requestnetwork/types'; + +import * as httpStatus from 'http-status-codes'; +import { getPersistTransactionTimeout } from '../config'; + +/** + * Handles ipfsAdd of data-access layer. + * + * @param clientRequest http client request object + * @param serverResponse http server response object + * @param dataAccess data access layer + */ +export default async function ipfsAdd( + clientRequest: any, + serverResponse: any, + ethereumStorage: StorageTypes.IStorage, + logger: LogTypes.ILogger, +): Promise { + // Retrieves data access layer + let dataAccessResponse; + + // Used to compute request time + const requestStartTime = Date.now(); + + // Set the timeout from the value from config and convert seconds to milliseconds + // tslint:disable:no-magic-numbers + clientRequest.setTimeout(getPersistTransactionTimeout() * 1000); + + // Verifies if data send from post are correct + // clientRequest.body is expected to contain data for data-acces layer: + // transactionData: data of the transaction + // topics (optional): arbitrary strings that reference the transaction + if (!clientRequest.body || !clientRequest.body.data) { + serverResponse.status(httpStatus.BAD_REQUEST).send('Incorrect data'); + } else { + try { + // check that the data are actually a data-access block + Block.parseBlock(clientRequest.body.data); + } catch (error) { + return serverResponse.status(httpStatus.BAD_REQUEST).send('data must be a block'); + } + + if (!ethereumStorage._ipfsAdd) { + return serverResponse + .status(httpStatus.INTERNAL_SERVER_ERROR) + .send('The node do not support this feature'); + } + + try { + dataAccessResponse = await ethereumStorage._ipfsAdd(JSON.stringify(clientRequest.body.data)); + + // Log the request time + const requestEndTime = Date.now(); + logger.debug(`ipfsAdd latency: ${requestEndTime - requestStartTime}ms`, [ + 'metric', + 'latency', + ]); + logger.debug(`ipfsAdd successfully completed`, ['metric', 'successRate']); + + serverResponse.status(httpStatus.OK).send(dataAccessResponse); + } catch (e) { + logger.error(`ipfsAdd error: ${e}`); + logger.debug(`ipfsAdd fail`, ['metric', 'successRate']); + + serverResponse.status(httpStatus.INTERNAL_SERVER_ERROR).send(e); + } + } +} diff --git a/packages/request-node/src/request/persistTransaction.ts b/packages/request-node/src/request/persistTransaction.ts index be1447f258..0c4448ed55 100644 --- a/packages/request-node/src/request/persistTransaction.ts +++ b/packages/request-node/src/request/persistTransaction.ts @@ -1,56 +1,109 @@ import { DataAccess } from '@requestnetwork/data-access'; -import { LogTypes } from '@requestnetwork/types'; +import { LogTypes, MultiFormatTypes } from '@requestnetwork/types'; +import Utils from '@requestnetwork/utils'; import * as httpStatus from 'http-status-codes'; import { getPersistTransactionTimeout } from '../config'; +import ConfirmedTransactionStore from './confirmedTransactionStore'; + /** - * Handles persistTransaction of data-access layer. - * - * @param clientRequest http client request object - * @param serverResponse http server response object - * @param dataAccess data access layer + * Class to persist transactions though the data-access layer */ -export default async function persistTransaction( - clientRequest: any, - serverResponse: any, - dataAccess: DataAccess, - logger: LogTypes.ILogger, -): Promise { - // Retrieves data access layer - let dataAccessResponse; - - // Used to compute request time - const requestStartTime = Date.now(); - - // Set the timeout from the value from config and convert seconds to milliseconds - // tslint:disable:no-magic-numbers - clientRequest.setTimeout(getPersistTransactionTimeout() * 1000); - - // Verifies if data send from post are correct - // clientRequest.body is expected to contain data for data-acces layer: - // transactionData: data of the transaction - // topics (optional): arbitrary strings that reference the transaction - if (!clientRequest.body || !clientRequest.body.transactionData || !clientRequest.body.channelId) { - serverResponse.status(httpStatus.UNPROCESSABLE_ENTITY).send('Incorrect data'); - } else { - try { - dataAccessResponse = await dataAccess.persistTransaction( - clientRequest.body.transactionData, - clientRequest.body.channelId, - clientRequest.body.topics, - ); - - // Log the request time - const requestEndTime = Date.now(); - logger.debug(`persistTransaction latency: ${requestEndTime - requestStartTime}ms`, ['metric', 'latency']); - logger.debug(`persistTransaction successfully completed`, ['metric', 'successRate']); - - serverResponse.status(httpStatus.OK).send(dataAccessResponse); - } catch (e) { - logger.error(`persistTransaction error: ${e}`); - logger.debug(`persistTransaction fail`, ['metric', 'successRate']); - - serverResponse.status(httpStatus.INTERNAL_SERVER_ERROR).send(e); +export default class PersistTransaction { + private confirmedTransactionStore: ConfirmedTransactionStore; + + /** + * Persist transaction constructor + */ + constructor(confirmedTransactionStore: ConfirmedTransactionStore) { + this.confirmedTransactionStore = confirmedTransactionStore; + } + + /** + * Handles persistTransaction of data-access layer. + * + * @param clientRequest http client request object + * @param serverResponse http server response object + * @param dataAccess data access layer + */ + public async persistTransaction( + clientRequest: any, + serverResponse: any, + dataAccess: DataAccess, + logger: LogTypes.ILogger, + ): Promise { + // Retrieves data access layer + let dataAccessResponse; + + // Used to compute request time + const requestStartTime = Date.now(); + + // Set the timeout from the value from config and convert seconds to milliseconds + // tslint:disable:no-magic-numbers + clientRequest.setTimeout(getPersistTransactionTimeout() * 1000); + + // Verifies if data send from post are correct + // clientRequest.body is expected to contain data for data-acces layer: + // transactionData: data of the transaction + // topics (optional): arbitrary strings that reference the transaction + if ( + !clientRequest.body || + !clientRequest.body.transactionData || + !clientRequest.body.channelId + ) { + serverResponse.status(httpStatus.UNPROCESSABLE_ENTITY).send('Incorrect data'); + } else { + try { + const transactionHash: MultiFormatTypes.HashTypes.IHash = Utils.crypto.normalizeKeccak256Hash( + clientRequest.body.transactionData, + ); + + dataAccessResponse = await dataAccess.persistTransaction( + clientRequest.body.transactionData, + clientRequest.body.channelId, + clientRequest.body.topics, + ); + + // when the transaction is confirmed, store the information to be serve when requested + dataAccessResponse.on('confirmed', async dataAccessConfirmedResponse => { + await this.confirmedTransactionStore.addConfirmedTransaction( + transactionHash.value, + dataAccessConfirmedResponse, + ); + logger.info(`Transaction confirmed: ${transactionHash.value}`, ['metric', 'successRate']); + }); + + // when the transaction fails, log an error + dataAccessResponse.on('error', async e => { + const logData = [ + 'transactionHash', + transactionHash.value, + 'channelId', + clientRequest.body.channelId, + 'topics', + clientRequest.body.topics, + 'transactionData', + clientRequest.body.transactionData, + ].join('\n'); + + logger.error(`persistTransaction error: ${e}. \n${logData}`); + }); + + // Log the request time + const requestEndTime = Date.now(); + logger.debug(`persistTransaction latency: ${requestEndTime - requestStartTime}ms`, [ + 'metric', + 'latency', + ]); + logger.debug(`persistTransaction successfully completed`, ['metric', 'successRate']); + + serverResponse.status(httpStatus.OK).send(dataAccessResponse); + } catch (e) { + logger.error(`persistTransaction error: ${e}`); + logger.debug(`persistTransaction fail`, ['metric', 'successRate']); + + serverResponse.status(httpStatus.INTERNAL_SERVER_ERROR).send(e); + } } } } diff --git a/packages/request-node/src/requestNode.ts b/packages/request-node/src/requestNode.ts index af50238f1b..d3db90c366 100644 --- a/packages/request-node/src/requestNode.ts +++ b/packages/request-node/src/requestNode.ts @@ -8,16 +8,23 @@ import KeyvFile from 'keyv-file'; import Utils from '@requestnetwork/utils'; import { getCustomHeaders, getInitializationStorageFilePath, getMnemonic } from './config'; +import ConfirmedTransactionStore from './request/confirmedTransactionStore'; import getChannelsByTopic from './request/getChannelsByTopic'; +import getStatus from './request/getStatus'; import getTransactionsByChannelId from './request/getTransactionsByChannelId'; -import persistTransaction from './request/persistTransaction'; +import ipfsAdd from './request/ipfsAdd'; +import PersistTransaction from './request/persistTransaction'; import { getEthereumStorage } from './storageUtils'; +const packageJson = require('../package.json'); + const NOT_FOUND_MESSAGE = - 'Not found\nAvailable endpoints:\n/POST persistTransaction\n/GET getTransactionsByChannelId\n/GET getChannelsByTopic'; + 'Not found\nAvailable endpoints:\n/POST persistTransaction\n/GET getTransactionsByChannelId\n/GET getChannelsByTopic\n/POST /ipfsAdd\nGET getConfirmedTransaction\nGET status'; const NOT_INITIALIZED_MESSAGE = 'The node is not initialized'; +const REQUEST_NODE_VERSION_HEADER = 'X-Request-Network-Node-Version'; + /** * Main class for request node express server * This class defines routes to handle requests from client @@ -28,11 +35,14 @@ class RequestNode { * This attribute is left public for mocking purpose */ public dataAccess: DataAccess; + public ethereumStorage: StorageTypes.IStorage; private express: any; private initialized: boolean; private logger: LogTypes.ILogger; - + private persistTransaction: PersistTransaction; + private confirmedTransactionStore: ConfirmedTransactionStore; + private requestNodeVersion: string; /** * Request Node constructor * @@ -52,22 +62,26 @@ class RequestNode { : undefined; // Use ethereum storage for the storage layer - const ethereumStorage: StorageTypes.IStorage = getEthereumStorage( - getMnemonic(), - this.logger, - store, - ); + const ethereumStorage = getEthereumStorage(getMnemonic(), this.logger, store); // Use an in-file Transaction index if a path is specified, an in-memory otherwise const transactionIndex = new TransactionIndex(store); + this.ethereumStorage = ethereumStorage; + this.dataAccess = new DataAccess(ethereumStorage, { logger: this.logger, transactionIndex, }); + this.confirmedTransactionStore = new ConfirmedTransactionStore(store); + this.persistTransaction = new PersistTransaction(this.confirmedTransactionStore); + this.express = express(); this.mountRoutes(); + + // Get the version of the Request Node for the request's response header + this.requestNodeVersion = packageJson.version; } /** @@ -136,6 +150,12 @@ class RequestNode { }); } + // Set the Request Node version to the header + this.express.use((_: any, res: any, next: any) => { + res.header(REQUEST_NODE_VERSION_HEADER, this.requestNodeVersion); + next(); + }); + // Supported encodings this.express.use(express.json()); this.express.use(express.urlencoded({ extended: true })); @@ -149,6 +169,23 @@ class RequestNode { router.get('/readyz', (_, serverResponse: any) => { if (this.initialized) { return serverResponse.status(httpStatus.OK).send('OK'); + } + return serverResponse.status(httpStatus.SERVICE_UNAVAILABLE).send(NOT_INITIALIZED_MESSAGE); + }); + + // Route for satus check + router.get('/status', (clientRequest: any, serverResponse: any) => { + if (this.initialized) { + return getStatus(clientRequest, serverResponse, this.dataAccess, this.logger); + } else { + return serverResponse.status(httpStatus.SERVICE_UNAVAILABLE).send(NOT_INITIALIZED_MESSAGE); + } + }); + + // Route for ipfs-add request + router.post('/ipfsAdd', (clientRequest: any, serverResponse: any) => { + if (this.initialized) { + return ipfsAdd(clientRequest, serverResponse, this.ethereumStorage, this.logger); } else { return serverResponse.status(httpStatus.SERVICE_UNAVAILABLE).send(NOT_INITIALIZED_MESSAGE); } @@ -157,7 +194,25 @@ class RequestNode { // Route for persistTransaction request router.post('/persistTransaction', (clientRequest: any, serverResponse: any) => { if (this.initialized) { - return persistTransaction(clientRequest, serverResponse, this.dataAccess, this.logger); + return this.persistTransaction.persistTransaction( + clientRequest, + serverResponse, + this.dataAccess, + this.logger, + ); + } else { + return serverResponse.status(httpStatus.SERVICE_UNAVAILABLE).send(NOT_INITIALIZED_MESSAGE); + } + }); + + // Route for getConfirmedTransaction request + router.get('/getConfirmedTransaction', (clientRequest: any, serverResponse: any) => { + if (this.initialized) { + return this.confirmedTransactionStore.getConfirmedTransaction( + clientRequest, + serverResponse, + this.logger, + ); } else { return serverResponse.status(httpStatus.SERVICE_UNAVAILABLE).send(NOT_INITIALIZED_MESSAGE); } diff --git a/packages/request-node/src/storageUtils.ts b/packages/request-node/src/storageUtils.ts index 817e111347..e8b340180b 100644 --- a/packages/request-node/src/storageUtils.ts +++ b/packages/request-node/src/storageUtils.ts @@ -2,6 +2,7 @@ import { EthereumStorage } from '@requestnetwork/ethereum-storage'; import { LogTypes, StorageTypes } from '@requestnetwork/types'; import * as config from './config'; +import * as Keyv from 'keyv'; import KeyvFile from 'keyv-file'; const hdWalletProvider = require('@truffle/hdwallet-provider'); @@ -34,7 +35,13 @@ export function getEthereumStorage( web3Provider: provider, }; + const store = new Keyv({ + namespace: 'EthereumStorage', + store: metadataStore, + }); + return new EthereumStorage( + config.getServerExternalUrl(), ipfsGatewayConnection, web3Connection, { @@ -43,6 +50,6 @@ export function getEthereumStorage( maxConcurrency: config.getStorageConcurrency(), retryDelay: config.getEthereumRetryDelay(), }, - metadataStore, + store, ); } diff --git a/packages/request-node/test/getConfirmedTransaction.test.ts b/packages/request-node/test/getConfirmedTransaction.test.ts new file mode 100644 index 0000000000..5605ac03ad --- /dev/null +++ b/packages/request-node/test/getConfirmedTransaction.test.ts @@ -0,0 +1,71 @@ +import 'mocha'; + +import Utils from '@requestnetwork/utils'; +import { expect } from 'chai'; +import * as httpStatus from 'http-status-codes'; +import * as request from 'supertest'; +import requestNode from '../src/requestNode'; + +const channelId = '010aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + +const transactionData = { data: 'this is sample data for a transaction' }; +const transactionHash = Utils.crypto.normalizeKeccak256Hash(transactionData).value; + +let requestNodeInstance; +let server: any; + +// tslint:disable:no-magic-numbers +// tslint:disable:no-unused-expression +describe('getConfirmedTransaction', () => { + before(async () => { + requestNodeInstance = new requestNode(); + await requestNodeInstance.initialize(); + + // Any port number can be used since we use supertest + server = requestNodeInstance.listen(3000, () => 0); + }); + + after(() => { + server.close(); + }); + + it('responds with status 200 to requests with correct values', async () => { + await request(server) + .post('/persistTransaction') + .send({ channelId, transactionData }) + .set('Accept', 'application/json') + .expect(httpStatus.OK); + + await request(server) + .get('/getConfirmedTransaction') + .query({ transactionHash }) + .set('Accept', 'application/json') + .expect(httpStatus.NOT_FOUND); + + // wait a bit for the confirmation + await new Promise((resolve): any => setTimeout(resolve, 5000)); + + const serverResponse = await request(server) + .get('/getConfirmedTransaction') + .query({ transactionHash }) + .set('Accept', 'application/json') + .expect(httpStatus.OK); + + expect( + serverResponse.body.result, + 'getConfirmedTransaction request result should always be empty', + ).to.be.empty; + expect( + serverResponse.body.meta.storageMeta.state, + 'getConfirmedTransaction request meta', + ).to.be.equal('confirmed'); + }); + + it('responds with status 422 to requests with no value', async () => { + await request(server) + .get('/getConfirmedTransaction') + .query({}) + .set('Accept', 'application/json') + .expect(httpStatus.UNPROCESSABLE_ENTITY); + }); +}); diff --git a/packages/request-node/test/getTransactionsByChannelId.test.ts b/packages/request-node/test/getTransactionsByChannelId.test.ts index 4d11e544ef..1fc32481cf 100644 --- a/packages/request-node/test/getTransactionsByChannelId.test.ts +++ b/packages/request-node/test/getTransactionsByChannelId.test.ts @@ -5,9 +5,9 @@ import * as httpStatus from 'http-status-codes'; import * as request from 'supertest'; import requestNode from '../src/requestNode'; -const channelId = '0xchannelId1'; -const anotherChannelId = '0xanotherChannelId1'; -const nonExistentChannelId = 'NonExistentTopic'; +const channelId = '01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab'; +const anotherChannelId = '01bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc'; +const nonExistentChannelId = '01cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccd'; const transactionData = { data: 'this is sample data for a transaction to test getTransactionsByChannelId', }; diff --git a/packages/request-node/test/ipfsAdd.test.ts b/packages/request-node/test/ipfsAdd.test.ts new file mode 100644 index 0000000000..ee6c4ef7a0 --- /dev/null +++ b/packages/request-node/test/ipfsAdd.test.ts @@ -0,0 +1,71 @@ +import 'mocha'; + +import * as httpStatus from 'http-status-codes'; +import * as request from 'supertest'; +import requestNode from '../src/requestNode'; + +let requestNodeInstance; +let server: any; + +// tslint:disable:no-magic-numbers +// tslint:disable:no-unused-expression +describe('ipfsAdd', () => { + before(async () => { + requestNodeInstance = new requestNode(); + await requestNodeInstance.initialize(); + + // Any port number can be used since we use supertest + server = requestNodeInstance.listen(3000, () => 0); + }); + + after(() => { + server.close(); + }); + + it('responds with status 200 to requests with correct values', async () => { + const blockString = JSON.stringify({ + header: { + channelIds: { '01ae1a665f3c4ebd7599fe32d30eb21cc6118097d485deee911118b143b92be12d': [0] }, + topics: { + '01ae1a665f3c4ebd7599fe32d30eb21cc6118097d485deee911118b143b92be12d': [ + '01f1a21ab419611dbf492b3136ac231c8773dc897ee0eb5167ef2051a39e685e76', + ], + }, + version: '0.1.0', + }, + transactions: [ + { + data: + '{"data":{"name":"create","parameters":{"currency":{"type":"BTC","value":"BTC"},"expectedAmount":"1000","payee":{"type":"ethereumAddress","value":"0x627306090abab3a6e1400e9345bc60c78a8bef57"},"payer":{"type":"ethereumAddress","value":"0xf17f52151ebef6c7334fad080c5704d77216b732"},"extensionsData":[],"timestamp":1578884046},"version":"2.0.2"},"signature":{"method":"ecdsa","value":"0x82dac7769e5ea7889d1916205de71628ad14bd152d9d4341c9e6d3401425a60f32a2c5ca998260dfc5d8c6575b564efc1bd8018fd1576d1920b8296caa6407521c"}}', + }, + ], + }); + await request(server) + .post('/ipfsAdd') + .send({ data: blockString }) + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect({ + ipfsHash: 'QmaViWwahWwCU7DgYBLYwfvBuEU9bj3F3rmLDoAS5ujqXX', + ipfsSize: 1026, + }); + }); + + it('responds with status 400 to requests with no value', async () => { + await request(server) + .post('/ipfsAdd') + .send({}) + .set('Accept', 'application/json') + .expect(httpStatus.BAD_REQUEST); + }); + + it('responds with status 400 to requests with badly formatted value', async () => { + await request(server) + .post('/ipfsAdd') + .send({ + data: 'not parsable', + }) + .set('Accept', 'application/json') + .expect(httpStatus.BAD_REQUEST); + }); +}); diff --git a/packages/request-node/test/requestNode.test.ts b/packages/request-node/test/requestNode.test.ts index 24275a23d5..3b1bbe3ca3 100644 --- a/packages/request-node/test/requestNode.test.ts +++ b/packages/request-node/test/requestNode.test.ts @@ -10,6 +10,9 @@ import requestNode from '../src/requestNode'; chai.use(chaiAsPromised); const expect = chai.expect; +const packageJson = require('../package.json'); +const requestNodeVersion = packageJson.version; + const dataAccessInitializeFailureMock = async (): Promise => { throw Error('This mock function always fails'); }; @@ -32,7 +35,7 @@ describe('requestNode server', () => { server.close(); }); - it('responds with status 404 to unimplemented requests', async () => { + it('responds with status 404 to unimplemented requests', () => { request(server) .post('/') .end((_err, res) => { @@ -99,6 +102,16 @@ describe('requestNode server', () => { .expect('x-custom-test-header', 'test-passed'); }); + it('the response header contains the Request Node version', async () => { + // Import directly requestNode to create a server + requestNodeInstance = new requestNode(); + server = requestNodeInstance.listen(3003, () => 0); + + await request(server) + .post('/') + .expect('X-Request-Network-Node-Version', requestNodeVersion); + }); + it('must throw if no mnemonic given with rinkeby', async () => { process.env.ETHEREUM_NETWORK_ID = '4'; diff --git a/packages/ethereum-storage/.gitignore b/packages/smart-contracts/.gitignore similarity index 100% rename from packages/ethereum-storage/.gitignore rename to packages/smart-contracts/.gitignore diff --git a/packages/ethereum-storage/.soliumrc.json b/packages/smart-contracts/.soliumrc.json similarity index 100% rename from packages/ethereum-storage/.soliumrc.json rename to packages/smart-contracts/.soliumrc.json diff --git a/packages/smart-contracts/CHANGELOG.md b/packages/smart-contracts/CHANGELOG.md new file mode 100644 index 0000000000..0125ef8151 --- /dev/null +++ b/packages/smart-contracts/CHANGELOG.md @@ -0,0 +1,270 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# 0.9.0 (2020-06-29) + + +### Features + +* add the identity ethereumSmartContract to the request logic ([#218](https://github.com/RequestNetwork/requestNetwork/issues/218)) ([66d97e0](https://github.com/RequestNetwork/requestNetwork/commit/66d97e00dee7305088cb94a0edf542fe4d0bbd56)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* ethereum payment proxy contract ([#135](https://github.com/RequestNetwork/requestNetwork/issues/135)) ([f9bff97](https://github.com/RequestNetwork/requestNetwork/commit/f9bff97fbe47b8b7fc6ff4fe5048ccc260501ab2)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.8.0 (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* ethereum payment proxy contract ([#135](https://github.com/RequestNetwork/requestNetwork/issues/135)) ([f9bff97](https://github.com/RequestNetwork/requestNetwork/commit/f9bff97fbe47b8b7fc6ff4fe5048ccc260501ab2)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.7.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* ethereum payment proxy contract ([#135](https://github.com/RequestNetwork/requestNetwork/issues/135)) ([f9bff97](https://github.com/RequestNetwork/requestNetwork/commit/f9bff97fbe47b8b7fc6ff4fe5048ccc260501ab2)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.6.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* ethereum payment proxy contract ([#135](https://github.com/RequestNetwork/requestNetwork/issues/135)) ([f9bff97](https://github.com/RequestNetwork/requestNetwork/commit/f9bff97fbe47b8b7fc6ff4fe5048ccc260501ab2)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.5.0 (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* ethereum payment proxy contract ([#135](https://github.com/RequestNetwork/requestNetwork/issues/135)) ([f9bff97](https://github.com/RequestNetwork/requestNetwork/commit/f9bff97fbe47b8b7fc6ff4fe5048ccc260501ab2)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.4.0 (2020-02-20) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* custom docker ganache image ([#129](https://github.com/RequestNetwork/requestNetwork/issues/129)) ([9ab725d](https://github.com/RequestNetwork/requestNetwork/commit/9ab725dca826ba82152c9f7e0cedc8038c6a17b1)) +* ethereum payment proxy contract ([#135](https://github.com/RequestNetwork/requestNetwork/issues/135)) ([f9bff97](https://github.com/RequestNetwork/requestNetwork/commit/f9bff97fbe47b8b7fc6ff4fe5048ccc260501ab2)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.3.0 (2020-01-16) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) +* **smart-contracts:** fix json require to work with webpack ([#105](https://github.com/RequestNetwork/requestNetwork/issues/105)) ([a465e83](https://github.com/RequestNetwork/requestNetwork/commit/a465e83a739a7648e71d8ebb4a3a4eb389e00f13)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) + + + + + +# 0.2.0 (2019-12-18) + + +### Bug Fixes + +* add payable to the ERC20 proxy contract fallback ([#96](https://github.com/RequestNetwork/requestNetwork/issues/96)) ([1237b64](https://github.com/RequestNetwork/requestNetwork/commit/1237b6431f3d6e141e3bae1690ac59c553ed49f2)) + + +### Features + +* deploy ERC20 proxy smart contract to mainnet ([#97](https://github.com/RequestNetwork/requestNetwork/issues/97)) ([84a7d2a](https://github.com/RequestNetwork/requestNetwork/commit/84a7d2ae9c06a3c6e457c8583e44e8df01676b2a)) +* deploy ERC20 proxy smart contract to Rinkeby ([#95](https://github.com/RequestNetwork/requestNetwork/issues/95)) ([39e6a6a](https://github.com/RequestNetwork/requestNetwork/commit/39e6a6a0ea62fd4ee9e6343d03770711638b698b)) +* **request-client.js:** get balance from request ERC20 with proxy contract ([#94](https://github.com/RequestNetwork/requestNetwork/issues/94)) ([08758ae](https://github.com/RequestNetwork/requestNetwork/commit/08758ae83e3834db06c0f1441e51bc6c2b897669)) +* **smart-contracts:** add the erc20 proxy contract PN's smart contract ([#92](https://github.com/RequestNetwork/requestNetwork/issues/92)) ([30f7937](https://github.com/RequestNetwork/requestNetwork/commit/30f79374a78f1a060a91bc7e53e6dc44c2fbad2c)) diff --git a/packages/smart-contracts/Dockerfile b/packages/smart-contracts/Dockerfile new file mode 100644 index 0000000000..9678a02fba --- /dev/null +++ b/packages/smart-contracts/Dockerfile @@ -0,0 +1,24 @@ +FROM node:10-alpine as builder + +WORKDIR /app +COPY package.json . +RUN yarn + +COPY truffle-config.js . +COPY ./src ./src +RUN yarn build:sol + +FROM trufflesuite/ganache-cli as runtime +WORKDIR /app + +RUN apk add bash +RUN npm install -g truffle +RUN npm install -g node-wait-for-it + +COPY truffle-config.js . +COPY --from=builder "/app/build/contracts" "/app/build/contracts" +COPY ./docker/ . +COPY ./migrations ./migrations +RUN chmod +x ./entrypoint.sh + +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/packages/smart-contracts/README.md b/packages/smart-contracts/README.md new file mode 100644 index 0000000000..1df7f50f6c --- /dev/null +++ b/packages/smart-contracts/README.md @@ -0,0 +1,79 @@ +# @requestnetwork/smart-contracs + +`@requestnetwork/smart-contracts` is a package part of the [Request Network protocol](https://github.com/RequestNetwork/requestNetwork). +The package stores the sources and artifacts of the smart contracts deployed on Ethereum. It also exposes a library to get information about the artifacts. + +## Installation + +```bash +npm install @requestnetwork/smart-contracts +``` + +## Usage + +Library usage: + +```js +import * as SmartContracts from '@requestnetwork/smart-contracts'; + +const requestHashStorageMainnetAddress = SmartContracts.requestHashStorageArtifact.getAddress( + 'mainnet', +); + +const requestHashSubmitterRinkebyAddress = SmartContracts.requestHashSubmitterArtifact.getAddress( + 'rinkeby', +); + +const requestHashStorageABI = SmartContracts.requestHashStorageArtifact.getContractAbi(); +``` + +## Smart Contract + +The package stores the following smart contracts: + +**Smart contracts for ethereum-storage package** + +- `RequestHashStorage` allows to declare a hash `NewHash(hash, submitter, feesParameters)`. Only a whitelisted contract can declare hashes. +- `RequestOpenHashSubmitter` entry point to add hashes in `RequestHashStorage`. It gives the rules to get the right to submit hashes and collect the fees. This contract must be whitelisted in `RequestHashStorage`. The only condition for adding hash is to pay the fees. +- `StorageFeeCollector` parent contract (not deployed) of `RequestOpenHashSubmitter`, computes the fees and send them to the burner. + +**Smart contracts for advanced-logic package** + +- `TestERC20` minimal erc20 token used for tests. +- `ERC20Proxy` smart contract used by the erc20 proxy contract payment network to store payment references of erc20 transfers +- `EthereumProxy` smart contract used by the ethereum proxy contract payment network to store payment references of Ethereum transfers + +#### Smart contracts local deployment + +The smart contracts can be deployed locally with the following commands: + +```bash +git clone https://github.com/RequestNetwork/requestNetwork.git +cd requestNetwork/packages/smart-contracts +yarn install +yarn run build +yarn run ganache +``` + +And in another terminal: + +```bash +yarn run deploy +``` + +#### Configuring the provider using Truffle and the development network + +When deploying the smart contracts for development you can manually set the provider host and port via env variables: + +```bash +TRUFFLE_GANACHE_HOST="host" TRUFFLE_GANACHE_PORT=1010 yarn run deploy +``` + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. +[Read the contributing guide](/CONTRIBUTING.md) + +## License + +[MIT](/LICENSE) diff --git a/packages/smart-contracts/artifacts/ERC20Proxy/0.1.0.json b/packages/smart-contracts/artifacts/ERC20Proxy/0.1.0.json new file mode 100644 index 0000000000..8bb69de611 --- /dev/null +++ b/packages/smart-contracts/artifacts/ERC20Proxy/0.1.0.json @@ -0,0 +1,70 @@ +{ + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes", + "name": "paymentReference", + "type": "bytes" + } + ], + "name": "TransferWithReference", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_paymentReference", + "type": "bytes" + } + ], + "name": "transferFromWithReference", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ] +} diff --git a/packages/smart-contracts/artifacts/ERC20Proxy/artifacts.json b/packages/smart-contracts/artifacts/ERC20Proxy/artifacts.json new file mode 100644 index 0000000000..545d85ab1b --- /dev/null +++ b/packages/smart-contracts/artifacts/ERC20Proxy/artifacts.json @@ -0,0 +1,20 @@ +{ + "lastVersion": "0.1.0", + "0.1.0": { + "artifact": "0.1.0.json", + "deployment": { + "private": { + "address": "0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4", + "creationBlockNumber": 0 + }, + "mainnet": { + "address": "0x5f821c20947ff9be22e823edc5b3c709b33121b3", + "creationBlockNumber": 9119380 + }, + "rinkeby": { + "address": "0x162edb802fae75b9ee4288345735008ba51a4ec9", + "creationBlockNumber": 5628198 + } + } + } +} diff --git a/packages/smart-contracts/artifacts/EthereumProxy/0.1.0.json b/packages/smart-contracts/artifacts/EthereumProxy/0.1.0.json new file mode 100644 index 0000000000..5cab97e2d2 --- /dev/null +++ b/packages/smart-contracts/artifacts/EthereumProxy/0.1.0.json @@ -0,0 +1,54 @@ +{ + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes", + "name": "paymentReference", + "type": "bytes" + } + ], + "name": "TransferWithReference", + "type": "event" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address payable", + "name": "_to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_paymentReference", + "type": "bytes" + } + ], + "name": "transferWithReference", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + } + ] +} diff --git a/packages/smart-contracts/artifacts/EthereumProxy/artifacts.json b/packages/smart-contracts/artifacts/EthereumProxy/artifacts.json new file mode 100644 index 0000000000..aedf017a0c --- /dev/null +++ b/packages/smart-contracts/artifacts/EthereumProxy/artifacts.json @@ -0,0 +1,20 @@ +{ + "lastVersion": "0.1.0", + "0.1.0": { + "artifact": "0.1.0.json", + "deployment": { + "private": { + "address": "0xf204a4Ef082f5c04bB89F7D5E6568B796096735a", + "creationBlockNumber": 0 + }, + "mainnet": { + "address": "0x37a8f5f64f2a84f2377481537f04d2a59c9f59b6", + "creationBlockNumber": 9466832 + }, + "rinkeby": { + "address": "0x9c6c7817e3679c4b3f9ef9486001eae5aaed25ff", + "creationBlockNumber": 5955681 + } + } + } +} diff --git a/packages/ethereum-storage/artifacts/RequestHashStorage/0.1.0.json b/packages/smart-contracts/artifacts/RequestHashStorage/0.1.0.json similarity index 100% rename from packages/ethereum-storage/artifacts/RequestHashStorage/0.1.0.json rename to packages/smart-contracts/artifacts/RequestHashStorage/0.1.0.json diff --git a/packages/ethereum-storage/artifacts/RequestHashStorage/artifacts.json b/packages/smart-contracts/artifacts/RequestHashStorage/artifacts.json similarity index 100% rename from packages/ethereum-storage/artifacts/RequestHashStorage/artifacts.json rename to packages/smart-contracts/artifacts/RequestHashStorage/artifacts.json diff --git a/packages/ethereum-storage/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/0.1.0.json b/packages/smart-contracts/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/0.1.0.json similarity index 100% rename from packages/ethereum-storage/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/0.1.0.json rename to packages/smart-contracts/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/0.1.0.json diff --git a/packages/ethereum-storage/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json b/packages/smart-contracts/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json similarity index 100% rename from packages/ethereum-storage/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json rename to packages/smart-contracts/artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json diff --git a/packages/smart-contracts/docker/entrypoint.sh b/packages/smart-contracts/docker/entrypoint.sh new file mode 100644 index 0000000000..068a4072f1 --- /dev/null +++ b/packages/smart-contracts/docker/entrypoint.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +wait-for-it localhost:8545 -- truffle --contracts_directory=/app/build/contracts deploy & + +MNEMONIC=${MNEMONIC:="candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"} + +node /app/ganache-core.docker.cli.js -l 90000000 -m "$MNEMONIC" diff --git a/packages/ethereum-storage/migrations/1_initial_migration.js b/packages/smart-contracts/migrations/1_initial_migration.js similarity index 100% rename from packages/ethereum-storage/migrations/1_initial_migration.js rename to packages/smart-contracts/migrations/1_initial_migration.js diff --git a/packages/smart-contracts/migrations/2_deploy_contracts.js b/packages/smart-contracts/migrations/2_deploy_contracts.js new file mode 100644 index 0000000000..eeb7ab1152 --- /dev/null +++ b/packages/smart-contracts/migrations/2_deploy_contracts.js @@ -0,0 +1,67 @@ +const RequestHashStorage = artifacts.require('./RequestHashStorage.sol'); +const RequestOpenHashSubmitter = artifacts.require('./RequestOpenHashSubmitter.sol'); +const erc20 = artifacts.require('./TestERC20.sol'); +const ERC20Proxy = artifacts.require('./ERC20Proxy.sol'); +const EthereumProxy = artifacts.require('./EthereumProxy.sol'); + +const addressContractBurner = '0xfCb4393e7fAef06fAb01c00d67c1895545AfF3b8'; + +// Deploys, set up the contracts +module.exports = async function(deployer) { + try { + // Deploy the contract RequestHashStorage + await deployer.deploy(RequestHashStorage); + console.log('RequestHashStorage Contract deployed: ' + RequestHashStorage.address); + + // Deploy the contract RequestOpenHashSubmitter + await deployer.deploy( + RequestOpenHashSubmitter, + RequestHashStorage.address, + addressContractBurner, + ); + console.log('RequestOpenHashSubmitter Contract deployed: ' + RequestOpenHashSubmitter.address); + + // Whitelist the requestSubmitter in requestHashDeclaration + const instanceRequestHashStorage = await RequestHashStorage.deployed(); + await instanceRequestHashStorage.addWhitelisted(RequestOpenHashSubmitter.address); + console.log('requestSubmitter Whitelisted in requestHashDeclaration'); + + // Deploy the ERC20 contract + const instanceTestERC20 = await deployer.deploy(erc20, 1000); // 1000 initial supply + + // Deploy ERC20 proxy contract + const instanceRequestERC20Proxy = await deployer.deploy(ERC20Proxy); + console.log('ERC20Proxy Contract deployed: ' + ERC20Proxy.address); + + // create some events for test purpose + await instanceTestERC20.approve(ERC20Proxy.address, 110); + await instanceRequestERC20Proxy.transferFromWithReference( + instanceTestERC20.address, + '0x6330A553Fc93768F612722BB8c2eC78aC90B3bbc', + 100, + '0x7157f6ce9085a520', + ); + await instanceRequestERC20Proxy.transferFromWithReference( + instanceTestERC20.address, + '0x5AEDA56215b167893e80B4fE645BA6d5Bab767DE', + 10, + '0xdeea051f2e9120e0', + ); + // ---------------------------------- + + // Deploy Ethereym proxy contract + const instanceRequestEthereumProxy = await deployer.deploy(EthereumProxy); + console.log('EthereumProxy Contract deployed: ' + EthereumProxy.address); + + console.log('Contracts initialized'); + console.log(` + RequestHashStorage: ${RequestHashStorage.address} + RequestOpenHashSubmitter: ${RequestOpenHashSubmitter.address} + TestERC20: ${erc20.address} + ERC20Proxy: ${ERC20Proxy.address} + EthereumProxy: ${EthereumProxy.address} + `); + } catch (e) { + console.error(e); + } +}; diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json new file mode 100644 index 0000000000..87cb0d845e --- /dev/null +++ b/packages/smart-contracts/package.json @@ -0,0 +1,57 @@ +{ + "name": "@requestnetwork/smart-contracts", + "version": "0.9.0", + "publishConfig": { + "access": "public" + }, + "description": "Smart contracts for the Request protocol.", + "keywords": [ + "requestnetwork", + "smart-contracts" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RequestNetwork/requestNetwork.git" + }, + "homepage": "https://github.com/RequestNetwork/requestNetwork/tree/master/packages/smart-contract#readme", + "bugs": { + "url": "https://github.com/RequestNetwork/requestNetwork/issues" + }, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + }, + "main": "dist/src/lib/index.js", + "types": "dist/src/lib/index.d.ts", + "directories": { + "lib": "src/lib", + "test": "test/lib" + }, + "files": [ + "dist" + ], + "scripts": { + "build:lib": "tsc -b", + "build:sol": "truffle compile --contracts_directory=./src", + "build": "yarn run build:lib && yarn run build:sol", + "clean:lib": "shx rm -rf dist", + "clean:sol": "shx rm -rf build", + "clean": "yarn run clean:lib && yarn run clean:sol", + "lint:lib": "tslint --project . && eslint \"src/lib/**/*.ts\"", + "lint:sol": "solium --dir src/contracts", + "lint": "yarn run lint:lib && yarn run lint:sol", + "ganache": "ganache-cli -l 90000000 -p 8545 -m \"candy maple cake sugar pudding cream honey rich smooth crumble sweet treat\"", + "deploy": "truffle --contracts_directory=./src deploy", + "test": "truffle test --contracts_directory=./src test/contracts/*.js" + }, + "devDependencies": { + "@openzeppelin/contracts": "2.4.0", + "ethers": "4.0.45", + "ganache-cli": "6.7.0", + "lint-staged": "9.5.0", + "openzeppelin-solidity": "2.1.2", + "openzeppelin-test-helpers": "0.1.2", + "shx": "0.3.2", + "truffle": "5.1.2" + } +} diff --git a/packages/ethereum-storage/src/contracts/Bytes.sol b/packages/smart-contracts/src/contracts/Bytes.sol similarity index 100% rename from packages/ethereum-storage/src/contracts/Bytes.sol rename to packages/smart-contracts/src/contracts/Bytes.sol diff --git a/packages/smart-contracts/src/contracts/ERC20Proxy.sol b/packages/smart-contracts/src/contracts/ERC20Proxy.sol new file mode 100644 index 0000000000..c981114c04 --- /dev/null +++ b/packages/smart-contracts/src/contracts/ERC20Proxy.sol @@ -0,0 +1,51 @@ +pragma solidity ^0.5.0; + +import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + + +/** + * @title ERC20Proxy + * @notice This contract performs an ERC20 token transfer and stores a reference + */ +contract ERC20Proxy { + // Event to declare a transfer with a reference + event TransferWithReference( + address tokenAddress, + address to, + uint256 amount, + bytes indexed paymentReference + ); + + // Fallback function returns funds to the sender + function() + external + payable + { + revert("not payable fallback"); + } + + /** + * @notice Performs a ERC20 token transfer with a reference + * @param _tokenAddress Address of the ERC20 token smart contract + * @param _to Transfer recipient + * @param _amount Amount to transfer + * @param _paymentReference Reference of the payment related + */ + function transferFromWithReference( + address _tokenAddress, + address _to, + uint256 _amount, + bytes calldata _paymentReference + ) + external + { + ERC20 erc20 = ERC20(_tokenAddress); + require(erc20.transferFrom(msg.sender, _to, _amount), "transferFrom() failed"); + emit TransferWithReference( + _tokenAddress, + _to, + _amount, + _paymentReference + ); + } +} diff --git a/packages/smart-contracts/src/contracts/EthereumProxy.sol b/packages/smart-contracts/src/contracts/EthereumProxy.sol new file mode 100644 index 0000000000..d9b4abdb2c --- /dev/null +++ b/packages/smart-contracts/src/contracts/EthereumProxy.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.5.0; + + +/** + * @title EthereumProxy + * @notice This contract performs an Ethereum transfer and stores a reference + */ +contract EthereumProxy { + // Event to declare a transfer with a reference + event TransferWithReference(address to, uint256 amount, bytes indexed paymentReference); + + // Fallback function returns funds to the sender + function() external payable { + revert("not payable fallback"); + } + + /** + * @notice Performs an Ethereum transfer with a reference + * @param _to Transfer recipient + * @param _paymentReference Reference of the payment related + */ + function transferWithReference(address payable _to, bytes calldata _paymentReference) + external + payable + { + _to.transfer(msg.value); + emit TransferWithReference(_to, msg.value, _paymentReference); + } +} diff --git a/packages/ethereum-storage/src/contracts/Migrations.sol b/packages/smart-contracts/src/contracts/Migrations.sol similarity index 100% rename from packages/ethereum-storage/src/contracts/Migrations.sol rename to packages/smart-contracts/src/contracts/Migrations.sol diff --git a/packages/ethereum-storage/src/contracts/RequestHashStorage.sol b/packages/smart-contracts/src/contracts/RequestHashStorage.sol similarity index 100% rename from packages/ethereum-storage/src/contracts/RequestHashStorage.sol rename to packages/smart-contracts/src/contracts/RequestHashStorage.sol diff --git a/packages/ethereum-storage/src/contracts/RequestOpenHashSubmitter.sol b/packages/smart-contracts/src/contracts/RequestOpenHashSubmitter.sol similarity index 100% rename from packages/ethereum-storage/src/contracts/RequestOpenHashSubmitter.sol rename to packages/smart-contracts/src/contracts/RequestOpenHashSubmitter.sol diff --git a/packages/ethereum-storage/src/contracts/StorageFeeCollector.sol b/packages/smart-contracts/src/contracts/StorageFeeCollector.sol similarity index 100% rename from packages/ethereum-storage/src/contracts/StorageFeeCollector.sol rename to packages/smart-contracts/src/contracts/StorageFeeCollector.sol diff --git a/packages/ethereum-storage/src/contracts/testERC20.sol b/packages/smart-contracts/src/contracts/TestERC20.sol similarity index 99% rename from packages/ethereum-storage/src/contracts/testERC20.sol rename to packages/smart-contracts/src/contracts/TestERC20.sol index 5f82132592..97d227d247 100644 --- a/packages/ethereum-storage/src/contracts/testERC20.sol +++ b/packages/smart-contracts/src/contracts/TestERC20.sol @@ -3,11 +3,12 @@ pragma solidity ^0.5.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol"; + /** * @title TestERC20 * * @notice TestERC20 is a contract to test ERC20 detection - */ +*/ contract TestERC20 is ERC20, ERC20Detailed { constructor(uint256 initialSupply) ERC20Detailed("ERC 20", "ERC20", 18) public { _mint(msg.sender, initialSupply); diff --git a/packages/ethereum-storage/src/contracts/test/BytesUtilsMock.sol b/packages/smart-contracts/src/contracts/test/BytesUtilsMock.sol similarity index 100% rename from packages/ethereum-storage/src/contracts/test/BytesUtilsMock.sol rename to packages/smart-contracts/src/contracts/test/BytesUtilsMock.sol diff --git a/packages/smart-contracts/src/lib/erc20ProxyArtifact.ts b/packages/smart-contracts/src/lib/erc20ProxyArtifact.ts new file mode 100644 index 0000000000..475d682609 --- /dev/null +++ b/packages/smart-contracts/src/lib/erc20ProxyArtifact.ts @@ -0,0 +1,61 @@ +const artifactsERC20Proxy = require('../../artifacts/ERC20Proxy/artifacts.json'); +const ARTIFACTS_VERSION: string = artifactsERC20Proxy.lastVersion; + +/** + * Retrieve the abi from the artifact of the used version + * @returns the abi of the artifact as a json object + */ +export function getContractAbi(): any { + const artifactFilename: string = artifactsERC20Proxy[ARTIFACTS_VERSION].artifact; + + const artifact = require(`../../artifacts/ERC20Proxy/${artifactFilename.replace( + /\.[^/.]+$/, + '', + )}.json`); + + // Check the abi exists inside the artifact file + if (!artifact.abi) { + throw Error(`No abi in artifact ${artifactFilename}`); + } + + return artifact.abi; +} + +/** + * Retrieve the address from the artifact of the used version + * deployed into the specified network + * @param networkName the name of the network where the contract is deployed + * @returns the address of the deployed contract + */ +export function getAddress(networkName: string): string { + return getDeploymentInformation(networkName).address; +} + +/** + * Retrieve the block creation number from the artifact of the used version + * deployed into the specified network + * @param networkName the name of the network where the contract is deployed + * @returns the number of the block where the contract was deployed + */ +export function getCreationBlockNumber(networkName: string): number { + return getDeploymentInformation(networkName).creationBlockNumber; +} + +/** + * Retrieve the deployment information from the artifact of the used version + * deployed into the specified network + * @param networkName the name of the network where the contract is deployed + * @returns the deployment information of the contract as a json object containing address and the number of the creation block + */ +function getDeploymentInformation( + networkName: string, +): { address: string; creationBlockNumber: number } { + const deploymentInformation = artifactsERC20Proxy[ARTIFACTS_VERSION].deployment[networkName]; + + // Check the artifact has been deployed into the specified network + if (!deploymentInformation) { + throw Error(`No deployment for network: ${networkName}`); + } + + return deploymentInformation; +} diff --git a/packages/smart-contracts/src/lib/ethereumProxyArtifact.ts b/packages/smart-contracts/src/lib/ethereumProxyArtifact.ts new file mode 100644 index 0000000000..7e8fc9cdcc --- /dev/null +++ b/packages/smart-contracts/src/lib/ethereumProxyArtifact.ts @@ -0,0 +1,74 @@ +const artifactsEthereumProxy = require('../../artifacts/EthereumProxy/artifacts.json'); +const ARTIFACTS_VERSION: string = artifactsEthereumProxy.lastVersion; + +/** + * Retrieve the abi from the artifact of the used version + * @param version version of the contract + * @returns the abi of the artifact as a json object + */ +export function getContractAbi(version?: string): any { + const artifactObject: any = artifactsEthereumProxy[version || ARTIFACTS_VERSION]; + if (!artifactObject) { + throw Error(`No artifact found for version ${version || ARTIFACTS_VERSION}`); + } + const artifactFilename: string = artifactObject.artifact; + + const artifact = require(`../../artifacts/EthereumProxy/${artifactFilename.replace( + /\.[^/.]+$/, + '', + )}.json`); + + // Check the abi exists inside the artifact file + if (!artifact.abi) { + throw Error(`No abi in artifact ${artifactFilename}`); + } + + return artifact.abi; +} + +/** + * Retrieve the address from the artifact of the used version + * deployed into the specified network + * @param networkName the name of the network where the contract is deployed + * @param version version of the contract + * @returns the address of the deployed contract + */ +export function getAddress(networkName: string, version?: string): string { + return getDeploymentInformation(networkName, version).address; +} + +/** + * Retrieve the block creation number from the artifact of the used version + * deployed into the specified network + * @param networkName the name of the network where the contract is deployed + * @param version version of the contract + * @returns the number of the block where the contract was deployed + */ +export function getCreationBlockNumber(networkName: string, version?: string): number { + return getDeploymentInformation(networkName, version).creationBlockNumber; +} + +/** + * Retrieve the deployment information from the artifact of the used version + * deployed into the specified network + * @param networkName the name of the network where the contract is deployed + * @param version version of the contract + * @returns the deployment information of the contract as a json object containing address and the number of the creation block + */ +function getDeploymentInformation( + networkName: string, + version?: string, +): { address: string; creationBlockNumber: number } { + const artifactObject: any = artifactsEthereumProxy[version || ARTIFACTS_VERSION]; + if (!artifactObject) { + throw Error(`No artifact found for version ${version || ARTIFACTS_VERSION}`); + } + const deploymentInformation = artifactObject.deployment[networkName]; + + // Check the artifact has been deployed into the specified network + if (!deploymentInformation) { + throw Error(`No deployment for network: ${networkName}`); + } + + return deploymentInformation; +} diff --git a/packages/smart-contracts/src/lib/index.ts b/packages/smart-contracts/src/lib/index.ts new file mode 100644 index 0000000000..e0e585c024 --- /dev/null +++ b/packages/smart-contracts/src/lib/index.ts @@ -0,0 +1,11 @@ +import * as erc20ProxyArtifact from './erc20ProxyArtifact'; +import * as ethereumProxyArtifact from './ethereumProxyArtifact'; +import * as requestHashStorageArtifact from './requestHashStorageArtifact'; +import * as requestHashSubmitterArtifact from './requestHashSubmitterArtifact'; + +export { + ethereumProxyArtifact, + erc20ProxyArtifact, + requestHashStorageArtifact, + requestHashSubmitterArtifact, +}; diff --git a/packages/ethereum-storage/src/lib/artifacts-request-hash-storage-utils.ts b/packages/smart-contracts/src/lib/requestHashStorageArtifact.ts similarity index 93% rename from packages/ethereum-storage/src/lib/artifacts-request-hash-storage-utils.ts rename to packages/smart-contracts/src/lib/requestHashStorageArtifact.ts index f3a0d242f1..027a24c486 100644 --- a/packages/ethereum-storage/src/lib/artifacts-request-hash-storage-utils.ts +++ b/packages/smart-contracts/src/lib/requestHashStorageArtifact.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; const artifacts = require('../../artifacts/RequestHashStorage/artifacts.json'); const ARTIFACTS_VERSION: string = artifacts.lastVersion; @@ -8,7 +7,11 @@ const ARTIFACTS_VERSION: string = artifacts.lastVersion; */ export function getContractAbi(): any { const artifactFilename: string = artifacts[ARTIFACTS_VERSION].artifact; - const artifact = require(path.join('../../artifacts/RequestHashStorage/', artifactFilename)); + + const artifact = require(`../../artifacts/RequestHashStorage/${artifactFilename.replace( + /\.[^/.]+$/, + '', + )}.json`); // Check the abi exists inside the artifact file if (!artifact.abi) { diff --git a/packages/ethereum-storage/src/lib/artifacts-request-hash-submitter-utils.ts b/packages/smart-contracts/src/lib/requestHashSubmitterArtifact.ts similarity index 92% rename from packages/ethereum-storage/src/lib/artifacts-request-hash-submitter-utils.ts rename to packages/smart-contracts/src/lib/requestHashSubmitterArtifact.ts index 9c503102c1..b19ba884f5 100644 --- a/packages/ethereum-storage/src/lib/artifacts-request-hash-submitter-utils.ts +++ b/packages/smart-contracts/src/lib/requestHashSubmitterArtifact.ts @@ -1,5 +1,3 @@ -import * as path from 'path'; - const artifactsRequestOpenHashSubmitter = require('../../artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json'); const ARTIFACTS_OPEN_STORAGE_LAST_VERSION: string = artifactsRequestOpenHashSubmitter.lastVersion; @@ -11,10 +9,10 @@ export function getContractAbi(): any { const artifactFilename: string = artifactsRequestOpenHashSubmitter[ARTIFACTS_OPEN_STORAGE_LAST_VERSION].artifact; - const artifact = require(path.join( - '../../artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/', - artifactFilename, - )); + const artifact = require(`../../artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/${artifactFilename.replace( + /\.[^/.]+$/, + '', + )}.json`); // Check the abi exists inside the artifact file if (!artifact.abi) { diff --git a/packages/smart-contracts/test/contracts/ERC20Proxy.js b/packages/smart-contracts/test/contracts/ERC20Proxy.js new file mode 100644 index 0000000000..b7c8fdd51e --- /dev/null +++ b/packages/smart-contracts/test/contracts/ERC20Proxy.js @@ -0,0 +1,69 @@ +const ethers = require('ethers'); + +const { expectEvent, shouldFail } = require('openzeppelin-test-helpers'); +const ERC20Proxy = artifacts.require('./ERC20Proxy.sol'); +const TestERC20 = artifacts.require('./TestERC20.sol'); + +contract('ERC20Proxy', function(accounts) { + const from = accounts[0]; + const to = accounts[1]; + const otherGuy = accounts[2]; + let erc20Proxy; + let testERC20; + const referenceExample = '0xaaaa'; + + beforeEach(async () => { + testERC20 = await TestERC20.new(1000, { + from, + }); + erc20Proxy = await ERC20Proxy.new({ + from, + }); + }); + + it('allows to store a reference', async function() { + await testERC20.approve(erc20Proxy.address, '100', { from }); + + let { logs } = await erc20Proxy.transferFromWithReference( + testERC20.address, + to, + '100', + referenceExample, + { from }, + ); + + // transferReference indexes the event log, therefore the keccak256 is stored + expectEvent.inLogs(logs, 'TransferWithReference', { + tokenAddress: testERC20.address, + to, + amount: '100', + paymentReference: ethers.utils.keccak256(referenceExample), + }); + }); + + it('allows to transfer tokens', async function() { + await testERC20.approve(erc20Proxy.address, '100', { from }); + + const fromOldBalance = await testERC20.balanceOf(from); + const toOldBalance = await testERC20.balanceOf(to); + + await erc20Proxy.transferFromWithReference(testERC20.address, to, '100', referenceExample, { + from, + }); + + const fromNewBalance = await testERC20.balanceOf(from); + const toNewBalance = await testERC20.balanceOf(to); + + // Check balance changes + expect(fromNewBalance.toNumber()).to.equals(fromOldBalance.toNumber() - 100); + expect(toNewBalance.toNumber()).to.equals(toOldBalance.toNumber() + 100); + }); + + it('should revert if no fund', async function() { + await shouldFail.reverting( + erc20Proxy.transferFromWithReference(testERC20.address, to, '100', referenceExample, { + from: otherGuy, + }), + ); + }); +}); diff --git a/packages/smart-contracts/test/contracts/EthereumProxy.js b/packages/smart-contracts/test/contracts/EthereumProxy.js new file mode 100644 index 0000000000..2792d210df --- /dev/null +++ b/packages/smart-contracts/test/contracts/EthereumProxy.js @@ -0,0 +1,60 @@ +const ethers = require('ethers'); + +const { expectEvent, shouldFail } = require('openzeppelin-test-helpers'); +const EthereumProxy = artifacts.require('./EthereumProxy.sol'); + +contract('EthereumProxy', function(accounts) { + const from = accounts[0]; + const to = accounts[1]; + const otherGuy = accounts[2]; + let ethProxy; + const referenceExample = '0xaaaa'; + const DEFAULT_GAS_PRICE = new ethers.utils.BigNumber('100000000000'); + const amount = new ethers.utils.BigNumber('10000000000000000'); + const provider = new ethers.providers.JsonRpcProvider(); + + beforeEach(async () => { + ethProxy = await EthereumProxy.new({ + from, + }); + }); + + it('allows to store a reference', async function() { + let { logs } = await ethProxy.transferWithReference(to, referenceExample, { + from, + value: amount, + }); + + // transferReference indexes the event log, therefore the keccak256 is stored + expectEvent.inLogs(logs, 'TransferWithReference', { + to, + amount: amount.toString(), + paymentReference: ethers.utils.keccak256(referenceExample), + }); + }); + + it('allows to transfer ethers', async function() { + const fromOldBalance = await provider.getBalance(from); + const toOldBalance = await provider.getBalance(to); + + const tx = await ethProxy.transferWithReference(to, referenceExample, { + from, + value: amount, + gasPrice: DEFAULT_GAS_PRICE, + }); + + const gasCost = DEFAULT_GAS_PRICE.mul(new ethers.utils.BigNumber(tx.receipt.gasUsed)); + + const fromNewBalance = await provider.getBalance(from); + const toNewBalance = await provider.getBalance(to); + + // Check balance changes + expect(fromNewBalance.toString()).to.equals( + fromOldBalance + .sub(gasCost) + .sub(amount) + .toString(), + ); + expect(toNewBalance.toString()).to.equals(toOldBalance.add(amount).toString()); + }); +}); diff --git a/packages/ethereum-storage/test/contracts/RequestHashStorage.js b/packages/smart-contracts/test/contracts/RequestHashStorage.js similarity index 98% rename from packages/ethereum-storage/test/contracts/RequestHashStorage.js rename to packages/smart-contracts/test/contracts/RequestHashStorage.js index 39bccec8c1..2784c0a09a 100644 --- a/packages/ethereum-storage/test/contracts/RequestHashStorage.js +++ b/packages/smart-contracts/test/contracts/RequestHashStorage.js @@ -1,4 +1,3 @@ -const BigNumber = require('bn.js'); const { expectEvent, shouldFail } = require('openzeppelin-test-helpers'); const RequestHashStorage = artifacts.require('./RequestHashStorage.sol'); diff --git a/packages/ethereum-storage/test/contracts/RequestOpenHashSubmitter.js b/packages/smart-contracts/test/contracts/RequestOpenHashSubmitter.js similarity index 100% rename from packages/ethereum-storage/test/contracts/RequestOpenHashSubmitter.js rename to packages/smart-contracts/test/contracts/RequestOpenHashSubmitter.js diff --git a/packages/ethereum-storage/test/contracts/StorageFeeCollector.js b/packages/smart-contracts/test/contracts/StorageFeeCollector.js similarity index 100% rename from packages/ethereum-storage/test/contracts/StorageFeeCollector.js rename to packages/smart-contracts/test/contracts/StorageFeeCollector.js diff --git a/packages/ethereum-storage/test/contracts/bytes-test.js b/packages/smart-contracts/test/contracts/bytes-test.js similarity index 100% rename from packages/ethereum-storage/test/contracts/bytes-test.js rename to packages/smart-contracts/test/contracts/bytes-test.js diff --git a/packages/ethereum-storage/test/contracts/utils.js b/packages/smart-contracts/test/contracts/utils.js similarity index 100% rename from packages/ethereum-storage/test/contracts/utils.js rename to packages/smart-contracts/test/contracts/utils.js diff --git a/packages/ethereum-storage/truffle-config.js b/packages/smart-contracts/truffle-config.js similarity index 100% rename from packages/ethereum-storage/truffle-config.js rename to packages/smart-contracts/truffle-config.js diff --git a/packages/smart-contracts/tsconfig.json b/packages/smart-contracts/tsconfig.json new file mode 100644 index 0000000000..33d3fb47ac --- /dev/null +++ b/packages/smart-contracts/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": ".", + "resolveJsonModule": true + }, + "include": ["./src/**/*", "./artifacts/**/*"], + "files": [ + "./artifacts/ERC20Proxy/artifacts.json", + "./artifacts/ERC20Proxy/0.1.0.json", + "./artifacts/RequestHashStorage/artifacts.json", + "./artifacts/RequestHashStorage/0.1.0.json", + "./artifacts/RequestHashStorage/artifacts.json", + "./artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/0.1.0.json", + "./artifacts/RequestHashSubmitter/RequestOpenHashSubmitter/artifacts.json", + "./artifacts/EthereumProxy/artifacts.json", + "./artifacts/EthereumProxy/0.1.0.json" + ] +} diff --git a/packages/smart-contracts/tslint.json b/packages/smart-contracts/tslint.json new file mode 100644 index 0000000000..86908345b4 --- /dev/null +++ b/packages/smart-contracts/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tslint.json", + "linterOptions": { + "exclude": ["artifacts/**/*"] + } +} diff --git a/packages/toolbox/.vscode/settings.json b/packages/toolbox/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/toolbox/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/toolbox/CHANGELOG.md b/packages/toolbox/CHANGELOG.md index 67edc88ab7..3491d1399f 100644 --- a/packages/toolbox/CHANGELOG.md +++ b/packages/toolbox/CHANGELOG.md @@ -3,6 +3,192 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.1.12](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.12) (2020-06-29) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.1.11](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.11) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.1.10](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.10) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.1.9](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.9) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.1.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.8) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.1.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.7) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.1.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.6) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/toolbox + + + + + +## [0.1.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.5) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/toolbox + + + + + +## [0.1.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.3...@requestnetwork/toolbox@0.1.4) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/toolbox + + + + + ## [0.1.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/toolbox@0.1.2...@requestnetwork/toolbox@0.1.3) (2019-11-20) **Note:** Version bump only for package @requestnetwork/toolbox diff --git a/packages/toolbox/package.json b/packages/toolbox/package.json index 09dd8c9385..d6d0cec005 100644 --- a/packages/toolbox/package.json +++ b/packages/toolbox/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/toolbox", - "version": "0.1.3", + "version": "0.1.12", "private": true, "description": "Toolbox for Request Network.", "keywords": [ @@ -32,28 +32,29 @@ }, "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "prepare": "yarn run build", "cli": "cross-env NODE_ENV=development ts-node src/cli.ts" }, "dependencies": { - "@requestnetwork/epk-signature": "0.5.4", - "@requestnetwork/request-client.js": "0.9.0", - "@requestnetwork/types": "0.9.0", - "axios": "0.18.1", + "@requestnetwork/epk-signature": "0.5.13", + "@requestnetwork/request-client.js": "0.18.0", + "@requestnetwork/types": "0.17.0", + "axios": "0.19.0", "yargs": "14.0.0" }, "devDependencies": { "cross-env": "5.2.1", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "husky": "2.4.0", "lint-staged": "8.1.3", "prettier": "1.16.4", "shx": "0.3.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" } diff --git a/packages/transaction-manager/.vscode/settings.json b/packages/transaction-manager/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/transaction-manager/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/transaction-manager/CHANGELOG.md b/packages/transaction-manager/CHANGELOG.md index b59b4ea3a8..8f3ed6ab67 100644 --- a/packages/transaction-manager/CHANGELOG.md +++ b/packages/transaction-manager/CHANGELOG.md @@ -3,6 +3,272 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.14.0) (2020-06-29) + + +### Features + +* remove hash in encrypted transaction ([#232](https://github.com/RequestNetwork/requestNetwork/issues/232)) ([d58f101](https://github.com/RequestNetwork/requestNetwork/commit/d58f101f9f76e408671dd1edb0d67863d1c8abd5)) +* replace symmetric encryption algorithm by aes-256-gcm ([#233](https://github.com/RequestNetwork/requestNetwork/issues/233)) ([969bebe](https://github.com/RequestNetwork/requestNetwork/commit/969bebeb99b4bc2fdd31405a162934cfdff6db05)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.13.0) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.12.0) (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.11.0) (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.10.0) (2020-03-23) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.9.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.8.4](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.8.4) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/transaction-manager + + + + + +## [0.8.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.8.3) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/transaction-manager + + + + + +## [0.8.2](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.1...@requestnetwork/transaction-manager@0.8.2) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/transaction-manager + + + + + ## [0.8.1](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/transaction-manager@0.8.0...@requestnetwork/transaction-manager@0.8.1) (2019-11-20) **Note:** Version bump only for package @requestnetwork/transaction-manager diff --git a/packages/transaction-manager/package.json b/packages/transaction-manager/package.json index 5c8500fbe6..00950d1ff3 100644 --- a/packages/transaction-manager/package.json +++ b/packages/transaction-manager/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/transaction-manager", - "version": "0.8.1", + "version": "0.14.0", "publishConfig": { "access": "public" }, @@ -32,35 +32,35 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0" + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0" }, "devDependencies": { "@types/chai": "4.1.7", "@types/chai-spies": "1.0.0", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", "source-map-support": "0.5.13", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" }, diff --git a/packages/transaction-manager/specs/encryption.md b/packages/transaction-manager/specs/encryption.md index 7d72147534..95c74ca704 100644 --- a/packages/transaction-manager/specs/encryption.md +++ b/packages/transaction-manager/specs/encryption.md @@ -19,6 +19,23 @@ A "normalized Keccak256 hash" of a JSON object is Keccak256 hash of an object wh - the properties have been sorted alphabetically - all the values and properties have been lowercased +## Note on multiformatted identities + +In this layer, we refer to identities as the encryption key pairs, which can be different from the payer or payee identities (from the RequestLogic). + +Formatted identities allow typed identities such as: +``` +{ + type: "EthereumAddress", + value: "0x123" +} +``` +to be serialized. The value is prefixed with 2 digits representing the identity type. We use this to lighten the storage. +The example above becomes: `20123`. + +Cf. [multi-format-types.ts](./packages/types/src/multi-format-types.ts) + + ## Requirement The implementation must use a cryptographically strong random number generator method. @@ -37,12 +54,12 @@ Parties can be added on an encrypted channel by any other parties. The encryption uses: - asymmetric Elliptic Curve Integrated Encryption Scheme (ECIES) -- symmetric AES-256-CBC encryption. +- symmetric AES-256-GCM encryption. The data are first encrypted by AES-256 with a generated key. This key is then encrypted for every other party with ECIES from their public key. To not expose the public keys, the encrypted keys are indexed by the addresses. -The encrypted data, the encrypted keys and a hash of the data are pushed on chain. +The encrypted data, the encrypted keys and the encrypted method of the data are pushed on chain. ## Functions @@ -50,10 +67,13 @@ The encrypted data, the encrypted keys and a hash of the data are pushed on chai | Property | Type | Description | | -------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------- | -| **data** | String | First encrypted data of the channel in base64 | +| **encryptedData** | String | First encrypted data of the channel in base64 | +<<<<<<< HEAD +======= | **hash** | String | Normalized Keccak256 hash of the message before encryption | +>>>>>>> master | **keys** | Object | AES-256 key encrypted with ECIES from the parties public keys, encoded in base64 and indexed by their multi-formatted identities | -| **encryptionMethod** | String | Encryption method use for the channel _('ECIES-AES256' here)_ | +| **encryptionMethod** | String | Encryption method use for the channel _('ECIES-AES256-GCM' here)_ | The data are encrypted with the algorithm AES-256 from the channel key generated for this channel. The channel key generation must be cryptographically strong. @@ -63,30 +83,34 @@ Example: ```JSON { - "data": "JOz9aOV1aYatMSAx+3CD9EyjNI/FwLp6DeA+AYk5ERnTDwwaETY7zz2NemubnGW7GGDATjSVsnCVWXuM58cihq1Bhkon2aiPHhQdpteEugkrM2Zx/kWrVlvRY8kyseB30hU7NhyiDUSLGOJ/Pmq3PjANbBi2svgADLFZ6SdYgwFkjxaO1XkvW/qvjuraFqW55/4wCd53yjWcjMcLzMgLYcTLmSns642xAjx0hAvwVPQmTVI5xOFf6PjbEN9qfRPfdQkaOuuGG2AYsVhOkSK73BULdIvx6PArfqICCtL23xmt4kCeFgd6HYQvSzSFqszqAWT1kJdiRj3sZXRtf6xcpeXDelBacHN+xD2mzdZlroVhlsjZi5s0mhemBd+C", - "hash": "01865ea95812388a93162b560e01c5680f12966492dfbad8a9a104e1e79f6665fc", + "encryptedData": "JOz9aOV1aYatMSAx+3CD9EyjNI/FwLp6DeA+AYk5ERnTDwwaETY7zz2NemubnGW7GGDATjSVsnCVWXuM58cihq1Bhkon2aiPHhQdpteEugkrM2Zx/kWrVlvRY8kyseB30hU7NhyiDUSLGOJ/Pmq3PjANbBi2svgADLFZ6SdYgwFkjxaO1XkvW/qvjuraFqW55/4wCd53yjWcjMcLzMgLYcTLmSns642xAjx0hAvwVPQmTVI5xOFf6PjbEN9qfRPfdQkaOuuGG2AYsVhOkSK73BULdIvx6PArfqICCtL23xmt4kCeFgd6HYQvSzSFqszqAWT1kJdiRj3sZXRtf6xcpeXDelBacHN+xD2mzdZlroVhlsjZi5s0mhemBd+C", "keys": { "20af083f77f1ffd54218d91491afd06c9296eac3ce": "aYOGYgtlt0JkBoKjxkMpoQJbE7GXtTT6JrjA+NF0Bd6BxDLyn5+hFIDvHltMkGS7rpzR3RyEnDl+SncDJ+cCxLo9Od7ntqGNVdin6n7EJqilmY0AmxJpAIAOnCwK5C46zH4RE0g7vBv/+3Gx2uFKw2Dfhpy7olQ5NL6Krsb2qEnmW32R3wmv85uCE88uxmcDlo/OrS36X+jzOye+/ZR+kOE=", "20740fc87bd3f41d07d23a01dec90623ebc5fed9d6": "AKJaJONWml2moKwTGZCuXQMxBt014+6Sxo2rzXYBbgKV8peBo3RM6KrxvhIdnCtTwxu3CrlFrkfUm6VYoMsKPu5WhZMU1Wk2R+vYl7roJFCQsTqTN1Qkx0skBLhaSKwynzZY3BWyTZ5rf1+JPmi7g6fGB9VOUpv6EDlp9k1p2RZnsVc+fMYKMAWhMnSZ3gJQUVbHY2Jx0CiQX/N+PtpnTWM=", }, - "encryptionMethod": "ECIES-AES256-CBC" + "encryptionMethod": "ECIES-AES256-GCM" } ``` ### Add encrypted data -| Property | Type | Description | -| -------- | ------ | ---------------------------------------------------------- | -| **data** | String | Encrypted data in base64 | -| **hash** | String | Normalized Keccak256 hash of the message before encryption | +<<<<<<< HEAD +| Property | Type | Description | +| ----------------- | ------ | ------------------------ | +| **encryptedData** | String | Encrypted data in base64 | +======= +| Property | Type | Description | +| ----------------- | ------ | ---------------------------------------------------------- | +| **encryptedData** | String | Encrypted data in base64 | +| **hash** | String | Normalized Keccak256 hash of the message before encryption | +>>>>>>> master Example: ```JSON { - "data": "mBVy2ENb0Edkego5c9QXcFxszKxe7iQVE22wUPHMbrC7bBm99S238BAyACa1TBDlI4SajbrWM+/MG8CkBoph4FLTvh4PsUjhnfazFI9BnMtIMhdqDAoxXUSHsvnwbEFhllqwhFCWn6pslLNu7X7UJSDgj7nQ0t1IHegBSV7ZRqdOYw3UoxAEAyVOoUwMhr/sitF2AlgMSvKas5YCD47YIm6rDNmzyBn9Ed/fAxNojMXcg386khrPs37P6Q==", - "hash": "018f94ee7e96fa65a761e8df9792af3f72fcf936f186fbb86881630f7d5333c8bb", + "encryptedData": "mBVy2ENb0Edkego5c9QXcFxszKxe7iQVE22wUPHMbrC7bBm99S238BAyACa1TBDlI4SajbrWM+/MG8CkBoph4FLTvh4PsUjhnfazFI9BnMtIMhdqDAoxXUSHsvnwbEFhllqwhFCWn6pslLNu7X7UJSDgj7nQ0t1IHegBSV7ZRqdOYw3UoxAEAyVOoUwMhr/sitF2AlgMSvKas5YCD47YIm6rDNmzyBn9Ed/fAxNojMXcg386khrPs37P6Q==", } ``` diff --git a/packages/transaction-manager/src/channel-parser.ts b/packages/transaction-manager/src/channel-parser.ts index 3fbfac1694..6cda8b64e5 100644 --- a/packages/transaction-manager/src/channel-parser.ts +++ b/packages/transaction-manager/src/channel-parser.ts @@ -20,10 +20,10 @@ export default class ChannelParser { */ public async decryptAndCleanChannel( channelId: string, - transactions: TransactionTypes.IConfirmedTransaction[], + transactions: TransactionTypes.ITimestampedTransaction[], ): Promise<{ encryptionMethod: string | undefined; - transactions: Array; + transactions: Array; ignoredTransactions: Array; }> { let channelType: TransactionTypes.ChannelType = TransactionTypes.ChannelType.UNKNOWN; @@ -31,7 +31,7 @@ export default class ChannelParser { let encryptionMethod: string | undefined; interface IValidAndIgnoredTransactions { - valid: TransactionTypes.IConfirmedTransaction | null; + valid: TransactionTypes.ITimestampedTransaction | null; ignored: TransactionTypes.IIgnoredTransaction | null; } @@ -39,7 +39,7 @@ export default class ChannelParser { const validAndIgnoredTransactions: IValidAndIgnoredTransactions[] = await transactions.reduce( async ( accumulatorPromise: Promise, - confirmedTransaction: TransactionTypes.IConfirmedTransaction, + timestampedTransaction: TransactionTypes.ITimestampedTransaction, ) => { const result = await accumulatorPromise; @@ -47,7 +47,7 @@ export default class ChannelParser { try { // Parse the transaction from data-access to get a transaction object and the channel key if encrypted parsedData = await this.transactionParser.parsePersistedTransaction( - confirmedTransaction.transaction, + timestampedTransaction.transaction, channelType, channelKey, ); @@ -56,7 +56,7 @@ export default class ChannelParser { { ignored: { reason: error.message, - transaction: confirmedTransaction, + transaction: timestampedTransaction, }, valid: null, }, @@ -72,7 +72,7 @@ export default class ChannelParser { { ignored: { reason: error, - transaction: confirmedTransaction, + transaction: timestampedTransaction, }, valid: null, }, @@ -88,7 +88,7 @@ export default class ChannelParser { ignored: { reason: 'as first transaction, the hash of the transaction do not match the channelId', - transaction: confirmedTransaction, + transaction: timestampedTransaction, }, valid: null, }, @@ -111,7 +111,11 @@ export default class ChannelParser { return result.concat([ { ignored: null, - valid: { transaction: { data }, timestamp: confirmedTransaction.timestamp }, + valid: { + state: timestampedTransaction.state, + timestamp: timestampedTransaction.timestamp, + transaction: { data }, + }, }, ]); }, @@ -122,7 +126,7 @@ export default class ChannelParser { (elem: any) => elem.ignored, ); - const cleanTransactions: Array = validAndIgnoredTransactions.map( + const cleanTransactions: Array = validAndIgnoredTransactions.map( (elem: any) => elem.valid, ); @@ -143,7 +147,7 @@ export default class ChannelParser { */ public async getChannelTypeAndChannelKey( channelId: string, - transactions: TransactionTypes.IConfirmedTransaction[], + transactions: TransactionTypes.ITimestampedTransaction[], ): Promise<{ channelType: TransactionTypes.ChannelType; channelKey: EncryptionTypes.IDecryptionParameters | undefined; @@ -161,7 +165,7 @@ export default class ChannelParser { channelKey: EncryptionTypes.IDecryptionParameters | undefined; encryptionMethod: string | undefined; }>, - confirmedTransaction: TransactionTypes.IConfirmedTransaction, + timestampedTransaction: TransactionTypes.ITimestampedTransaction, ) => { const result = await accumulatorPromise; @@ -174,7 +178,7 @@ export default class ChannelParser { try { // Parse the transaction from data-access to get a transaction object and the channel key if encrypted parsedData = await this.transactionParser.parsePersistedTransaction( - confirmedTransaction.transaction, + timestampedTransaction.transaction, TransactionTypes.ChannelType.UNKNOWN, ); } catch (error) { diff --git a/packages/transaction-manager/src/encrypted-transaction.ts b/packages/transaction-manager/src/encrypted-transaction.ts index 10caf6443d..c230207dc6 100644 --- a/packages/transaction-manager/src/encrypted-transaction.ts +++ b/packages/transaction-manager/src/encrypted-transaction.ts @@ -15,26 +15,20 @@ export default class EncryptedTransaction implements TransactionTypes.ITransacti /** Persisted data */ private persistedData: TransactionTypes.ITransactionData; - /** hash given by the persisted transaction */ - private hashFromPersistedTransaction: string; - /** channel key to decrypt the encrypted data */ private channelKey: EncryptionTypes.IDecryptionParameters; /** * Creates an instance of EncryptedTransaction. * @param persistedData the encrypted data of the transaction - * @param hashFromPersistedTransaction the hash of the decrypted data (not checked) * @param channelKey decryption parameters to decrypted the encrypted data */ constructor( persistedData: TransactionTypes.ITransactionData, - hashFromPersistedTransaction: string, channelKey: EncryptionTypes.IDecryptionParameters, ) { this.persistedData = persistedData; this.channelKey = channelKey; - this.hashFromPersistedTransaction = hashFromPersistedTransaction; } /** @@ -78,13 +72,17 @@ export default class EncryptedTransaction implements TransactionTypes.ITransacti * @returns a promise resolving a string of the error if any, otherwise an empty string */ public async getError(): Promise { + let data = ''; try { - if ((await this.getHash()) !== this.hashFromPersistedTransaction) { - throw Error('The given hash does not match the hash of the decrypted data'); - } - return ''; - } catch (error) { - return error.message; + data = await this.getData(); + } catch (e) { + return e.message; + } + try { + JSON.parse(data); + } catch (e) { + return 'Impossible to JSON parse the decrypted transaction data'; } + return ''; } } diff --git a/packages/transaction-manager/src/transaction-manager.ts b/packages/transaction-manager/src/transaction-manager.ts index ce453f4579..36a2628a3e 100644 --- a/packages/transaction-manager/src/transaction-manager.ts +++ b/packages/transaction-manager/src/transaction-manager.ts @@ -7,6 +7,8 @@ import { } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; +import { EventEmitter } from 'events'; + import ChannelParser from './channel-parser'; import TransactionsFactory from './transactions-factory'; @@ -111,13 +113,34 @@ export default class TransactionManager implements TransactionTypes.ITransaction topics.concat([hash]), ); - return { + // Create the return result with EventEmitter + const result: TransactionTypes.IReturnPersistTransaction = Object.assign(new EventEmitter(), { meta: { dataAccessMeta: persistResult.meta, encryptionMethod: channelEncryptionMethod, }, result: {}, - }; + }); + + // When receive the confirmation from data-access propagate to the higher layer + persistResult + .on('confirmed', (resultPersistTransaction: DataAccessTypes.IReturnPersistTransaction) => { + const resultAfterConfirmation = { + meta: { + dataAccessMeta: resultPersistTransaction.meta, + encryptionMethod: channelEncryptionMethod, + }, + result: {}, + }; + + // propagate the confirmation + result.emit('confirmed', resultAfterConfirmation); + }) + .on('error', error => { + result.emit('error', error); + }); + + return result; } /** diff --git a/packages/transaction-manager/src/transactions-factory.ts b/packages/transaction-manager/src/transactions-factory.ts index bda962b961..3ce94bee23 100644 --- a/packages/transaction-manager/src/transactions-factory.ts +++ b/packages/transaction-manager/src/transactions-factory.ts @@ -1,5 +1,5 @@ import MultiFormat from '@requestnetwork/multi-format'; -import { EncryptionTypes, MultiFormatTypes, TransactionTypes } from '@requestnetwork/types'; +import { EncryptionTypes, TransactionTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; /** @@ -36,21 +36,19 @@ export default class TransactionsFactory { encryptionParams: EncryptionTypes.IEncryptionParameters[], ): Promise { // format encryption method property - const encryptionMethod = `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_CBC}`; + const encryptionMethod = `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_GCM}`; // Generate a key for the AES encryption const symmetricKey: string = await Utils.crypto.generate32BufferKey(); - // Encrypt the data with the key and the AES256-CBC algorithm + // Encrypt the data with the key and the AES256-GCM algorithm const encryptedData: EncryptionTypes.IEncryptedData = await Utils.encryption.encrypt(data, { key: symmetricKey, - method: EncryptionTypes.METHOD.AES256_CBC, + method: EncryptionTypes.METHOD.AES256_GCM, }); - // Compute the hash of the data - let hash: MultiFormatTypes.HashTypes.IHash; try { - hash = Utils.crypto.normalizeKeccak256Hash(JSON.parse(data)); + JSON.parse(data); } catch (error) { throw new Error('Data not parsable'); } @@ -105,9 +103,8 @@ export default class TransactionsFactory { ); const encryptedDataSerialized: string = MultiFormat.serialize(encryptedData); - const hashSerialized: string = MultiFormat.serialize(hash); - return { encryptedData: encryptedDataSerialized, keys, hash: hashSerialized, encryptionMethod }; + return { encryptedData: encryptedDataSerialized, keys, encryptionMethod }; } /** @@ -122,27 +119,24 @@ export default class TransactionsFactory { channelKey: EncryptionTypes.IEncryptionParameters, ): Promise { // check if the encryption method is the good one - if (channelKey.method !== EncryptionTypes.METHOD.AES256_CBC) { + if (channelKey.method !== EncryptionTypes.METHOD.AES256_GCM) { throw new Error(`encryption method not supported for the channel key: ${channelKey.method}`); } - // Encrypt the data with the key and the AES256-CBC algorithm + // Encrypt the data with the key and the AES256-GCM algorithm const encryptedData: EncryptionTypes.IEncryptedData = await Utils.encryption.encrypt( data, channelKey, ); - // Compute the hash of the data - let hash: MultiFormatTypes.HashTypes.IHash; try { - hash = Utils.crypto.normalizeKeccak256Hash(JSON.parse(data)); + JSON.parse(data); } catch (error) { throw new Error('Data not parsable'); } const encryptedDataSerialized: string = MultiFormat.serialize(encryptedData); - const hashSerialized: string = MultiFormat.serialize(hash); - return { encryptedData: encryptedDataSerialized, hash: hashSerialized }; + return { encryptedData: encryptedDataSerialized }; } } diff --git a/packages/transaction-manager/src/transactions-parser.ts b/packages/transaction-manager/src/transactions-parser.ts index 47c5d765e1..e1c244a127 100644 --- a/packages/transaction-manager/src/transactions-parser.ts +++ b/packages/transaction-manager/src/transactions-parser.ts @@ -44,7 +44,6 @@ export default class TransactionsParser { if ( persistedTransaction.encryptedData || persistedTransaction.encryptionMethod || - persistedTransaction.hash || persistedTransaction.keys ) { throw new Error('only the property "data" is allowed for clear transaction'); @@ -57,9 +56,6 @@ export default class TransactionsParser { if (channelType === TransactionTypes.ChannelType.CLEAR) { throw new Error('Encrypted transactions are not allowed in clear channel'); } - if (!persistedTransaction.hash) { - throw new Error('the property "hash" is missing for the encrypted transaction'); - } // if we don't have the channel key we need to decrypt it if (!channelKey) { @@ -83,11 +79,7 @@ export default class TransactionsParser { return { channelKey, encryptionMethod: persistedTransaction.encryptionMethod, - transaction: new EncryptedTransaction( - persistedTransaction.encryptedData, - persistedTransaction.hash, - channelKey, - ), + transaction: new EncryptedTransaction(persistedTransaction.encryptedData, channelKey), }; } @@ -110,10 +102,17 @@ export default class TransactionsParser { throw new Error(`No decryption provider given`); } + let channelKeyMethod: EncryptionTypes.METHOD; // Check the encryption method if ( - encryptionMethod !== `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_CBC}` + encryptionMethod === `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_CBC}` + ) { + channelKeyMethod = EncryptionTypes.METHOD.AES256_CBC; + } else if ( + encryptionMethod === `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_GCM}` ) { + channelKeyMethod = EncryptionTypes.METHOD.AES256_GCM; + } else { throw new Error(`Encryption method not supported: ${encryptionMethod}`); } @@ -157,7 +156,7 @@ export default class TransactionsParser { } return { key: channelKey, - method: EncryptionTypes.METHOD.AES256_CBC, + method: channelKeyMethod, }; } } diff --git a/packages/transaction-manager/test/index.test.ts b/packages/transaction-manager/test/index.test.ts index 7196075bad..efa6b4ebc4 100644 --- a/packages/transaction-manager/test/index.test.ts +++ b/packages/transaction-manager/test/index.test.ts @@ -3,6 +3,8 @@ import 'mocha'; import MultiFormat from '@requestnetwork/multi-format'; import Utils from '@requestnetwork/utils'; +import { EventEmitter } from 'events'; + const chai = require('chai'); const spies = require('chai-spies'); const chaiAsPromised = require('chai-as-promised'); @@ -12,7 +14,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; -import { DataAccessTypes, EncryptionTypes } from '@requestnetwork/types'; +import { DataAccessTypes, EncryptionTypes, TransactionTypes } from '@requestnetwork/types'; import { TransactionManager } from '../src/index'; import TransactionsFactory from '../src/transactions-factory'; @@ -25,18 +27,29 @@ const fakeTxHash = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa const data = '{ "what": "ever", "it": "is,", "this": "must", "work": true }'; const data2 = '{"or": "can", "be":false}'; -const tx: DataAccessTypes.IConfirmedTransaction = { transaction: { data }, timestamp: 1 }; -const tx2: DataAccessTypes.IConfirmedTransaction = { transaction: { data: data2 }, timestamp: 1 }; +const tx: DataAccessTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, +}; +const tx2: DataAccessTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data: data2 }, +}; const dataHash = Utils.crypto.normalizeKeccak256Hash(JSON.parse(data)); const channelId = MultiFormat.serialize(dataHash); const dataHash2 = Utils.crypto.normalizeKeccak256Hash(JSON.parse(data2)); const channelId2 = MultiFormat.serialize(dataHash2); -const fakeMetaDataAccessPersistReturn: DataAccessTypes.IReturnPersistTransaction = { - meta: { transactionStorageLocation: 'fakeDataId', topics: extraTopics }, - result: { topics: [fakeTxHash] }, -}; +const fakeMetaDataAccessPersistReturn: DataAccessTypes.IReturnPersistTransaction = Object.assign( + new EventEmitter(), + { + meta: { transactionStorageLocation: 'fakeDataId', topics: extraTopics }, + result: { topics: [fakeTxHash] }, + }, +); const fakeMetaDataAccessGetReturn: DataAccessTypes.IReturnGetTransactions = { meta: { transactionsStorageLocation: ['fakeDataId1', 'fakeDataId2'] }, @@ -53,11 +66,28 @@ let fakeDataAccess: DataAccessTypes.IDataAccess; describe('index', () => { beforeEach(() => { fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy.returns(fakeMetaDataAccessGetChannelsReturn), getChannelsByTopic: chai.spy.returns(fakeMetaDataAccessGetChannelsReturn), getTransactionsByChannelId: chai.spy.returns(fakeMetaDataAccessGetReturn), initialize: chai.spy(), - persistTransaction: chai.spy.returns(fakeMetaDataAccessPersistReturn), + // persistTransaction: chai.spy.returns(fakeMetaDataAccessPersistReturn), + persistTransaction: chai.spy( + (): any => { + setTimeout(() => { + fakeMetaDataAccessPersistReturn.emit( + 'confirmed', + { + meta: { transactionStorageLocation: 'fakeDataId', topics: extraTopics }, + result: { topics: [fakeTxHash] }, + }, + // tslint:disable-next-line:no-magic-numbers + 100, + ); + }); + return fakeMetaDataAccessPersistReturn; + }, + ), }; }); describe('persistTransaction', () => { @@ -67,6 +97,16 @@ describe('index', () => { const ret = await transactionManager.persistTransaction(data, channelId, extraTopics); + ret.on('confirmed', resultConfirmed1 => { + expect(resultConfirmed1, 'result Confirmed wrong').to.deep.equal({ + meta: { + dataAccessMeta: { transactionStorageLocation: 'fakeDataId', topics: extraTopics }, + encryptionMethod: undefined, + }, + result: {}, + }); + }); + expect(ret.result, 'ret.result is wrong').to.be.deep.equal({}); expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, @@ -90,10 +130,45 @@ describe('index', () => { expect(ret.result, 'ret.result is wrong').to.be.deep.equal({}); expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: 'ecies-aes256-cbc', + encryptionMethod: 'ecies-aes256-gcm', }); expect(fakeDataAccess.persistTransaction).to.have.been.called.once(); }); + + it('cannot persist a transaction if data access emit error', async () => { + const fakeDataAccessEmittingError = Object.assign({}, fakeDataAccess); + fakeDataAccessEmittingError.persistTransaction = chai.spy( + (): any => { + const persistWithEvent = Object.assign( + new EventEmitter(), + fakeMetaDataAccessPersistReturn, + ); + setTimeout(() => { + // tslint:disable-next-line:no-magic-numbers + persistWithEvent.emit('error', 'error for test purpose', 100); + }); + return persistWithEvent; + }, + ); + + const transactionManager = new TransactionManager(fakeDataAccess); + + const ret = await transactionManager.persistTransaction(data, channelId, extraTopics); + + ret.on('error', error => { + expect(error, 'result Confirmed wrong').to.equal('error for test purpose'); + }); + + expect(ret.result, 'ret.result is wrong').to.be.deep.equal({}); + expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ + dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, + encryptionMethod: undefined, + }); + expect(fakeDataAccess.persistTransaction).to.have.been.called.with( + await TransactionsFactory.createClearTransaction(data), + extraTopics.concat([channelId]), + ); + }); }); describe('in an existing new channel', () => { @@ -124,11 +199,18 @@ describe('index', () => { transactionsStorageLocation: ['fakeDataId1'], }, result: { - transactions: [{ transaction: encryptedTx, timestamp: 1 }], + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -147,7 +229,7 @@ describe('index', () => { expect(ret.result, 'ret.result is wrong').to.be.deep.equal({}); expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ dataAccessMeta: fakeMetaDataAccessPersistReturn.meta, - encryptionMethod: 'ecies-aes256-cbc', + encryptionMethod: 'ecies-aes256-gcm', }); expect(fakeDataAccess.persistTransaction).to.have.been.called.to.have.been.called.with( @@ -167,6 +249,7 @@ describe('index', () => { }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns(fakeMetaDataAccessGetReturnEmpty), @@ -194,11 +277,18 @@ describe('index', () => { transactionsStorageLocation: ['fakeDataId1'], }, result: { - transactions: [{ transaction: encryptedTx, timestamp: 1 }], + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -239,7 +329,8 @@ describe('index', () => { }); it('can getTransactionsByChannelId() with channelId not matching the first transaction hash', async () => { - const txWrongHash: DataAccessTypes.IConfirmedTransaction = { + const txWrongHash: DataAccessTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { data: '{"wrong": "hash"}' }, }; @@ -250,6 +341,7 @@ describe('index', () => { }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns(fakeMetaDataAccessGetReturnFirstHashWrong), @@ -280,7 +372,8 @@ describe('index', () => { }); it('can getTransactionsByChannelId() the first transaction data not parsable', async () => { - const txWrongHash: DataAccessTypes.IConfirmedTransaction = { + const txWrongHash: DataAccessTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { data: 'Not parsable' }, }; @@ -291,6 +384,7 @@ describe('index', () => { }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns(fakeMetaDataAccessGetReturnFirstHashWrong), @@ -328,10 +422,19 @@ describe('index', () => { ]); const fakeMetaDataAccessGetReturnWithEncryptedTransaction: DataAccessTypes.IReturnGetTransactions = { meta: { transactionsStorageLocation: ['fakeDataId1'] }, - result: { transactions: [{ transaction: encryptedTx, timestamp: 1 }] }, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], + }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -350,10 +453,18 @@ describe('index', () => { expect(ret, 'return is wrong').to.be.deep.equal({ meta: { dataAccessMeta: { transactionsStorageLocation: ['fakeDataId1'] }, - encryptionMethod: 'ecies-aes256-cbc', + encryptionMethod: 'ecies-aes256-gcm', ignoredTransactions: [null], }, - result: { transactions: [{ transaction: { data }, timestamp: 1 }] }, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, + }, + ], + }, }); }); @@ -365,10 +476,19 @@ describe('index', () => { ]); const fakeMetaDataAccessGetReturnWithEncryptedTransaction: DataAccessTypes.IReturnGetTransactions = { meta: { transactionsStorageLocation: ['fakeDataId1'] }, - result: { transactions: [{ transaction: encryptedTx, timestamp: 1 }] }, + result: { + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], + }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -387,7 +507,11 @@ describe('index', () => { ignoredTransactions: [ { reason: 'No decryption provider given', - transaction: { transaction: encryptedTx, timestamp: 1 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, }, ], }, @@ -409,13 +533,22 @@ describe('index', () => { }, result: { transactions: [ - { transaction: encryptedTx, timestamp: 1 }, - { transaction: encryptedTx2, timestamp: 2 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: encryptedTx2, + }, ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -436,18 +569,29 @@ describe('index', () => { dataAccessMeta: { transactionsStorageLocation: ['fakeDataId1', 'fakeDataId2'], }, - encryptionMethod: 'ecies-aes256-cbc', + encryptionMethod: 'ecies-aes256-gcm', ignoredTransactions: [ null, { reason: 'the properties "encryptionMethod" and "keys" have been already given for this channel', - transaction: { transaction: encryptedTx2, timestamp: 2 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: encryptedTx2, + }, }, ], }, result: { - transactions: [{ transaction: { data }, timestamp: 1 }, null], + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, + }, + null, + ], }, }); }); @@ -457,7 +601,6 @@ describe('index', () => { data2, [TestData.idRaw3.encryptionParams], ); - encryptedTxFakeHash.hash = channelId; const encryptedTx = await TransactionsFactory.createEncryptedTransactionInNewChannel(data, [ TestData.idRaw1.encryptionParams, @@ -469,13 +612,22 @@ describe('index', () => { }, result: { transactions: [ - { transaction: encryptedTxFakeHash, timestamp: 1 }, - { transaction: encryptedTx, timestamp: 2 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTxFakeHash, + }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: encryptedTx, + }, ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -496,17 +648,29 @@ describe('index', () => { dataAccessMeta: { transactionsStorageLocation: ['fakeDataId1', 'fakeDataId2'], }, - encryptionMethod: 'ecies-aes256-cbc', + encryptionMethod: 'ecies-aes256-gcm', ignoredTransactions: [ { - reason: 'The given hash does not match the hash of the decrypted data', - transaction: { transaction: encryptedTxFakeHash, timestamp: 1 }, + reason: + 'as first transaction, the hash of the transaction do not match the channelId', + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTxFakeHash, + }, }, null, ], }, result: { - transactions: [null, { transaction: { data }, timestamp: 2 }], + transactions: [ + null, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data }, + }, + ], }, }); }); @@ -522,13 +686,22 @@ describe('index', () => { }, result: { transactions: [ - { transaction: encryptedTx, timestamp: 1 }, - { transaction: { data: data2 }, timestamp: 2 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data: data2 }, + }, ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -549,17 +722,28 @@ describe('index', () => { dataAccessMeta: { transactionsStorageLocation: ['fakeDataId1', 'fakeDataId2'], }, - encryptionMethod: 'ecies-aes256-cbc', + encryptionMethod: 'ecies-aes256-gcm', ignoredTransactions: [ null, { reason: `Clear transactions are not allowed in encrypted channel`, - transaction: { transaction: { data: data2 }, timestamp: 2 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data: data2 }, + }, }, ], }, result: { - transactions: [{ transaction: { data }, timestamp: 1 }, null], + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, + }, + null, + ], }, }); }); @@ -577,13 +761,22 @@ describe('index', () => { }, result: { transactions: [ - { transaction: encryptedTx, timestamp: 1 }, - { transaction: { data: data2 }, timestamp: 2 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data: data2 }, + }, ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -608,12 +801,20 @@ describe('index', () => { ignoredTransactions: [ { reason: 'Impossible to decrypt the channel key from this transaction ()', - transaction: { transaction: encryptedTx, timestamp: 1 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, }, { reason: 'as first transaction, the hash of the transaction do not match the channelId', - transaction: { transaction: { data: data2 }, timestamp: 2 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data: data2 }, + }, }, ], }, @@ -634,13 +835,22 @@ describe('index', () => { }, result: { transactions: [ - { transaction: { data }, timestamp: 1 }, - { transaction: encryptedTx, timestamp: 2 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, + }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: encryptedTx, + }, ], }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy(), getTransactionsByChannelId: chai.spy.returns( @@ -665,12 +875,23 @@ describe('index', () => { null, { reason: 'Encrypted transactions are not allowed in clear channel', - transaction: { transaction: encryptedTx, timestamp: 2 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: encryptedTx, + }, }, ], }, result: { - transactions: [{ transaction: { data }, timestamp: 1 }, null], + transactions: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, + }, + null, + ], }, }); }); @@ -709,11 +930,18 @@ describe('index', () => { }, result: { transactions: { - [channelId]: [{ transaction: encryptedTx, timestamp: 1 }], + [channelId]: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], }, }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy.returns(fakeMetaDataAccessGetReturnWithEncryptedTransaction), getTransactionsByChannelId: chai.spy(), @@ -757,11 +985,18 @@ describe('index', () => { }, result: { transactions: { - [channelId]: [{ transaction: encryptedTx, timestamp: 1 }], + [channelId]: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], }, }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy.returns(fakeMetaDataAccessGetReturnWithEncryptedTransaction), getTransactionsByChannelId: chai.spy(), @@ -784,7 +1019,11 @@ describe('index', () => { [channelId]: [ { reason: 'No decryption provider given', - transaction: { transaction: encryptedTx, timestamp: 1 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, }, ], }, @@ -808,13 +1047,22 @@ describe('index', () => { result: { transactions: { [channelId]: [ - { transaction: encryptedTx, timestamp: 1 }, - { transaction: { data }, timestamp: 2 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data }, + }, ], }, }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy.returns(fakeMetaDataAccessGetReturnWithEncryptedTransaction), getTransactionsByChannelId: chai.spy(), @@ -828,7 +1076,14 @@ describe('index', () => { expect(ret.result, 'ret.result is wrong').to.be.deep.equal({ transactions: { - [channelId]: [null, { transaction: { data }, timestamp: 2 }], + [channelId]: [ + null, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 2, + transaction: { data }, + }, + ], }, }); expect(ret.meta, 'ret.meta is wrong').to.be.deep.equal({ @@ -837,7 +1092,11 @@ describe('index', () => { [channelId]: [ { reason: 'No decryption provider given', - transaction: { transaction: encryptedTx, timestamp: 1 }, + transaction: { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, }, null, ], @@ -847,7 +1106,8 @@ describe('index', () => { }); it('can get channels indexed by topics with channelId not matching the first transaction hash', async () => { - const txWrongHash: DataAccessTypes.IConfirmedTransaction = { + const txWrongHash: DataAccessTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { data: '{"wrong": "hash"}' }, }; @@ -861,6 +1121,7 @@ describe('index', () => { result: { transactions: { [channelId]: [txWrongHash, tx, tx2] } }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy.returns(fakeMetaDataAccessGetReturnFirstHashWrong), getTransactionsByChannelId: chai.spy(), @@ -908,12 +1169,25 @@ describe('index', () => { }, result: { transactions: { - [channelId]: [{ transaction: encryptedTx, timestamp: 1 }], - [channelId2]: [{ transaction: { data: data2 }, timestamp: 1 }], + [channelId]: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedTx, + }, + ], + [channelId2]: [ + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data: data2 }, + }, + ], }, }, }; fakeDataAccess = { + _getStatus: chai.spy(), getChannelsByMultipleTopics: chai.spy(), getChannelsByTopic: chai.spy.returns(fakeMetaDataAccessGetReturnWithEncryptedTransaction), getTransactionsByChannelId: chai.spy(), diff --git a/packages/transaction-manager/test/unit/channel-parser-test.ts b/packages/transaction-manager/test/unit/channel-parser-test.ts index 24742b877b..78c4b0af59 100644 --- a/packages/transaction-manager/test/unit/channel-parser-test.ts +++ b/packages/transaction-manager/test/unit/channel-parser-test.ts @@ -17,8 +17,16 @@ let channelParser: ChannelParser; const data = '{ "what": "ever", "it": "is,", "this": "must", "work": true }'; const data2 = '{"or": "can", "be":false}'; -const tx: TransactionTypes.IConfirmedTransaction = { transaction: { data }, timestamp: 1 }; -const tx2: TransactionTypes.IConfirmedTransaction = { transaction: { data: data2 }, timestamp: 1 }; +const tx: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, +}; +const tx2: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data: data2 }, +}; const dataHash = Utils.crypto.normalizeKeccak256Hash(JSON.parse(data)); const channelId = MultiFormat.serialize(dataHash); @@ -51,7 +59,8 @@ describe('channel-parser', () => { expect(ret.transactions, 'transactions wrong').to.be.deep.equal([null, tx2]); }); it('can clean a clear channel with a transaction data not parsable', async () => { - const txNotParsable: TransactionTypes.IConfirmedTransaction = { + const txNotParsable: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { data: 'not parsable' }, }; @@ -68,7 +77,8 @@ describe('channel-parser', () => { expect(ret.transactions, 'transactions wrong').to.be.deep.equal([tx, null]); }); it('can clean a clear channel with a transaction not well formated', async () => { - const txNotFormatted: TransactionTypes.IConfirmedTransaction = { + const txNotFormatted: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { noData: 'here' } as any, }; @@ -90,12 +100,20 @@ describe('channel-parser', () => { data, [TestData.idRaw1.encryptionParams], ); - const confirmedTx = { transaction: encryptedParsedTx, timestamp: 1 }; + const confirmedTx = { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedParsedTx, + }; const ret = await channelParser.decryptAndCleanChannel(channelId, [confirmedTx]); expect(ret.ignoredTransactions, 'ignoredTransactions wrong').to.be.deep.equal([null]); expect(ret.transactions, 'transactions wrong').to.be.deep.equal([ - { transaction: { data }, timestamp: 1 }, + { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: { data }, + }, ]); }); }); @@ -116,7 +134,8 @@ describe('channel-parser', () => { }); it('can get channel type on a clear channel with a transaction data not parsable', async () => { - const txNotParsable: TransactionTypes.IConfirmedTransaction = { + const txNotParsable: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { data: 'not parsable' }, }; @@ -128,7 +147,8 @@ describe('channel-parser', () => { }); it('can get channel type on a clear channel with a transaction not well formated', async () => { - const txNotFormatted: TransactionTypes.IConfirmedTransaction = { + const txNotFormatted: TransactionTypes.ITimestampedTransaction = { + state: TransactionTypes.TransactionState.PENDING, timestamp: 1, transaction: { noData: 'here' } as any, }; @@ -145,7 +165,11 @@ describe('channel-parser', () => { data, [TestData.idRaw1.encryptionParams], ); - const confirmedTx = { transaction: encryptedParsedTx, timestamp: 1 }; + const confirmedTx = { + state: TransactionTypes.TransactionState.PENDING, + timestamp: 1, + transaction: encryptedParsedTx, + }; const ret = await channelParser.getChannelTypeAndChannelKey(channelId, [confirmedTx]); expect(ret.channelKey, 'channelKey wrong').to.be.not.undefined; diff --git a/packages/transaction-manager/test/unit/encryption-transaction-test.ts b/packages/transaction-manager/test/unit/encryption-transaction-test.ts index dc182920d9..38c52a31f1 100644 --- a/packages/transaction-manager/test/unit/encryption-transaction-test.ts +++ b/packages/transaction-manager/test/unit/encryption-transaction-test.ts @@ -24,14 +24,14 @@ const encryptedData = describe('encryption-transaction', () => { describe('getData', () => { it('can getData()', async () => { - const tx = new EncryptedTransaction(encryptedData, hash, channelKey); + const tx = new EncryptedTransaction(encryptedData, channelKey); expect(await tx.getData(), 'transaction not right').to.deep.equal(data); }); }); describe('getHash', () => { it('can get hash of the data', async () => { - const tx = new EncryptedTransaction(encryptedData, hash, channelKey); + const tx = new EncryptedTransaction(encryptedData, channelKey); expect(await tx.getHash(), 'hash not right').to.deep.equal(hash); }); @@ -43,14 +43,14 @@ describe('encryption-transaction', () => { await Utils.encryption.encrypt('Not parsable', channelKey), ); - const tx = new EncryptedTransaction(encryptedDataNotParsable, hash, channelKey); + const tx = new EncryptedTransaction(encryptedDataNotParsable, channelKey); expect(await tx.getError(), 'error not right').to.deep.equal( 'Impossible to JSON parse the decrypted transaction data', ); }); it('can get error of a transaction impossible to decrypt', async () => { - const tx = new EncryptedTransaction(encryptedData, hash, { + const tx = new EncryptedTransaction(encryptedData, { key: 'Corrupted', method: EncryptionTypes.METHOD.AES256_CBC, }); @@ -59,16 +59,8 @@ describe('encryption-transaction', () => { 'Impossible to decrypt the transaction', ); }); - it('can get error of a transaction with hash given not matching real hash', async () => { - const tx = new EncryptedTransaction(encryptedData, 'wrong hash', channelKey); - - expect(await tx.getError(), 'error not right').to.deep.equal( - 'The given hash does not match the hash of the decrypted data', - ); - }); it('can get error of a transaction if no error', async () => { - const tx = new EncryptedTransaction(encryptedData, hash, channelKey); - + const tx = new EncryptedTransaction(encryptedData, channelKey); expect(await tx.getError(), 'error not right').to.deep.equal(''); }); }); diff --git a/packages/transaction-manager/test/unit/transactions-factory-test.ts b/packages/transaction-manager/test/unit/transactions-factory-test.ts index d7a089bfab..04db37ecf2 100644 --- a/packages/transaction-manager/test/unit/transactions-factory-test.ts +++ b/packages/transaction-manager/test/unit/transactions-factory-test.ts @@ -7,7 +7,6 @@ const expect = chai.expect; import MultiFormat from '@requestnetwork/multi-format'; import { EncryptionTypes, MultiFormatTypes } from '@requestnetwork/types'; -import Utils from '@requestnetwork/utils'; import TransactionsFactory from '../../src/transactions-factory'; import * as TestData from './utils/test-data'; @@ -40,20 +39,16 @@ describe('transaction-factory', () => { if (encryptedTx.encryptedData) { // tslint:disable-next-line:no-magic-numbers - expect(encryptedTx.encryptedData.length, 'encryptedData not right').to.equal(110); + expect(encryptedTx.encryptedData.length, 'encryptedData not right').to.equal(126); expect(encryptedTx.encryptedData.slice(0, 2), 'encryptedData not right').to.deep.equal( - MultiFormatTypes.prefix.AES256_CBC_ENCRYPTED, + MultiFormatTypes.prefix.AES256_GCM_ENCRYPTED, ); } else { expect.fail('encryptedData should not be undefined'); } expect(encryptedTx.encryptionMethod, 'encryptionMethod not right').to.deep.equal( - `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_CBC}`, - ); - - expect(encryptedTx.hash, 'hash not right').to.deep.equal( - MultiFormat.serialize(Utils.crypto.normalizeKeccak256Hash(JSON.parse(data))), + `${EncryptionTypes.METHOD.ECIES}-${EncryptionTypes.METHOD.AES256_GCM}`, ); expect(Object.keys(encryptedTx.keys || {}).length, 'keys not right').to.deep.equal(3); @@ -103,16 +98,16 @@ describe('transaction-factory', () => { it('can create encrypted transaction', async () => { const channelKey = { key: 'Vt6L0ppo7tOs9KdnTT6HSHZ/wW1Pfu/rgSs5NVTigN8=', - method: EncryptionTypes.METHOD.AES256_CBC, + method: EncryptionTypes.METHOD.AES256_GCM, }; const encryptedTx = await TransactionsFactory.createEncryptedTransaction(data, channelKey); // tslint:disable-next-line:no-magic-numbers if (encryptedTx.encryptedData) { // tslint:disable-next-line:no-magic-numbers - expect(encryptedTx.encryptedData.length, 'encryptedData not right').to.equal(110); + expect(encryptedTx.encryptedData.length, 'encryptedData not right').to.equal(126); expect(encryptedTx.encryptedData.slice(0, 2), 'encryptedData not right').to.deep.equal( - MultiFormatTypes.prefix.AES256_CBC_ENCRYPTED, + MultiFormatTypes.prefix.AES256_GCM_ENCRYPTED, ); } else { expect.fail('encryptedData should not be undefined'); @@ -120,10 +115,6 @@ describe('transaction-factory', () => { expect(encryptedTx.encryptionMethod, 'encryptionMethod not right').to.be.undefined; - expect(encryptedTx.hash, 'hash not right').to.equal( - MultiFormat.serialize(Utils.crypto.normalizeKeccak256Hash(JSON.parse(data))), - ); - expect(encryptedTx.keys, 'keys not right').to.be.undefined; }); @@ -142,7 +133,7 @@ describe('transaction-factory', () => { it('cannot create encrypted transaction with not parsable data', async () => { const channelKey = { key: 'Vt6L0ppo7tOs9KdnTT6HSHZ/wW1Pfu/rgSs5NVTigN8=', - method: EncryptionTypes.METHOD.AES256_CBC, + method: EncryptionTypes.METHOD.AES256_GCM, }; await expect( TransactionsFactory.createEncryptedTransaction('Not parsable', channelKey), diff --git a/packages/transaction-manager/test/unit/transactions-parser-test.ts b/packages/transaction-manager/test/unit/transactions-parser-test.ts index 7e19404c9d..1dd5051f66 100644 --- a/packages/transaction-manager/test/unit/transactions-parser-test.ts +++ b/packages/transaction-manager/test/unit/transactions-parser-test.ts @@ -24,7 +24,7 @@ describe('transaction-parser', () => { it('cannot parse transaction not well formatted', async () => { await expect( transactionParser.parsePersistedTransaction( - { hash: 'hash', encryptionMethod: 'encryptionMethod' }, + { encryptionMethod: 'encryptionMethod' }, TransactionTypes.ChannelType.UNKNOWN, ), 'must reject', @@ -64,16 +64,6 @@ describe('transaction-parser', () => { 'only the property "data" is allowed for clear transaction', ); - await expect( - transactionParser.parsePersistedTransaction( - { data: 'data', hash: 'hash' }, - TransactionTypes.ChannelType.UNKNOWN, - ), - 'must reject', - ).to.eventually.be.rejectedWith( - 'only the property "data" is allowed for clear transaction', - ); - await expect( transactionParser.parsePersistedTransaction( { data: 'data', keys: {} }, @@ -106,6 +96,7 @@ describe('transaction-parser', () => { TestData.idRaw3.encryptionParams, ], ); + const ret = await transactionParser.parsePersistedTransaction( encryptedParsedTx, TransactionTypes.ChannelType.UNKNOWN, @@ -154,7 +145,6 @@ describe('transaction-parser', () => { { encryptedData: 'encryptedData', encryptionMethod: 'encryptionMethod', - hash: 'hash', keys: {}, }, TransactionTypes.ChannelType.UNKNOWN, @@ -177,21 +167,10 @@ describe('transaction-parser', () => { 'must reject', ).to.eventually.be.rejectedWith('Encrypted transactions are not allowed in clear channel'); }); - it('cannot parse encrypted transaction without hash', async () => { - await expect( - transactionParser.parsePersistedTransaction( - { encryptedData: 'encryptedData' }, - TransactionTypes.ChannelType.UNKNOWN, - ), - 'must reject', - ).to.eventually.be.rejectedWith( - 'the property "hash" is missing for the encrypted transaction', - ); - }); it('cannot parse encrypted transaction without channelKey with no encryptionMethod or keys', async () => { await expect( transactionParser.parsePersistedTransaction( - { encryptedData: 'encryptedData', hash: 'hash', encryptionMethod: 'encryptionMethod' }, + { encryptedData: 'encryptedData', encryptionMethod: 'encryptionMethod' }, TransactionTypes.ChannelType.UNKNOWN, ), 'must reject', @@ -203,7 +182,6 @@ describe('transaction-parser', () => { transactionParser.parsePersistedTransaction( { encryptedData: 'encryptedData', - hash: 'hash', keys: {}, }, TransactionTypes.ChannelType.UNKNOWN, @@ -216,9 +194,9 @@ describe('transaction-parser', () => { it('cannot parse encrypted transaction with channelKey AND with encryptionMethod or keys', async () => { await expect( transactionParser.parsePersistedTransaction( - { encryptedData: 'encryptedData', hash: 'hash', encryptionMethod: 'encryptionMethod' }, + { encryptedData: 'encryptedData', encryptionMethod: 'encryptionMethod' }, TransactionTypes.ChannelType.UNKNOWN, - { key: 'channelKey', method: EncryptionTypes.METHOD.AES256_CBC }, + { key: 'channelKey', method: EncryptionTypes.METHOD.AES256_GCM }, ), 'must reject', ).to.eventually.be.rejectedWith( @@ -229,11 +207,10 @@ describe('transaction-parser', () => { transactionParser.parsePersistedTransaction( { encryptedData: 'encryptedData', - hash: 'hash', keys: {}, }, TransactionTypes.ChannelType.UNKNOWN, - { key: 'channelKey', method: EncryptionTypes.METHOD.AES256_CBC }, + { key: 'channelKey', method: EncryptionTypes.METHOD.AES256_GCM }, ), 'must reject', ).to.eventually.be.rejectedWith( diff --git a/packages/types/.vscode/settings.json b/packages/types/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/types/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index f809869249..98c0fdd77f 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -3,6 +3,353 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.17.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.17.0) (2020-06-29) + + +### Bug Fixes + +* enhance node synchronization and storing of ignored data ([#205](https://github.com/RequestNetwork/requestNetwork/issues/205)) ([fb6add2](https://github.com/RequestNetwork/requestNetwork/commit/fb6add27b0507e5db3a19682dbcda90274ab19f1)) +* topic can be any not only string ([#219](https://github.com/RequestNetwork/requestNetwork/issues/219)) ([8d8b601](https://github.com/RequestNetwork/requestNetwork/commit/8d8b6014759ca50b1152b98b1faf4888b732b327)) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) +* add the identity ethereumSmartContract to the request logic ([#218](https://github.com/RequestNetwork/requestNetwork/issues/218)) ([66d97e0](https://github.com/RequestNetwork/requestNetwork/commit/66d97e00dee7305088cb94a0edf542fe4d0bbd56)) +* amount are only number or string ([#223](https://github.com/RequestNetwork/requestNetwork/issues/223)) ([7a35bde](https://github.com/RequestNetwork/requestNetwork/commit/7a35bde63f78b9305819a80e97022fca7e9494d2)) +* remove hash in encrypted transaction ([#232](https://github.com/RequestNetwork/requestNetwork/issues/232)) ([d58f101](https://github.com/RequestNetwork/requestNetwork/commit/d58f101f9f76e408671dd1edb0d67863d1c8abd5)) +* replace symmetric encryption algorithm by aes-256-gcm ([#233](https://github.com/RequestNetwork/requestNetwork/issues/233)) ([969bebe](https://github.com/RequestNetwork/requestNetwork/commit/969bebeb99b4bc2fdd31405a162934cfdff6db05)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add an option to disable payment detection in the request client ([#201](https://github.com/RequestNetwork/requestNetwork/issues/201)) ([035302f](https://github.com/RequestNetwork/requestNetwork/commit/035302f70f86fe914d2970417c4b55a6e0a32eda)) +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.16.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.16.0) (2020-05-04) + + +### Bug Fixes + +* enhance node synchronization and storing of ignored data ([#205](https://github.com/RequestNetwork/requestNetwork/issues/205)) ([fb6add2](https://github.com/RequestNetwork/requestNetwork/commit/fb6add27b0507e5db3a19682dbcda90274ab19f1)) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add an option to disable payment detection in the request client ([#201](https://github.com/RequestNetwork/requestNetwork/issues/201)) ([035302f](https://github.com/RequestNetwork/requestNetwork/commit/035302f70f86fe914d2970417c4b55a6e0a32eda)) +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.15.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.15.0) (2020-04-21) + + +### Features + +* add an option to disable payment detection in the request client ([#201](https://github.com/RequestNetwork/requestNetwork/issues/201)) ([035302f](https://github.com/RequestNetwork/requestNetwork/commit/035302f70f86fe914d2970417c4b55a6e0a32eda)) +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.14.0) (2020-04-06) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* emits "error" event when the confirmation fails ([#179](https://github.com/RequestNetwork/requestNetwork/issues/179)) ([73bfcfb](https://github.com/RequestNetwork/requestNetwork/commit/73bfcfb5f6a54d2036a47e09ce180a00c12a81ae)) +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.13.0) (2020-03-23) + + +### Features + +* add the confirmed events in the highest layers ([#141](https://github.com/RequestNetwork/requestNetwork/issues/141)) ([7f9b756](https://github.com/RequestNetwork/requestNetwork/commit/7f9b756d51b20fbd45971f4db3e9865b75f2d265)) +* payment detection error does not throw ([#163](https://github.com/RequestNetwork/requestNetwork/issues/163)) ([f49640b](https://github.com/RequestNetwork/requestNetwork/commit/f49640b264c1350f1a7b0001fd71736f8bf3dc23)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.12.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* add proxy contract to eth input data in payment detection ([#140](https://github.com/RequestNetwork/requestNetwork/issues/140)) ([0c36de1](https://github.com/RequestNetwork/requestNetwork/commit/0c36de12d08b1b591a7fd282d2cac1e5f38adb24)) +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.11.0) (2020-01-16) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.10.0) (2019-12-18) + + +### Features + +* **advanced-logic:** add ERC20 proxy contract payment network ([#74](https://github.com/RequestNetwork/requestNetwork/issues/74)) ([031a374](https://github.com/RequestNetwork/requestNetwork/commit/031a3742d2dddc0324e75b7853287d252bf43c6c)) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.9.1](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.9.0...@requestnetwork/types@0.9.1) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/types + + + + + # [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/types@0.8.0...@requestnetwork/types@0.9.0) (2019-11-20) diff --git a/packages/types/package.json b/packages/types/package.json index e79fe51f69..9c0ca9d5cc 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/types", - "version": "0.9.0", + "version": "0.17.0", "publishConfig": { "access": "public" }, @@ -32,23 +32,26 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "prepare": "yarn run build" }, "dependencies": { - "bn.js": "4.11.8" + "bn.js": "4.11.8", + "events": "3.1.0" }, "devDependencies": { "@types/bn.js": "4.11.5", + "@types/events": "3.0.0", "@typescript-eslint/parser": "1.2.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", "prettier": "1.16.4", "shx": "0.3.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" }, diff --git a/packages/types/src/client-types.ts b/packages/types/src/client-types.ts new file mode 100644 index 0000000000..7ce93d3761 --- /dev/null +++ b/packages/types/src/client-types.ts @@ -0,0 +1,55 @@ +import * as Identity from './identity-types'; +import * as Payment from './payment-types'; +import * as RequestLogic from './request-logic-types'; + +/** Restrict research to two timestamp */ +export interface ITimestampBoundaries { + from?: number; + to?: number; +} + +/** Interface request data */ +export interface IRequestData extends Omit { + currency: string; + meta: RequestLogic.IReturnMeta | null; + balance: Payment.IBalanceWithEvents | null; + contentData: any; + currencyInfo: RequestLogic.ICurrency; + pending: RequestLogic.IPendingRequest | null; +} + +/** Interface request data with event emitter and subscriber */ +export interface IRequestDataWithEvents extends IRequestData { + on: (event: K, listener: IRequestEvents[K]) => this; + emit: ( + event: K, + ...args: Parameters + ) => boolean; +} + +/** Create request parameters */ +export interface ICreateRequestParameters { + requestInfo: RequestLogic.ICreateParameters | IRequestInfo; + signer: Identity.IIdentity; + paymentNetwork?: Payment.IPaymentNetworkCreateParameters; + topics?: any[]; + contentData?: any; + disablePaymentDetection?: boolean; +} + +/** Parameters to create a request. ICreateParameters with a more flexible currency */ +export interface IRequestInfo { + currency: string | RequestLogic.ICurrency; + expectedAmount: RequestLogic.Amount; + payee?: Identity.IIdentity; + payer?: Identity.IIdentity; + extensionsData?: any[]; + timestamp?: number; + nonce?: number; +} + +/** Events types risen by a request */ +export interface IRequestEvents { + confirmed: (requestData: IRequestDataWithEvents) => void; + error: (error: string) => void; +} diff --git a/packages/types/src/data-access-types.ts b/packages/types/src/data-access-types.ts index 1516f4d3b1..3cce29c360 100644 --- a/packages/types/src/data-access-types.ts +++ b/packages/types/src/data-access-types.ts @@ -1,3 +1,5 @@ +import { EventEmitter } from 'events'; + /** Data Access Layer */ export interface IDataAccess { initialize: () => Promise; @@ -18,6 +20,13 @@ export interface IDataAccess { topics: string[], updatedBetween?: ITimestampBoundaries, ): Promise; + _getStatus(detailed?: boolean): any; +} + +/** Enum of state possible for an action */ +export enum TransactionState { + PENDING = 'pending', + CONFIRMED = 'confirmed', } /** Restrict the get data research to two timestamp */ @@ -27,7 +36,7 @@ export interface ITimestampBoundaries { } /** return interface for PersistTransaction */ -export interface IReturnPersistTransaction { +export interface IReturnPersistTransaction extends EventEmitter { /** meta information */ meta: { /** location of the persisted transaction */ @@ -51,7 +60,7 @@ export interface IReturnGetTransactions { storageMeta?: any; }; /** result of the execution */ - result: { transactions: IConfirmedTransaction[] }; + result: { transactions: ITimestampedTransaction[] }; } /** return interface for getChannelsByTopic */ @@ -84,7 +93,7 @@ export interface IBlockHeader { /** Transactions group by channel ids */ export interface ITransactionsByChannelIds { - [key: string]: IConfirmedTransaction[]; + [key: string]: ITimestampedTransaction[]; } /** Channel ids, to connect the transactions to a channel */ @@ -110,7 +119,8 @@ export interface ITransaction { } /** Transaction confirmed */ -export interface IConfirmedTransaction { +export interface ITimestampedTransaction { + state: TransactionState; transaction: ITransaction; timestamp: number; } @@ -133,8 +143,11 @@ export interface ITransactionIndex { topics: string[], timestampBoundaries?: ITimestampBoundaries, ): Promise; + getIndexedLocations(): Promise; getStorageLocationList( channelId: string, timestampBoundaries?: ITimestampBoundaries, ): Promise; + updateTimestamp(dataId: string, timestamp: number): Promise; + removeTransaction(dataId: string): Promise; } diff --git a/packages/types/src/encryption-types.ts b/packages/types/src/encryption-types.ts index 92cd8c2c1e..5a8924db61 100644 --- a/packages/types/src/encryption-types.ts +++ b/packages/types/src/encryption-types.ts @@ -26,4 +26,5 @@ export interface IEncryptedData { export enum METHOD { ECIES = 'ecies', AES256_CBC = 'aes256-cbc', + AES256_GCM = 'aes256-gcm', } diff --git a/packages/types/src/extension-types.ts b/packages/types/src/extension-types.ts index 6c47941c93..ae29b09d15 100644 --- a/packages/types/src/extension-types.ts +++ b/packages/types/src/extension-types.ts @@ -1,11 +1,11 @@ import * as ContentData from './extensions/content-data-types'; import * as PnAddressBased from './extensions/pn-any-address-based-types'; import * as PnAnyDeclarative from './extensions/pn-any-declarative-types'; -import * as PnEthInputData from './extensions/pn-eth-input-data-types'; +import * as PnReferenceBased from './extensions/pn-any-reference-based-types'; import * as Identity from './identity-types'; import * as RequestLogic from './request-logic-types'; -export { ContentData, PnAnyDeclarative, PnAddressBased, PnEthInputData }; +export { ContentData, PnAnyDeclarative, PnAddressBased, PnReferenceBased }; /** Extension interface is extended by the extensions implementation */ export interface IExtension { @@ -48,6 +48,7 @@ export enum ID { PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED = 'pn-bitcoin-address-based', PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED = 'pn-testnet-bitcoin-address-based', PAYMENT_NETWORK_ERC20_ADDRESS_BASED = 'pn-erc20-address-based', + PAYMENT_NETWORK_ERC20_PROXY_CONTRACT = 'pn-erc20-proxy-contract', PAYMENT_NETWORK_ETH_INPUT_DATA = 'pn-eth-input-data', PAYMENT_NETWORK_ANY_DECLARATIVE = 'pn-any-declarative', } diff --git a/packages/types/src/extensions/pn-any-declarative-types.ts b/packages/types/src/extensions/pn-any-declarative-types.ts index b5609a6df2..7ab2c60842 100644 --- a/packages/types/src/extensions/pn-any-declarative-types.ts +++ b/packages/types/src/extensions/pn-any-declarative-types.ts @@ -1,4 +1,5 @@ import * as Extension from '../extension-types'; +import * as RequestLogicTypes from '../request-logic-types'; /** Manager of the extension */ export interface IAnyDeclarative extends Extension.IExtension { @@ -29,13 +30,13 @@ export interface ICreationParameters { /** Parameters of declareSentPayment and declareSentRefund action */ export interface ISentParameters { - amount: string; + amount: RequestLogicTypes.Amount; note: any; } /** Parameters of declareReceivedPayment and declareReceivedRefund action */ export interface IReceivedParameters { - amount: string; + amount: RequestLogicTypes.Amount; note: any; } diff --git a/packages/types/src/extensions/pn-eth-input-data-types.ts b/packages/types/src/extensions/pn-any-reference-based-types.ts similarity index 94% rename from packages/types/src/extensions/pn-eth-input-data-types.ts rename to packages/types/src/extensions/pn-any-reference-based-types.ts index e667765909..a88cf996cc 100644 --- a/packages/types/src/extensions/pn-eth-input-data-types.ts +++ b/packages/types/src/extensions/pn-any-reference-based-types.ts @@ -1,7 +1,7 @@ import * as Extension from '../extension-types'; /** Manager of the extension */ -export interface IEthInputData extends Extension.IExtension { +export interface IReferenceBased extends Extension.IExtension { createAddPaymentAddressAction: ( creationParameters: IAddPaymentAddressParameters, ) => Extension.IAction; diff --git a/packages/types/src/identity-types.ts b/packages/types/src/identity-types.ts index 94cf3d9213..509f0a0a07 100644 --- a/packages/types/src/identity-types.ts +++ b/packages/types/src/identity-types.ts @@ -6,7 +6,14 @@ export interface IIdentity { value: string; } +/** Identity for Ethereum Smart contract */ +export interface ISmartContractIdentity extends IIdentity { + // The smart contract network (e.g.: 'mainnet', 'rinkeby', 'bank_sandbox') + network?: string; +} + /** Supported identity types */ export enum TYPE { ETHEREUM_ADDRESS = 'ethereumAddress', + ETHEREUM_SMART_CONTRACT = 'ethereumSmartContract', } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index eb5966d744..33bd93a24e 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,5 @@ import * as AdvancedLogicTypes from './advanced-logic-types'; +import * as ClientTypes from './client-types'; import * as DataAccessTypes from './data-access-types'; import * as DecryptionProviderTypes from './decryption-provider-types'; import * as EncryptionTypes from './encryption-types'; @@ -6,6 +7,7 @@ import * as ExtensionTypes from './extension-types'; import * as IdentityTypes from './identity-types'; import * as LogTypes from './logger-types'; import * as MultiFormatTypes from './multi-format-types'; +import * as PaymentTypes from './payment-types'; import * as RequestLogicTypes from './request-logic-types'; import * as SignatureProviderTypes from './signature-provider-types'; import * as SignatureTypes from './signature-types'; @@ -14,16 +16,18 @@ import * as TransactionTypes from './transaction-types'; export { AdvancedLogicTypes, - LogTypes, + ClientTypes, + DataAccessTypes, DecryptionProviderTypes, EncryptionTypes, ExtensionTypes, + IdentityTypes, + LogTypes, + MultiFormatTypes, + PaymentTypes, RequestLogicTypes, - DataAccessTypes, - SignatureTypes, SignatureProviderTypes, - IdentityTypes, + SignatureTypes, StorageTypes, TransactionTypes, - MultiFormatTypes, }; diff --git a/packages/types/src/multi-format-types.ts b/packages/types/src/multi-format-types.ts index 8c8e30f7d4..aef5771a91 100644 --- a/packages/types/src/multi-format-types.ts +++ b/packages/types/src/multi-format-types.ts @@ -12,6 +12,8 @@ export enum prefix { ECIES_ENCRYPTED = '02', /** AES256-CBC encrypted data */ AES256_CBC_ENCRYPTED = '03', + /** AES256-GCM encrypted data */ + AES256_GCM_ENCRYPTED = '04', /** Identity Ethereum address */ IDENTITY_ETHEREUM_ADDRESS = '20', diff --git a/packages/types/src/payment-types.ts b/packages/types/src/payment-types.ts new file mode 100644 index 0000000000..24fa6e2c7d --- /dev/null +++ b/packages/types/src/payment-types.ts @@ -0,0 +1,144 @@ +import * as Extension from './extension-types'; +import * as RequestLogic from './request-logic-types'; + +/** Object interface to list the payment network id and its module by currency */ +export interface ISupportedPaymentNetworkByCurrency { + [currency: string]: ISupportedPaymentNetworkByNetwork; +} + +/** Object interface to list the payment network module by network */ +export interface ISupportedPaymentNetworkByNetwork { + [network: string]: IPaymentNetworkModuleByType; +} + +/** Object interface to list the payment network module by id */ +export interface IPaymentNetworkModuleByType { + [type: string]: any; +} + +/** Interface to create a payment network */ +export interface IPaymentNetworkCreateParameters { + id: PAYMENT_NETWORK_ID; + parameters: any; +} + +/** Parameters to create a request with reference based payment network */ +export interface IReferenceBasedCreationParameters { + paymentAddress?: string; + refundAddress?: string; + salt?: string; +} + +/** Interface of the class to manage a payment network */ +export interface IPaymentNetwork { + createExtensionsDataForCreation: (paymentNetworkCreationParameters: any) => Promise; + createExtensionsDataForAddRefundInformation: (parameters: any) => any; + createExtensionsDataForAddPaymentInformation: (parameters: any) => any; + getBalance(request: RequestLogic.IRequest): Promise>; +} + +/** Interface of the class to manage the bitcoin provider API */ +export interface IBitcoinDetectionProvider { + getAddressBalanceWithEvents: ( + bitcoinNetworkId: number, + address: string, + eventName: EVENTS_NAMES, + ) => Promise>; +} + +/** Interface for balances and the events link to the payments and refund */ +export interface IBalanceWithEvents { + balance: string | null; + events: Array>; + error?: IBalanceError; +} + +/** Interface for error encounter when getting the balance */ +export interface IBalanceError { + message: string; + code: BALANCE_ERROR_CODE; +} + +/** Balance error codes */ +export enum BALANCE_ERROR_CODE { + UNKNOWN, + WRONG_EXTENSION, + NETWORK_NOT_SUPPORTED, + VERSION_NOT_SUPPORTED, +} + +/** payment network event */ +export interface IPaymentNetworkEvent { + amount: string; + name: EVENTS_NAMES; + parameters?: TEventParameters; + timestamp?: number; +} + +/** payment network event names */ +export enum EVENTS_NAMES { + PAYMENT = 'payment', + REFUND = 'refund', +} + +/** List of payment networks available (abstract the extensions type) */ +export enum PAYMENT_NETWORK_ID { + BITCOIN_ADDRESS_BASED = Extension.ID.PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED, + TESTNET_BITCOIN_ADDRESS_BASED = Extension.ID.PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED, + ERC20_ADDRESS_BASED = Extension.ID.PAYMENT_NETWORK_ERC20_ADDRESS_BASED, + ERC20_PROXY_CONTRACT = Extension.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT, + ETH_INPUT_DATA = Extension.ID.PAYMENT_NETWORK_ETH_INPUT_DATA, + DECLARATIVE = Extension.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, +} + +/** Generic info retriever interface */ +export interface IPaymentNetworkInfoRetriever< + TPaymentNetworkEvent extends IPaymentNetworkEvent<{}> +> { + getTransferEvents(): Promise; +} + +/** Parameters for events of ERC20 payments */ +export interface IERC20PaymentEventParameters { + from?: string; + to: string; + block?: number; + txHash?: string; +} + +/** ERC20 Payment Network Event */ +export type ERC20PaymentNetworkEvent = IPaymentNetworkEvent; +/** ERC20 BalanceWithEvents */ +export type ERC20BalanceWithEvents = IBalanceWithEvents; + +/** Parameters for events of ETH payments */ +export interface IETHPaymentEventParameters { + block?: number; + confirmations?: number; + txHash?: string; +} +/** ETH Payment Network Event */ +export type ETHPaymentNetworkEvent = IPaymentNetworkEvent; +/** ETH BalanceWithEvents */ +export type ETHBalanceWithEvents = IBalanceWithEvents; + +/** Parameters for events of BTC payments */ +export interface IBTCPaymentEventParameters { + block?: number; + txHash?: string; +} +/** BTC Payment Network Event */ +export type BTCPaymentNetworkEvent = IPaymentNetworkEvent; +/** BTC BalanceWithEvents */ +export type BTCBalanceWithEvents = IBalanceWithEvents; + +/** Parameters for events of Declarative payments */ +export interface IDeclarativePaymentEventParameters { + note?: string; +} +/** Declarative Payment Network Event */ +export type DeclarativePaymentNetworkEvent = IPaymentNetworkEvent< + IDeclarativePaymentEventParameters +>; +/** Declarative BalanceWithEvents */ +export type DeclarativeBalanceWithEvents = IBalanceWithEvents; diff --git a/packages/types/src/request-logic-types.ts b/packages/types/src/request-logic-types.ts index 4a9dab4879..08bc701ae8 100644 --- a/packages/types/src/request-logic-types.ts +++ b/packages/types/src/request-logic-types.ts @@ -1,10 +1,10 @@ +import { EventEmitter } from 'events'; + import * as Encryption from './encryption-types'; import * as Extension from './extension-types'; import * as Identity from './identity-types'; import * as Signature from './signature-types'; -const bigNumber: any = require('bn.js'); - /** Request Logic layer */ export interface IRequestLogic { createRequest: ( @@ -26,27 +26,27 @@ export interface IRequestLogic { requestParameters: IAcceptParameters, signerIdentity: Identity.IIdentity, validate?: boolean, - ) => Promise; + ) => Promise; cancelRequest: ( requestParameters: ICancelParameters, signerIdentity: Identity.IIdentity, validate?: boolean, - ) => Promise; + ) => Promise; increaseExpectedAmountRequest: ( requestParameters: IIncreaseExpectedAmountParameters, signerIdentity: Identity.IIdentity, validate?: boolean, - ) => Promise; + ) => Promise; reduceExpectedAmountRequest: ( requestParameters: IReduceExpectedAmountParameters, signerIdentity: Identity.IIdentity, validate?: boolean, - ) => Promise; + ) => Promise; addExtensionsDataRequest: ( requestParameters: IAddExtensionsDataParameters, signerIdentity: Identity.IIdentity, validate?: boolean, - ) => Promise; + ) => Promise; getRequestFromId: (topic: string) => Promise; getRequestsByTopic: ( topic: any, @@ -72,6 +72,14 @@ export interface IRequestLogicReturn { meta: IReturnMeta; } +/** return of IRequestLogic functions with events */ +export interface IRequestLogicReturnWithConfirmation extends EventEmitter { + /** result of the execution */ + result?: any; + /** meta information */ + meta: IReturnMeta; +} + /** meta data given by the layer below (transaction manager) */ export interface IReturnMeta { transactionManagerMeta: any; @@ -79,18 +87,18 @@ export interface IReturnMeta { } /** return of the function createRequest */ -export interface IReturnCreateRequest extends IRequestLogicReturn { +export interface IReturnCreateRequest extends IRequestLogicReturnWithConfirmation { result: { requestId: RequestId }; } /** return of the function getFirstRequestFromTopic */ export interface IReturnGetRequestFromId extends IRequestLogicReturn { - result: { request: IRequest | null }; + result: { request: IRequest | null; pending: IPendingRequest | null }; } /** return of the function getRequestsByTopic */ export interface IReturnGetRequestsByTopic extends IRequestLogicReturn { - result: { requests: IRequest[] }; + result: { requests: Array<{ request: IRequest | null; pending: IPendingRequest | null }> }; } /** Interface of a request logic action */ @@ -139,13 +147,16 @@ export interface IRequest { nonce?: number; } +/** Pending data of a request in request logic */ +export type IPendingRequest = Partial; + /** Extensions state indexed by their Id */ export interface IExtensionStates { [key: string]: Extension.IState; } /** Amounts in request logic */ -export type Amount = number | string | typeof bigNumber; +export type Amount = number | string; /** RequestId */ export type RequestId = string; @@ -256,6 +267,8 @@ export enum CURRENCY { /** States of a request */ export enum STATE { + // use for upper layer (trick to avoid headache with retyping request in upper layer) + PENDING = 'pending', CREATED = 'created', ACCEPTED = 'accepted', CANCELED = 'canceled', diff --git a/packages/types/src/storage-types.ts b/packages/types/src/storage-types.ts index 67e778886d..e21ca30b76 100644 --- a/packages/types/src/storage-types.ts +++ b/packages/types/src/storage-types.ts @@ -1,12 +1,17 @@ +import { EventEmitter } from 'events'; + const bigNumber: any = require('bn.js'); /** Interface of the storage */ export interface IStorage { initialize: () => Promise; - append: (data: string) => Promise; + append: (data: string) => Promise; read: (dataId: string) => Promise; readMany: (dataIds: string[]) => Promise; getData: (options?: ITimestampBoundaries) => Promise; + getIgnoredData: () => Promise; + _ipfsAdd?: (data: string) => Promise; + _getStatus: (detailed?: boolean) => Promise; } /** An extensible template that declares a generic meta */ @@ -20,6 +25,13 @@ export interface ITimestampBoundaries { to?: number; } +/** Result of the append (IEntry + EventEmitter) */ +export interface IAppendResult extends EventEmitter { + id: string; + content: string; + meta: IEntryMetadata; +} + /** One entry on the storage layer */ export interface IEntry extends IWithMeta { id: string; @@ -43,16 +55,34 @@ export interface IEntryMetadata { /** Size in bytes of the file on ipfs */ size: number; }; + /** Enum of state possible for data */ + state: ContentState; + /** meta about local storing */ + local?: ILocalMetadata; /** timestamp of the data */ timestamp: number; } +/** Local storage meta data */ +export interface ILocalMetadata { + location: string; +} + /** One entry on the ethereum smart contract */ export interface IEthereumEntry extends IWithMeta { /** data id of the persisted data */ hash: string; /** parameters used to compute fees */ feesParameters: IFeesParameters; + /** error encounter */ + error?: { type: ErrorEntries; message: string }; +} + +/** Enum of state possible for data */ +export enum ErrorEntries { + IPFS_CONNECTION_ERROR, + INCORRECT_FILE, + WRONG_FEES, } /** A list of ethereum entries with the last block timestamp these entries were fetched from */ @@ -129,6 +159,9 @@ export enum StorageSystemType { /** Ethereum and IPFS */ ETHEREUM_IPFS = 'ethereumIpfs', + /** Storage in local, only used for node caching for the moment */ + LOCAL = 'local', + /** Mock storage, in memory. Used for local development. Should not be used in production */ IN_MEMORY_MOCK = 'inMemoryMock', } @@ -140,6 +173,12 @@ export interface IIpfsObject { content: string; } +/** interface of ipfs meta */ +export interface IIpfsMeta { + ipfsHash: string; + ipfsSize: number; +} + /** Configuration for the pinRequest method */ export interface IPinRequestConfiguration { delayBetweenCalls: number; @@ -147,6 +186,15 @@ export interface IPinRequestConfiguration { timeout: number; } +/** Configuration for the pinRequest method */ + +export interface IIgnoredDataId { + entry: IEthereumEntry; + lastTryTimestamp: number; + iteration: number; + toRetry: boolean; +} + /** Gas price type */ export enum GasPriceType { FAST = 'fast', @@ -165,3 +213,9 @@ export interface IIpfsErrorHandlingConfiguration { delayBetweenRetries: number; maxRetries: number; } + +/** Enum of state possible for data */ +export enum ContentState { + PENDING = 'pending', + CONFIRMED = 'confirmed', +} diff --git a/packages/types/src/transaction-types.ts b/packages/types/src/transaction-types.ts index 24a4ed0eb3..307fc93708 100644 --- a/packages/types/src/transaction-types.ts +++ b/packages/types/src/transaction-types.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from 'events'; import * as Encryption from './encryption-types'; /** Transaction Manager interface */ @@ -29,7 +30,7 @@ export interface ITimestampBoundaries { } /** return interface for PersistTransaction */ -export interface IReturnPersistTransaction { +export interface IReturnPersistTransaction extends EventEmitter { /** meta information */ meta: { /** meta-data from the layer below */ @@ -53,7 +54,7 @@ export interface IReturnGetTransactions { ignoredTransactions: Array; }; /** result of the execution */ - result: { transactions: Array }; + result: { transactions: Array }; } /** return interface for getTransactionsByChannelId */ @@ -68,23 +69,28 @@ export interface IReturnGetTransactionsByChannels { ignoredTransactions: { [key: string]: Array }; }; /** result of the execution */ - result: { transactions: { [key: string]: Array } }; + result: { transactions: { [key: string]: Array } }; } /** Persisted Transaction in data-access */ export interface IPersistedTransaction { data?: ITransactionData; encryptedData?: ITransactionData; - /** Hash of the data before encryption */ - hash?: string; /** Symmetric key encrypted with asymmetric key from the parties keys, indexed by the hash of their identities */ keys?: IKeysDictionary; /** Encryption method */ encryptionMethod?: string; } +/** Enum of state possible for an action */ +export enum TransactionState { + PENDING = 'pending', + CONFIRMED = 'confirmed', +} + /** Transaction confirmed */ -export interface IConfirmedTransaction { +export interface ITimestampedTransaction { + state: TransactionState; transaction: IPersistedTransaction; timestamp: number; } @@ -94,7 +100,7 @@ export type ITransactionData = string; /** Ignored transaction */ export interface IIgnoredTransaction { - transaction: IConfirmedTransaction; + transaction: ITimestampedTransaction; reason: string; } diff --git a/packages/usage-examples/.vscode/settings.json b/packages/usage-examples/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/usage-examples/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/usage-examples/CHANGELOG.md b/packages/usage-examples/CHANGELOG.md index 7defa7afe4..1a5b7d7b73 100644 --- a/packages/usage-examples/CHANGELOG.md +++ b/packages/usage-examples/CHANGELOG.md @@ -3,6 +3,301 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.11.0) (2020-06-29) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.10.0) (2020-05-04) + + +### Features + +* add getIgnoredData() to the ethereum storage ([#206](https://github.com/RequestNetwork/requestNetwork/issues/206)) ([255d2dc](https://github.com/RequestNetwork/requestNetwork/commit/255d2dc22ce0158ba3e6ce6766efece6e4c054cb)) + + + +# 0.16.0 (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.9.0) (2020-04-21) + + +### Features + +* add entry point to request node to get monitoring status ([#191](https://github.com/RequestNetwork/requestNetwork/issues/191)) ([1d9c239](https://github.com/RequestNetwork/requestNetwork/commit/1d9c239f5de5143cd54c3470b42786eff17748f6)) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.8.0) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.7.0) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +# [0.6.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.6.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + +### Features + +* buffered ethereum storage ([#113](https://github.com/RequestNetwork/requestNetwork/issues/113)) ([fe4ece6](https://github.com/RequestNetwork/requestNetwork/commit/fe4ece6a1768155182be2d3ebb2908501f571912)) +* confirmed & pending state in the highest layers ([#119](https://github.com/RequestNetwork/requestNetwork/issues/119)) ([9424dc0](https://github.com/RequestNetwork/requestNetwork/commit/9424dc0c9482208fdbe714f8d29f5deed68711de)) +* payment-processor new package ([#130](https://github.com/RequestNetwork/requestNetwork/issues/130)) ([a2ce521](https://github.com/RequestNetwork/requestNetwork/commit/a2ce521736e0607d3116347b42ecbfc6ba52d1b4)) +* persist transaction with custom ethereum provider ([#106](https://github.com/RequestNetwork/requestNetwork/issues/106)) ([61b215f](https://github.com/RequestNetwork/requestNetwork/commit/61b215fb8335d01dfa069d7f7899dd5b33749692)) + + + +# 0.12.0 (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.3](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.5.3) (2020-01-16) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.2](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.5.2) (2019-12-18) + + +### Bug Fixes + +* backward compatibility issue + removing test failing for external reason ([#93](https://github.com/RequestNetwork/requestNetwork/issues/93)) ([9a405dc](https://github.com/RequestNetwork/requestNetwork/commit/9a405dcc66b36a9a4a4b885dea2cd50abaad2725)) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.5.1](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.5.0...@requestnetwork/usage-examples@0.5.1) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/usage-examples + + + + + # [0.5.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/usage-examples@0.4.4...@requestnetwork/usage-examples@0.5.0) (2019-11-20) diff --git a/packages/usage-examples/package.json b/packages/usage-examples/package.json index 8c355a8587..88aceb23f9 100644 --- a/packages/usage-examples/package.json +++ b/packages/usage-examples/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/usage-examples", - "version": "0.5.0", + "version": "0.11.0", "private": true, "description": "Usage examples of Request Network.", "keywords": [ @@ -21,31 +21,33 @@ }, "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "start": "ts-node src/request-client-js.ts && ts-node src/request-client-js-declarative-request.ts && ts-node src/request-client-js-erc20-request.ts && ts-node src/request-logic-clear-request.ts && ts-node src/request-logic-encrypted-request.ts", + "start": "ts-node src/request-client-js-declarative-request.ts && ts-node src/request-client-js-erc20-request.ts && ts-node src/request-logic-clear-request.ts && ts-node src/request-logic-encrypted-request.ts", "prepare": "yarn run build" }, "dependencies": { - "@requestnetwork/data-access": "0.5.2", - "@requestnetwork/epk-decryption": "0.3.3", - "@requestnetwork/epk-signature": "0.5.4", - "@requestnetwork/multi-format": "0.2.1", - "@requestnetwork/request-client.js": "0.9.0", - "@requestnetwork/request-logic": "0.8.0", - "@requestnetwork/transaction-manager": "0.8.1", - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0" + "@requestnetwork/data-access": "0.11.0", + "@requestnetwork/epk-decryption": "0.3.12", + "@requestnetwork/epk-signature": "0.5.13", + "@requestnetwork/multi-format": "0.3.0", + "@requestnetwork/payment-processor": "0.18.0", + "@requestnetwork/request-client.js": "0.18.0", + "@requestnetwork/request-logic": "0.14.0", + "@requestnetwork/transaction-manager": "0.14.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", + "ethers": "4.0.45" }, "devDependencies": { "@typescript-eslint/parser": "1.2.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", "prettier": "1.16.4", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" } diff --git a/packages/usage-examples/src/mock/mock-storage.ts b/packages/usage-examples/src/mock/mock-storage.ts index ce274cf564..e2cd8dda54 100644 --- a/packages/usage-examples/src/mock/mock-storage.ts +++ b/packages/usage-examples/src/mock/mock-storage.ts @@ -1,21 +1,29 @@ -/* eslint-disable spellcheck/spell-checker */ -// Copy from packages/request-client.js/src/mock-storage.ts - import MultiFormat from '@requestnetwork/multi-format'; import { StorageTypes } from '@requestnetwork/types'; import Utils from '@requestnetwork/utils'; +import { EventEmitter } from 'events'; /** * Storage layer implemented with in-memory hashmap, to be used for testing. */ export default class MockStorage implements StorageTypes.IStorage { - private data: { [key: string]: { content: string; timestamp: number } } = {}; + private data: { + [key: string]: { state: StorageTypes.ContentState; content: string; timestamp: number }; + } = {}; public async initialize(): Promise { return; } - public async append(content: string): Promise { + public async _ipfsAdd(): Promise { + throw Error('will never be used'); + } + + public async _getStatus(): Promise { + throw Error('will never be used'); + } + + public async append(content: string): Promise { if (!content) { throw Error('Error: no content provided'); } @@ -23,16 +31,31 @@ export default class MockStorage implements StorageTypes.IStorage { const nowTimestampInSec = Utils.getCurrentTimestampInSecond(); - this.data[hash] = { content, timestamp: nowTimestampInSec }; + this.data[hash] = { + content, + state: StorageTypes.ContentState.PENDING, + timestamp: nowTimestampInSec, + }; - return { - content: '', + const resultData = { + content, id: hash, meta: { + state: StorageTypes.ContentState.PENDING, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, timestamp: nowTimestampInSec, }, }; + const result = Object.assign(new EventEmitter(), resultData); + + // emit confirmed + setTimeout(() => { + this.data[hash].state = StorageTypes.ContentState.CONFIRMED; + result.emit('confirmed', resultData); + // tslint:disable-next-line:no-magic-numbers + }, 100); + + return result; } public async read(id: string): Promise { @@ -43,6 +66,7 @@ export default class MockStorage implements StorageTypes.IStorage { content: this.data[id].content, id, meta: { + state: this.data[id].state, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, timestamp: this.data[id].timestamp, }, @@ -54,10 +78,11 @@ export default class MockStorage implements StorageTypes.IStorage { } public async getData(): Promise { - const entries = Object.entries(this.data).map(([id, { content, timestamp }]) => ({ + const entries = Object.entries(this.data).map(([id, { content, state, timestamp }]) => ({ content, id, meta: { + state, storageType: StorageTypes.StorageSystemType.IN_MEMORY_MOCK, timestamp, }, @@ -70,4 +95,8 @@ export default class MockStorage implements StorageTypes.IStorage { lastTimestamp: nowTimestampInSec, }; } + + public async getIgnoredData(): Promise { + return []; + } } diff --git a/packages/usage-examples/src/pay-erc20-request.ts b/packages/usage-examples/src/pay-erc20-request.ts new file mode 100644 index 0000000000..78b66a5be4 --- /dev/null +++ b/packages/usage-examples/src/pay-erc20-request.ts @@ -0,0 +1,32 @@ +import { Wallet } from 'ethers'; + +import { + approveErc20, + hasErc20Approval, + hasSufficientFunds, + payRequest, +} from '@requestnetwork/payment-processor'; +import { RequestNetwork } from '@requestnetwork/request-client.js'; + +/* tslint:disable:no-floating-promises */ + +// for demo purpose +const wallet = Wallet.createRandom(); +const requestNetwork = new RequestNetwork(); + +// tslint:disable-next-line: typedef +(async () => { + const account = wallet.address; + + const request = await requestNetwork.fromRequestId('REQUEST_ID'); + const requestData = request.getData(); + if (!(await hasSufficientFunds(requestData, account))) { + throw new Error('You do not have enough funds to pay this request'); + } + if (!(await hasErc20Approval(requestData, account))) { + const approvalTx = await approveErc20(requestData, wallet); + await approvalTx.wait(1); + } + const tx = await payRequest(requestData, wallet); + await tx.wait(1); +})(); diff --git a/packages/usage-examples/src/pay-eth-request.ts b/packages/usage-examples/src/pay-eth-request.ts new file mode 100644 index 0000000000..88237df019 --- /dev/null +++ b/packages/usage-examples/src/pay-eth-request.ts @@ -0,0 +1,22 @@ +import { hasSufficientFunds, payRequest } from '@requestnetwork/payment-processor'; +import { RequestNetwork } from '@requestnetwork/request-client.js'; +import { Wallet } from 'ethers'; + +/* tslint:disable:no-floating-promises */ + +// for demo purpose +const wallet = Wallet.createRandom(); +const requestNetwork = new RequestNetwork(); + +// tslint:disable-next-line: typedef +(async () => { + const account = wallet.address; + + const request = await requestNetwork.fromRequestId('REQUEST_ID'); + const requestData = request.getData(); + if (!(await hasSufficientFunds(requestData, account))) { + throw new Error('You do not have enough funds to pay this request'); + } + const tx = await payRequest(requestData, wallet); + await tx.wait(1); +})(); diff --git a/packages/usage-examples/src/request-client-js-declarative-request.ts b/packages/usage-examples/src/request-client-js-declarative-request.ts index 071f237331..de860bf36b 100644 --- a/packages/usage-examples/src/request-client-js-declarative-request.ts +++ b/packages/usage-examples/src/request-client-js-declarative-request.ts @@ -32,8 +32,8 @@ const requestInfo: RequestNetwork.Types.IRequestInfo = { payer: payerIdentity, }; -const paymentNetwork: RequestNetwork.Types.IPaymentNetworkCreateParameters = { - id: RequestNetwork.Types.PAYMENT_NETWORK_ID.DECLARATIVE, +const paymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.DECLARATIVE, parameters: { // eslint-disable-next-line spellcheck/spell-checker paymentInformation: { IBAN: 'FR89370400440532013000', BIC: 'SABAIE2D' }, @@ -57,6 +57,9 @@ const requestNetwork = new RequestNetwork.RequestNetwork({ signer: payeeIdentity, }); + // wait the confirmation + await request.waitForConfirmation(); + // the payer can declare that he sent a payment await request.declareSentPayment('11000', 'payment initiated from the bank', payerIdentity); @@ -73,16 +76,14 @@ const requestNetwork = new RequestNetwork.RequestNetwork({ await request.declareSentRefund('1000', 'refund initiated from the bank', payeeIdentity); // the payer can declare that he received a payment - await request.declareReceivedRefund('700', 'refund received', payerIdentity); + let requestData = await request.declareReceivedRefund('700', 'refund received', payerIdentity); - const requestData = request.getData(); + requestData = await new Promise((resolve): any => request.on('confirmed', resolve)); - // Get the balance if (requestData.balance) { console.log('request balance: ', requestData.balance.balance); console.log('request balance events: ', requestData.balance.events); } - // Get payment network information const paymentNetworkInformation = requestData.extensions['pn-any-declarative']; console.log('Payment network, values declared: ', paymentNetworkInformation.values); diff --git a/packages/usage-examples/src/request-client-js-erc20-request.ts b/packages/usage-examples/src/request-client-js-erc20-request.ts index 71e74dd244..a273708092 100644 --- a/packages/usage-examples/src/request-client-js-erc20-request.ts +++ b/packages/usage-examples/src/request-client-js-erc20-request.ts @@ -27,8 +27,8 @@ const requestInfo: RequestNetwork.Types.IRequestInfo = { payer: payerIdentity, }; -const paymentNetwork: RequestNetwork.Types.IPaymentNetworkCreateParameters = { - id: RequestNetwork.Types.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED, +const paymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED, parameters: { // eslint-disable-next-line spellcheck/spell-checker paymentAddress: '0x627306090abaB3A6e1400e9345bC60c78a8BEf57', @@ -67,6 +67,16 @@ requestNetwork .then(request => { console.log('clear request:'); console.log(request); + request + .waitForConfirmation() + .then(confirmedRequest => { + console.log('clear confirmed request:'); + console.log(confirmedRequest); + }) + .catch(error => { + console.error(error.message || error); + process.exit(1); + }); }) .catch(error => { console.error(error.message || error); diff --git a/packages/usage-examples/src/request-client-js.ts b/packages/usage-examples/src/request-client-js.ts index 0e08da2dad..b3c6ea1060 100644 --- a/packages/usage-examples/src/request-client-js.ts +++ b/packages/usage-examples/src/request-client-js.ts @@ -47,8 +47,8 @@ const requestInfo: RequestNetwork.Types.IRequestInfo = { payer: payerIdentity, }; -const paymentNetwork: RequestNetwork.Types.IPaymentNetworkCreateParameters = { - id: RequestNetwork.Types.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, +const paymentNetwork: RequestNetwork.Types.Payment.IPaymentNetworkCreateParameters = { + id: RequestNetwork.Types.Payment.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, parameters: { // eslint-disable-next-line spellcheck/spell-checker paymentAddress: '1LEMZPBit6tTtjXfaEfz4yYmTuctHWoMV', @@ -88,6 +88,16 @@ requestNetwork .then(request => { console.log('clear request:'); console.log(request.requestId); + request + .waitForConfirmation() + .then(confirmedRequest => { + console.log('clear confirmed request:'); + console.log(confirmedRequest); + }) + .catch(error => { + console.error(error.message || error); + process.exit(1); + }); }) .catch(error => { console.error(error.message || error); @@ -99,6 +109,16 @@ requestNetwork .then(request => { console.log('encrypted request:'); console.log(request.requestId); + request + .waitForConfirmation() + .then(confirmedRequest => { + console.log('encrypted confirmed request:'); + console.log(confirmedRequest); + }) + .catch(error => { + console.error(error.message || error); + process.exit(1); + }); }) .catch(error => { console.error(error.message || error); diff --git a/packages/usage-examples/tsconfig.json b/packages/usage-examples/tsconfig.json index 745001f7ad..0b38ded302 100644 --- a/packages/usage-examples/tsconfig.json +++ b/packages/usage-examples/tsconfig.json @@ -10,6 +10,7 @@ { "path": "../epk-decryption" }, { "path": "../epk-signature" }, { "path": "../multi-format" }, + { "path": "../payment-processor" }, { "path": "../transaction-manager" }, { "path": "../request-logic" }, { "path": "../request-client.js" }, diff --git a/packages/utils/.vscode/settings.json b/packages/utils/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/utils/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 88eb0a56ec..95c1570bcc 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -3,6 +3,258 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.16.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.16.0) (2020-06-29) + + +### Features + +* add the identity ethereumSmartContract to the request logic ([#218](https://github.com/RequestNetwork/requestNetwork/issues/218)) ([66d97e0](https://github.com/RequestNetwork/requestNetwork/commit/66d97e00dee7305088cb94a0edf542fe4d0bbd56)) +* amount are only number or string ([#223](https://github.com/RequestNetwork/requestNetwork/issues/223)) ([7a35bde](https://github.com/RequestNetwork/requestNetwork/commit/7a35bde63f78b9305819a80e97022fca7e9494d2)) +* replace symmetric encryption algorithm by aes-256-gcm ([#233](https://github.com/RequestNetwork/requestNetwork/issues/233)) ([969bebe](https://github.com/RequestNetwork/requestNetwork/commit/969bebeb99b4bc2fdd31405a162934cfdff6db05)) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.15.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.15.0) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.14.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.14.0) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.13.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.13.0) (2020-04-06) + + +### Bug Fixes + +* check payee and payer identity format at the creation ([#187](https://github.com/RequestNetwork/requestNetwork/issues/187)) ([4a19b24](https://github.com/RequestNetwork/requestNetwork/commit/4a19b241fb057d153ac7693e85a7e1d3bb6cb9e0)) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.12.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.12.0) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.11.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.11.0) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.10.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.10.0) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.9.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.9.0) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + +# [0.8.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.7.0...@requestnetwork/utils@0.8.0) (2019-12-04) + + +### Features + +* add ETH paymentNetwork to request-client ([#617](https://github.com/RequestNetwork/requestNetwork/issues/617)) ([84ed64e](https://github.com/RequestNetwork/requestNetwork/commit/84ed64ebf96a296155dc2d4d5e6c538344fb881b)) + + + + + # [0.7.0](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/utils@0.6.2...@requestnetwork/utils@0.7.0) (2019-11-20) diff --git a/packages/utils/package.json b/packages/utils/package.json index d448623659..5cba113c31 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/utils", - "version": "0.7.0", + "version": "0.16.0", "publishConfig": { "access": "public" }, @@ -32,36 +32,38 @@ ], "scripts": { "build": "tsc -b", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", "prepare": "yarn run build", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/types": "0.9.0", + "@requestnetwork/types": "0.17.0", "bn.js": "4.11.8", - "eth-crypto": "1.5.0" + "eth-crypto": "1.5.2" }, "devDependencies": { "@types/bn.js": "4.11.5", "@types/chai": "4.1.7", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", + "@types/sinon": "7.5.0", "@typescript-eslint/parser": "1.2.0", "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-spies": "1.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", - "nyc": "13.2.0", + "mocha": "6.2.2", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", - "sinon": "7.3.2", + "sinon": "7.5.0", "source-map-support": "0.5.13", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2" }, diff --git a/packages/utils/src/amount.ts b/packages/utils/src/amount.ts index ac0f23b18c..a765c6d67b 100644 --- a/packages/utils/src/amount.ts +++ b/packages/utils/src/amount.ts @@ -23,8 +23,7 @@ const regexInteger = RegExp(/^[\d]+$/); */ function isValid(amount: RequestLogicTypes.Amount): boolean { return ( - (bigNumber.isBN(amount) && !amount.isNeg()) || - (Utils.isString(amount) && regexInteger.test(amount)) || + (Utils.isString(amount) && regexInteger.test(amount as string)) || (typeof amount === 'number' && (Number.isSafeInteger(Number(amount)) && Number(amount) >= 0)) ); } @@ -45,9 +44,9 @@ function add(amount: RequestLogicTypes.Amount, delta: RequestLogicTypes.Amount): throw Error('delta must represent a positive integer'); } - amount = new bigNumber(amount); - delta = new bigNumber(delta); - return amount.add(delta).toString(); + const amountBN: typeof bigNumber = new bigNumber(amount); + const deltaBN: typeof bigNumber = new bigNumber(delta); + return amountBN.add(deltaBN).toString(); } /** @@ -68,9 +67,9 @@ function reduce(amount: RequestLogicTypes.Amount, delta: RequestLogicTypes.Amoun throw Error('delta must represent a positive integer'); } - amount = new bigNumber(amount); - delta = new bigNumber(delta); - const newAmount = amount.sub(delta).toString(); + const amountBN: typeof bigNumber = new bigNumber(amount); + const deltaBN: typeof bigNumber = new bigNumber(delta); + const newAmount = amountBN.sub(deltaBN).toString(); // Check if the new amount is valid (basically it is not negative) if (!isValid(newAmount)) { diff --git a/packages/utils/src/crypto.ts b/packages/utils/src/crypto.ts index 4de61cddae..1b3efc4c63 100644 --- a/packages/utils/src/crypto.ts +++ b/packages/utils/src/crypto.ts @@ -11,6 +11,7 @@ export default { CryptoWrapper, EcUtils, generate32BufferKey, + generate8randomBytes, keccak256Hash, normalize, normalizeKeccak256Hash, @@ -67,3 +68,16 @@ function keccak256Hash(data: string): string { async function generate32BufferKey(): Promise { return (await CryptoWrapper.random32Bytes()).toString('base64'); } + +// eslint-disable-next-line spellcheck/spell-checker +/** + * Generate 8 random bytes and return as a hexadecimal string. + * Used for salt in ETH input data. + * Example: 'ea3bc7caf64110ca' + * + * @returns a string of 8 random bytes + */ +async function generate8randomBytes(): Promise { + const random32Bytes = await CryptoWrapper.random32Bytes(); + return random32Bytes.slice(0, 8).toString('hex'); +} diff --git a/packages/utils/src/crypto/crypto-wrapper.ts b/packages/utils/src/crypto/crypto-wrapper.ts index 318d2370bc..cd3b09807e 100644 --- a/packages/utils/src/crypto/crypto-wrapper.ts +++ b/packages/utils/src/crypto/crypto-wrapper.ts @@ -5,15 +5,20 @@ import { createCipheriv, createDecipheriv, randomBytes as cryptoRandomBytes } fr */ export default { decryptWithAes256cbc, + decryptWithAes256gcm, encryptWithAes256cbc, + encryptWithAes256gcm, random32Bytes, }; // Algorithm name used for aes256-cbc encryption with the package 'crypto' const AES_256_CBC_ALGORITHM = 'aes-256-cbc'; +// Algorithm name used for aes256-gcm encryption with the package 'crypto' +const AES_256_GCM_ALGORITHM = 'aes-256-gcm'; -// Size of the initialization vector used for the aes256-cbc encryption +// Size of the initialization vector used for the aes256-cbc & aes256-gcm encryption const INITIALIZATION_VECTOR_LENGTH = 16; +const AUTH_TAG_LENGTH = 16; /** * Generates 32 cryptographically strong pseudo-random bytes @@ -47,6 +52,28 @@ async function encryptWithAes256cbc(data: Buffer, key: Buffer): Promise return Buffer.concat([iv, encrypted, cipher.final()]); } +/** + * Encrypts a buffer using AES-256-gcm plus a random Initialization Vector (IV) + * + * @param data the data to encrypt + * @param key the key that will be used for the encryption + * + * @returns Promise resolving a buffer containing the IV and the encrypted data + */ +async function encryptWithAes256gcm(data: Buffer, key: Buffer): Promise { + // Generate randomly the Initialization Vector + const iv = await randomBytes(INITIALIZATION_VECTOR_LENGTH); + // Create the cipher object to encrypt data + const cipher = createCipheriv(AES_256_GCM_ALGORITHM, key, iv); + + const encrypted = cipher.update(data); + const final = cipher.final(); + const authTag = cipher.getAuthTag(); + + // Concat the IV and the encrypted data, the call of final() makes the cipher not usable and flush the buffer + return Buffer.concat([iv, authTag, encrypted, final]); +} + /** * Decrypts an encrypted buffer using AES-256-cbc plus a random Initialization Vector (IV) * @@ -58,11 +85,41 @@ async function encryptWithAes256cbc(data: Buffer, key: Buffer): Promise async function decryptWithAes256cbc(encryptedAndIv: Buffer, key: Buffer): Promise { // Get the IV const iv = encryptedAndIv.slice(0, INITIALIZATION_VECTOR_LENGTH); + // Get the encrypted data itself const encryptedData = encryptedAndIv.slice(INITIALIZATION_VECTOR_LENGTH); // Create the decipher object const decipher = createDecipheriv(AES_256_CBC_ALGORITHM, key, iv); + // decipher.setAuthTag(authTag); + + // Return the buffer decrypted (the call of final() makes the decipher not usable and flush the buffer) + return Buffer.concat([decipher.update(encryptedData), decipher.final()]); +} + +/** + * Decrypts an encrypted buffer using AES-256-gcm plus a random Initialization Vector (IV) + * + * @param encrypted the data to decrypt + * @param key key of the encryption + * + * @returns Promise resolving a buffer containing the data decrypted + */ +async function decryptWithAes256gcm(encryptedAndIv: Buffer, key: Buffer): Promise { + // Get the IV + const iv = encryptedAndIv.slice(0, INITIALIZATION_VECTOR_LENGTH); + // Get the Auth tag + const authTag = encryptedAndIv.slice( + INITIALIZATION_VECTOR_LENGTH, + INITIALIZATION_VECTOR_LENGTH + AUTH_TAG_LENGTH, + ); + + // Get the encrypted data itself + const encryptedData = encryptedAndIv.slice(INITIALIZATION_VECTOR_LENGTH + AUTH_TAG_LENGTH); + + // Create the decipher object + const decipher = createDecipheriv(AES_256_GCM_ALGORITHM, key, iv); + decipher.setAuthTag(authTag); // Return the buffer decrypted (the call of final() makes the decipher not usable and flush the buffer) return Buffer.concat([decipher.update(encryptedData), decipher.final()]); diff --git a/packages/utils/src/encryption.ts b/packages/utils/src/encryption.ts index dc2223f9b0..115f738caa 100644 --- a/packages/utils/src/encryption.ts +++ b/packages/utils/src/encryption.ts @@ -60,6 +60,17 @@ async function encrypt( }; } + if (encryptionParams.method === EncryptionTypes.METHOD.AES256_GCM) { + const encryptedDataBuffer = await Crypto.CryptoWrapper.encryptWithAes256gcm( + Buffer.from(data, 'utf-8'), + Buffer.from(encryptionParams.key, 'base64'), + ); + return { + type: EncryptionTypes.METHOD.AES256_GCM, + value: encryptedDataBuffer.toString('Base64'), + }; + } + throw new Error('encryptionParams.method not supported'); } @@ -95,5 +106,18 @@ async function decrypt( return dataBuffer.toString(); } + if (encryptedData.type === EncryptionTypes.METHOD.AES256_GCM) { + if (decryptionParams.method !== EncryptionTypes.METHOD.AES256_GCM) { + throw new Error(`decryptionParams.method should be ${EncryptionTypes.METHOD.AES256_GCM}`); + } + + const dataBuffer = await Crypto.CryptoWrapper.decryptWithAes256gcm( + // remove the multi-format padding and decode from the base64 to a buffer + Buffer.from(encryptedData.value, 'base64'), + Buffer.from(decryptionParams.key, 'base64'), + ); + return dataBuffer.toString(); + } + throw new Error('encryptedData method not supported'); } diff --git a/packages/utils/src/identity.ts b/packages/utils/src/identity.ts index 2b82263719..48781e8acd 100644 --- a/packages/utils/src/identity.ts +++ b/packages/utils/src/identity.ts @@ -1,11 +1,18 @@ import { IdentityTypes } from '@requestnetwork/types'; +const supportedIdentities: IdentityTypes.TYPE[] = [ + IdentityTypes.TYPE.ETHEREUM_ADDRESS, + IdentityTypes.TYPE.ETHEREUM_SMART_CONTRACT, +]; + /** * Module to manage Request Logic Identity */ export default { areEqual, + hasError, normalizeIdentityValue, + supportedIdentities, }; /** @@ -30,3 +37,19 @@ function areEqual(id1: IdentityTypes.IIdentity, id2: IdentityTypes.IIdentity): b function normalizeIdentityValue(value: string): string { return value.toLowerCase(); } + +/** + * Checks if the identity has an error + * + * @param id identity to check + * @returns the error or null if valid + */ +function hasError(id: IdentityTypes.IIdentity): string | null { + if (!supportedIdentities.includes(id.type)) { + return 'identity type not supported'; + } + if (id.value.match(/^0x[a-fA-F0-9]{40}$/) === null) { + return 'identity value must be an ethereum address'; + } + return null; +} diff --git a/packages/utils/src/random.ts b/packages/utils/src/random.ts deleted file mode 100644 index 0f038f3700..0000000000 --- a/packages/utils/src/random.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * A collection of functions about randomness generation - */ -export default { - generate8randomBytes, -}; - -// eslint-disable-next-line spellcheck/spell-checker -/** - * Generate 8 random bytes and return as a hexadecimal string. - * Used for salt in ETH input data. - * Example: 'ea3bc7caf64110ca' - * - * @returns a string of 8 random bytes - */ -function generate8randomBytes(): string { - const base16 = 16; - - const generate4randomBytes = (): string => { - // A 4 byte random integer - const randomInteger = Math.floor(Math.random() * Math.pow(2, 4 * 8)); - - // Convert to hexadecimal and padded with 0 - return randomInteger.toString(base16).padStart(8, '0'); - }; - - // Do it in 2 passes because an integer doesn't have enough bits - const high = generate4randomBytes(); - const low = generate4randomBytes(); - return high + low; -} diff --git a/packages/utils/test/amount.test.ts b/packages/utils/test/amount.test.ts index e0192de068..e6efeb469b 100644 --- a/packages/utils/test/amount.test.ts +++ b/packages/utils/test/amount.test.ts @@ -3,7 +3,6 @@ import 'mocha'; const bigNumber: any = require('bn.js'); import Amount from '../src/amount'; -// import * as TestData from '../../request-logic/test/unit/utils/test-data-generator'; const magicIntegerSmall = 10000; const magicIntegerBig = 1000000000000000000000000000000; @@ -26,9 +25,9 @@ describe('Amount', () => { it('cannot valid amount as big integer', () => { expect(Amount.isValid(magicIntegerBig), 'Big integer should not be valid').to.be.false; }); - it('can valid amount as bn', () => { - expect(Amount.isValid(new bigNumber('1000000000000000000000000')), 'BN should be valid').to.be - .true; + it('cannot valid amount as bn', () => { + expect(Amount.isValid(new bigNumber('1000000000000000000000000')), 'BN should not be valid') + .to.be.false; }); it('can valid amount as string representing integer', () => { expect(Amount.isValid('10000'), 'integer as string should be valid').to.be.true; diff --git a/packages/utils/test/crypto.test.ts b/packages/utils/test/crypto.test.ts index 8f8308fdbd..480c814911 100644 --- a/packages/utils/test/crypto.test.ts +++ b/packages/utils/test/crypto.test.ts @@ -107,4 +107,31 @@ describe('Utils.crypto', () => { const randomKey = await crypto.generate32BufferKey(); expect(Buffer.from(randomKey, 'base64').length, 'random32Bytes() error').to.be.equal(32); }); + + /* tslint:disable:no-unused-expression */ + /* tslint:disable:no-magic-numbers */ + describe('generate8randomBytes', () => { + it('generates a 16 characters long string', async () => { + // Do it 20 times because it's random. It's ok, it takes a few milliseconds + for (let i = 0; i < 100; i++) { + expect((await crypto.generate8randomBytes()).length).to.be.equal(16); + } + }); + + it('generates a 16 character of hexademical number', async () => { + // Regex for "at least 16 hexadecimal numbers". Used to validate the salt + const eightHexRegex = /[0-9a-f]{16}/; + + // Do it 20 times because it's random. It's ok, it takes a few milliseconds + for (let i = 0; i < 100; i++) { + expect(eightHexRegex.test(await crypto.generate8randomBytes())).to.be.true; + } + }); + + it('generates unique strings', async () => { + const first = await crypto.generate8randomBytes(); + const second = await crypto.generate8randomBytes(); + expect(first).to.not.equal(second); + }); + }); }); diff --git a/packages/utils/test/crypto/crypto-wrapper.test.ts b/packages/utils/test/crypto/crypto-wrapper.test.ts index 90bcc6f7c3..2be779915b 100644 --- a/packages/utils/test/crypto/crypto-wrapper.test.ts +++ b/packages/utils/test/crypto/crypto-wrapper.test.ts @@ -55,4 +55,34 @@ describe('Utils.cryptoWrapper', () => { ); }); }); + + describe('encryptWithAes256gcm', () => { + it('can encrypt with the aes256-gcm algorithm', async () => { + const encrypted = await CryptoWrapper.encryptWithAes256gcm( + Buffer.from(anyData, 'utf8'), + Buffer.from(arbitraryKey, 'utf8'), + ); + expect(Buffer.isBuffer(encrypted), 'encryptWithAes256gcm() error').to.be.true; + expect(encrypted.length, 'encryptWithAes256gcm() error').to.be.equal(49); + expect( + await CryptoWrapper.decryptWithAes256gcm(encrypted, Buffer.from(arbitraryKey, 'utf8')), + 'decrypt() error', + ).to.be.deep.equal(Buffer.from(anyData, 'utf8')); + }); + }); + describe('decryptWithAes256gcm', () => { + it('can decrypt a message encrypted with the aes256-gcm algorithm', async () => { + const decrypted = await CryptoWrapper.decryptWithAes256gcm( + Buffer.from( + 'TTu/6w1cLS6ToK68ILt56eJ/dJGGbo+z/IwGLEg0WfD/naOONpInlrzQ2Zv1vYL+Vg==', + 'base64', + ), + Buffer.from(arbitraryKey, 'utf8'), + ); + expect(Buffer.isBuffer(decrypted), 'decryptWithAes256gcm() error').to.be.true; + expect(decrypted, 'decryptWithAes256gcm() error').to.be.deep.equal( + Buffer.from(anyData, 'utf8'), + ); + }); + }); }); diff --git a/packages/utils/test/encryption.test.ts b/packages/utils/test/encryption.test.ts index d2b330f683..a7597d7e0f 100644 --- a/packages/utils/test/encryption.test.ts +++ b/packages/utils/test/encryption.test.ts @@ -1,8 +1,11 @@ -import * as chai from 'chai'; import 'mocha'; -const chaiAsPromised = require('chai-as-promised'); +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as spies from 'chai-as-promised'; + chai.use(chaiAsPromised); +chai.use(spies); const expect = chai.expect; import { EncryptionTypes, IdentityTypes } from '@requestnetwork/types'; @@ -33,6 +36,11 @@ const arbitraryAES256cbcEncryptionParams: EncryptionTypes.IEncryptionParameters method: EncryptionTypes.METHOD.AES256_CBC, }; +const arbitraryAES256gcmEncryptionParams: EncryptionTypes.IEncryptionParameters = { + key: '+wqzz0nClfG9MNjEziGDfMPcxo7WwXQ/m/0ESEpmkCs=', + method: EncryptionTypes.METHOD.AES256_GCM, +}; + const data = { attribut1: 'VALUE', attribut2: 'Value', @@ -48,17 +56,14 @@ describe('Encryption', () => { ); }); - it('cannot getIdentityFromEncryptionParams with encryption method not supported', () => { - try { - const params: any = { - method: 'notECIES', - publicKey: otherIdRaw.publicKey, - }; - Encryption.getIdentityFromEncryptionParams(params); - expect(false, 'exception not thrown').to.be.true; - } catch (e) { - expect(e.message, 'exception not right').to.equal('encryptionParams.method not supported'); - } + it('cannot getIdentityFromEncryptionParams with encryption method not supported', async () => { + const params: any = { + method: 'notECIES', + publicKey: otherIdRaw.publicKey, + }; + expect(() => Encryption.getIdentityFromEncryptionParams(params)).to.be.throw( + 'encryptionParams.method not supported', + ); }); }); @@ -89,6 +94,19 @@ describe('Encryption', () => { ).to.be.deep.equal(JSON.stringify(data)); }); + it('can encrypt with AES256-gcm', async () => { + const encryptedData = await Encryption.encrypt( + JSON.stringify(data), + arbitraryAES256gcmEncryptionParams, + ); + expect(encryptedData.value.length, 'encrypt() error').to.be.equal(100); + expect(encryptedData.type, 'encrypt() error').to.be.equal(EncryptionTypes.METHOD.AES256_GCM); + expect( + await Encryption.decrypt(encryptedData, arbitraryAES256gcmEncryptionParams), + 'decrypt() error', + ).to.be.deep.equal(JSON.stringify(data)); + }); + it('cannot encrypt with an encryption method not supported', async () => { const params: any = { method: 'notECIES', @@ -147,6 +165,16 @@ describe('Encryption', () => { otherIdRaw.decryptionParams, ), ).to.eventually.rejectedWith('decryptionParams.method should be aes256-cbc'); + + await expect( + Encryption.decrypt( + { + type: EncryptionTypes.METHOD.AES256_GCM, + value: 'c9a9', + }, + arbitraryAES256cbcEncryptionParams, + ), + ).to.eventually.rejectedWith('decryptionParams.method should be aes256-gcm'); }); }); }); diff --git a/packages/utils/test/random.ts b/packages/utils/test/random.ts deleted file mode 100644 index e711a257ef..0000000000 --- a/packages/utils/test/random.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { expect } from 'chai'; -import 'mocha'; - -import Random from '../src/random'; - -// Regex for "at least 16 hexadecimal numbers". Used to validate the salt -const eightHexRegex = /[0-9a-f]{16}/; - -/* tslint:disable:no-unused-expression */ -/* tslint:disable:no-magic-numbers */ -describe('random/generate8randomBytes', () => { - it('generates a 16 charaters long string', () => { - // Do it 20 times because it's random. It's ok, it takes a few milliseconds - for (let i = 0; i < 100; i++) { - expect(Random.generate8randomBytes().length).to.be.equal(16); - } - }); - - it('generates a 16 charater of hexademical number', () => { - // Do it 20 times because it's random. It's ok, it takes a few milliseconds - for (let i = 0; i < 100; i++) { - expect(eightHexRegex.test(Random.generate8randomBytes())).to.be.true; - } - }); - - it('generates unique strings', () => { - const first = Random.generate8randomBytes(); - const second = Random.generate8randomBytes(); - expect(first).to.not.equal(second); - }); -}); diff --git a/packages/utils/test/signature.test.ts b/packages/utils/test/signature.test.ts index e9802d759f..96bb4efa28 100644 --- a/packages/utils/test/signature.test.ts +++ b/packages/utils/test/signature.test.ts @@ -44,21 +44,18 @@ describe('Signature', () => { }); it('cannot getIdentityFromSignatureParams with signature method not supported', () => { - try { - const params: any = { - method: 'notECDSA', - privateKey: otherIdRaw.privateKey, - }; - Signature.getIdentityFromSignatureParams(params); - expect(false, 'exception not thrown').to.be.true; - } catch (e) { - expect(e.message, 'exception not right').to.equal('signatureParams.method not supported'); - } + const params: any = { + method: 'notECDSA', + privateKey: otherIdRaw.privateKey, + }; + expect(() => Signature.getIdentityFromSignatureParams(params)).to.throw( + 'signatureParams.method not supported', + ); }); }); describe('sign', () => { - it('can sign()', () => { + it('can sign() with ECDSA', () => { const signature = Signature.sign(data, { method: SignatureTypes.METHOD.ECDSA, privateKey: otherIdRaw.privateKey, @@ -73,6 +70,21 @@ describe('Signature', () => { }); }); + it('can sign() with ECDSA_ETHEREUM', () => { + const signature = Signature.sign(data, { + method: SignatureTypes.METHOD.ECDSA_ETHEREUM, + privateKey: otherIdRaw.privateKey, + }); + expect(signature, 'sign() error').to.be.deep.equal({ + data, + signature: { + method: SignatureTypes.METHOD.ECDSA_ETHEREUM, + value: + '0x3fbc7ed9dfa003067f646749d4223def2a69df70371d4f15ec001bc1491cdee40558de1f31fdc7cc5d805a5c4080b54cda3430b29ab14f04e17a5b23fcd39b391b', + }, + }); + }); + it('can sign() with different case', () => { const signature = Signature.sign(dataDiffCase, { method: SignatureTypes.METHOD.ECDSA, @@ -89,21 +101,18 @@ describe('Signature', () => { }); it('cannot sign with signature method not supported', () => { - try { - const params: any = { - method: 'notECDSA', - privateKey: otherIdRaw.privateKey, - }; - Signature.sign(Crypto.normalizeKeccak256Hash(data), params); - expect(false, 'exception not thrown').to.be.true; - } catch (e) { - expect(e.message, 'exception not right').to.equal('signatureParams.method not supported'); - } + const params: any = { + method: 'notECDSA', + privateKey: otherIdRaw.privateKey, + }; + expect(() => Signature.sign(Crypto.normalizeKeccak256Hash(data), params)).to.throw( + 'signatureParams.method not supported', + ); }); }); describe('recover', () => { - it('can recover()', () => { + it('can recover() ECDSA signature', () => { const id = Signature.recover({ data, signature: { @@ -115,6 +124,19 @@ describe('Signature', () => { expect(id, 'recover() error').to.be.deep.equal(otherIdRaw.identity); }); + it('can recover() ECDSA_ETHEREUM signature', () => { + const id = Signature.recover({ + data, + signature: { + method: SignatureTypes.METHOD.ECDSA_ETHEREUM, + value: + '0x3fbc7ed9dfa003067f646749d4223def2a69df70371d4f15ec001bc1491cdee40558de1f31fdc7cc5d805a5c4080b54cda3430b29ab14f04e17a5b23fcd39b391b', + }, + }); + expect(id.value, 'recover() error').to.be.deep.equal(otherIdRaw.identity.value.toLowerCase()); + expect(id.type, 'recover() error').to.be.deep.equal(otherIdRaw.identity.type); + }); + it('can recover() with different case', () => { const id = Signature.recover({ data: dataDiffCase, @@ -128,16 +150,13 @@ describe('Signature', () => { }); it('cannot recover with signature method not supported', () => { - try { - const params: any = { - method: 'notECDSA', - value: '0x00000000000000000000', - }; - Signature.recover({ data, signature: params }); - expect(false, 'exception not thrown').to.be.true; - } catch (e) { - expect(e.message, 'exception not right').to.equal('signatureParams.method not supported'); - } + const params: any = { + method: 'notECDSA', + value: '0x00000000000000000000', + }; + expect(() => Signature.recover({ data, signature: params })).to.throw( + 'signatureParams.method not supported', + ); }); }); }); diff --git a/packages/utils/test/utils.test.ts b/packages/utils/test/utils.test.ts index 19e404bdd2..b8be2be982 100644 --- a/packages/utils/test/utils.test.ts +++ b/packages/utils/test/utils.test.ts @@ -87,10 +87,15 @@ describe('Utils', () => { }); it('getCurrentTimestampInSecond()', () => { + sinon.useFakeTimers(Date.now()); + const time = Math.floor(Date.now() / 1000); expect(Utils.getCurrentTimestampInSecond(), 'getCurrentTimestampInSecond() error').to.be.equal( time, ); + + // Cleanup + sinon.restore(); }); describe('unique', () => { diff --git a/packages/web3-signature/.vscode/settings.json b/packages/web3-signature/.vscode/settings.json new file mode 100644 index 0000000000..1a7d6049b8 --- /dev/null +++ b/packages/web3-signature/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} \ No newline at end of file diff --git a/packages/web3-signature/CHANGELOG.md b/packages/web3-signature/CHANGELOG.md index c839db3ba9..6c898d13ab 100644 --- a/packages/web3-signature/CHANGELOG.md +++ b/packages/web3-signature/CHANGELOG.md @@ -3,6 +3,192 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.14](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.14) (2020-06-29) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.13](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.13) (2020-05-04) + + + +# 0.16.0 (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.12](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.12) (2020-04-21) + + + +# 0.15.0 (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.11](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.11) (2020-04-06) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.10](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.10) (2020-03-23) + + + +# 0.13.0 (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.9](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.9) (2020-02-20) + + +### Bug Fixes + +* ts-node configuration ([#138](https://github.com/RequestNetwork/requestNetwork/issues/138)) ([e2180d5](https://github.com/RequestNetwork/requestNetwork/commit/e2180d507bd87116fdeb3466690b6df0c5187976)) + + + +# 0.12.0 (2020-01-16) + + + +# 0.10.0 (2019-12-04) + + + + + +## [0.4.8](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.8) (2020-01-16) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/web3-signature + + + + + +## [0.4.7](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.7) (2019-12-18) + + + +# 0.10.0 (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/web3-signature + + + + + +## [0.4.6](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.5...@requestnetwork/web3-signature@0.4.6) (2019-12-04) + +**Note:** Version bump only for package @requestnetwork/web3-signature + + + + + ## [0.4.5](https://github.com/RequestNetwork/requestNetwork/compare/@requestnetwork/web3-signature@0.4.4...@requestnetwork/web3-signature@0.4.5) (2019-11-20) **Note:** Version bump only for package @requestnetwork/web3-signature diff --git a/packages/web3-signature/package.json b/packages/web3-signature/package.json index bb8d5a66e5..6fbe13e140 100644 --- a/packages/web3-signature/package.json +++ b/packages/web3-signature/package.json @@ -1,6 +1,6 @@ { "name": "@requestnetwork/web3-signature", - "version": "0.4.5", + "version": "0.4.14", "publishConfig": { "access": "public" }, @@ -34,21 +34,21 @@ "build": "run-s build:commonjs build:umd", "build:commonjs": "tsc -b", "build:umd": "webpack", - "clean": "shx rm -rf dist", + "clean": "shx rm -rf dist tsconfig.tsbuildinfo", "lint": "tslint --project . && eslint \"src/**/*.ts\"", "lint-staged": "lint-staged", - "test": "nyc mocha --require ts-node/register --require source-map-support/register \"test/**/*.ts\"", - "test:watch": "nyc mocha --watch --watch-extensions ts --require ts-node/register --require source-map-support/register \"test/**/*.ts\"" + "test": "nyc mocha --extension ts --require source-map-support/register \"test/**/*.ts\"", + "test:watch": "yarn test --watch" }, "dependencies": { - "@requestnetwork/types": "0.9.0", - "@requestnetwork/utils": "0.7.0", + "@requestnetwork/types": "0.17.0", + "@requestnetwork/utils": "0.16.0", "web3-eth": "1.0.0-beta.37" }, "devDependencies": { "@types/chai": "4.1.7", "@types/chai-spies": "1.0.0", - "@types/mocha": "5.2.6", + "@types/mocha": "5.2.7", "@typescript-eslint/parser": "1.2.0", "amd-loader": "0.0.8", "awesome-typescript-loader": "5.2.1", @@ -57,21 +57,21 @@ "chai-spies": "1.0.0", "duplicate-package-checker-webpack-plugin": "3.0.0", "eslint": "5.13.0", - "eslint-plugin-spellcheck": "0.0.11", + "eslint-plugin-spellcheck": "0.0.14", "eslint-plugin-typescript": "0.14.0", "lint-staged": "8.1.3", - "mocha": "5.2.0", + "mocha": "6.2.2", "npm-run-all": "4.1.5", - "nyc": "13.2.0", + "nyc": "15.0.0", "prettier": "1.16.4", "shx": "0.3.2", "source-map-support": "0.5.13", "terser-webpack-plugin": "1.3.0", - "ts-node": "8.5.2", + "ts-node": "8.6.2", "tslint": "5.12.1", "typescript": "3.7.2", "webpack": "4.38.0", - "webpack-bundle-analyzer": "3.3.2", - "webpack-cli": "3.3.6" + "webpack-bundle-analyzer": "3.6.0", + "webpack-cli": "3.3.10" } } diff --git a/scripts/collect.js b/scripts/collect.js index 487c68309d..2d07b623bd 100644 --- a/scripts/collect.js +++ b/scripts/collect.js @@ -19,6 +19,7 @@ function getDest(module) { 'data-format', 'epk-signature', 'ethereum-storage', + 'payment-processor', 'request-client.js', 'request-logic', 'request-node', diff --git a/yarn.lock b/yarn.lock index effa8c0330..e12b6f41cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,59 +2,859 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== +"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== dependencies: - "@babel/highlight" "^7.0.0" + "@babel/highlight" "^7.8.3" -"@babel/generator@^7.4.0", "@babel/generator@^7.7.2": - version "7.7.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.2.tgz#2f4852d04131a5e17ea4f6645488b5da66ebf3af" - integrity sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ== +"@babel/compat-data@^7.8.6", "@babel/compat-data@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c" + integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g== dependencies: - "@babel/types" "^7.7.2" + browserslist "^4.9.1" + invariant "^2.2.4" + semver "^5.5.0" + +"@babel/core@7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" + integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.4" + "@babel/helpers" "^7.8.4" + "@babel/parser" "^7.8.4" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.4" + "@babel/types" "^7.8.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@^7.7.4", "@babel/core@^7.7.5": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" + integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.0" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.0" + "@babel/parser" "^7.9.0" + "@babel/template" "^7.8.6" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.8.4", "@babel/generator@^7.9.0", "@babel/generator@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" + integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== + dependencies: + "@babel/types" "^7.9.5" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-function-name@^7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz#44a5ad151cfff8ed2599c91682dda2ec2c8430a3" - integrity sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q== +"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" + integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== dependencies: - "@babel/helper-get-function-arity" "^7.7.0" - "@babel/template" "^7.7.0" - "@babel/types" "^7.7.0" + "@babel/types" "^7.8.3" -"@babel/helper-get-function-arity@^7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz#c604886bc97287a1d1398092bc666bc3d7d7aa2d" - integrity sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" + integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== dependencies: - "@babel/types" "^7.7.0" + "@babel/helper-explode-assignable-expression" "^7.8.3" + "@babel/types" "^7.8.3" -"@babel/helper-split-export-declaration@^7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz#1365e74ea6c614deeb56ebffabd71006a0eb2300" - integrity sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA== +"@babel/helper-builder-react-jsx-experimental@^7.9.0": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.5.tgz#0b4b3e04e6123f03b404ca4dfd6528fe6bb92fe3" + integrity sha512-HAagjAC93tk748jcXpZ7oYRZH485RCq/+yEv9SIWezHRPv9moZArTnkUNciUNzvwHUABmiWKlcxJvMcu59UwTg== dependencies: - "@babel/types" "^7.7.0" + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-module-imports" "^7.8.3" + "@babel/types" "^7.9.5" -"@babel/highlight@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" - integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== +"@babel/helper-builder-react-jsx@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz#16bf391990b57732700a3278d4d9a81231ea8d32" + integrity sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/types" "^7.9.0" + +"@babel/helper-compilation-targets@^7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz#dac1eea159c0e4bd46e309b5a1b04a66b53c1dde" + integrity sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw== + dependencies: + "@babel/compat-data" "^7.8.6" + browserslist "^4.9.1" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-class-features-plugin@^7.8.3": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.5.tgz#79753d44017806b481017f24b02fd4113c7106ea" + integrity sha512-IipaxGaQmW4TfWoXdqjY0TzoXQ1HRS0kPpEgvjosb3u7Uedcq297xFqDQiCcQtRRwzIMif+N1MLVI8C5a4/PAA== + dependencies: + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-split-export-declaration" "^7.8.3" + +"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": + version "7.8.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" + integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + regexpu-core "^4.7.0" + +"@babel/helper-define-map@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" + integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" + integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== + dependencies: + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.9.5" + +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-hoist-variables@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" + integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-plugin-utils@7.8.3", "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" + integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== + +"@babel/helper-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" + integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" + integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-wrap-function" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" + integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.6" + "@babel/types" "^7.8.6" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== + +"@babel/helper-wrap-function@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" + integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helpers@^7.8.4", "@babel/helpers@^7.9.0": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" + integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + +"@babel/highlight@^7.8.3": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" + integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== + dependencies: + "@babel/helper-validator-identifier" "^7.9.0" chalk "^2.0.0" - esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.7.2": - version "7.7.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.3.tgz#5fad457c2529de476a248f75b0f090b3060af043" - integrity sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A== +"@babel/parser@^7.4.3", "@babel/parser@^7.7.5", "@babel/parser@^7.8.4", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0", "@babel/parser@^7.9.4": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" + integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== + +"@babel/plugin-proposal-async-generator-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" + integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" + integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" + integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" + integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" + integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + +"@babel/plugin-proposal-object-rest-spread@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" + integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + +"@babel/plugin-proposal-object-rest-spread@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz#3fd65911306d8746014ec0d0cf78f0e39a149116" + integrity sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.9.5" + +"@babel/plugin-proposal-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" + integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" + integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": + version "7.8.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" + integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.8" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-dynamic-import@^7.7.4", "@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@7.8.3", "@babel/plugin-syntax-jsx@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94" + integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" + integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" + integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-typescript@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz#c1f659dda97711a569cef75275f7e15dcaa6cabc" + integrity sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-arrow-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" + integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" + integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + +"@babel/plugin-transform-block-scoped-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" + integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" + integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" + integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-define-map" "^7.8.3" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-split-export-declaration" "^7.8.3" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" + integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-destructuring@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50" + integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" + integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-duplicate-keys@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" + integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" + integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" + integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" + integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" + integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-member-expression-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" + integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-modules-amd@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz#19755ee721912cf5bb04c07d50280af3484efef4" + integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q== + dependencies: + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-commonjs@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940" + integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g== + dependencies: + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-systemjs@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz#e9fd46a296fc91e009b64e07ddaa86d6f0edeb90" + integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-umd@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" + integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== + dependencies: + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" + integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + +"@babel/plugin-transform-new-target@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" + integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-object-super@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" + integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" + integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-property-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" + integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-react-display-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz#70ded987c91609f78353dd76d2fb2a0bb991e8e5" + integrity sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-react-jsx-development@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz#3c2a130727caf00c2a293f0aed24520825dbf754" + integrity sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw== + dependencies: + "@babel/helper-builder-react-jsx-experimental" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-react-jsx-self@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz#f4f26a325820205239bb915bad8e06fcadabb49b" + integrity sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-react-jsx-source@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz#89ef93025240dd5d17d3122294a093e5e0183de0" + integrity sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-react-jsx@^7.9.4": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.4.tgz#86f576c8540bd06d0e95e0b61ea76d55f6cbd03f" + integrity sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw== + dependencies: + "@babel/helper-builder-react-jsx" "^7.9.0" + "@babel/helper-builder-react-jsx-experimental" "^7.9.0" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-regenerator@^7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" + integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" + integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-runtime@^7.7.4": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz#45468c0ae74cc13204e1d3b1f4ce6ee83258af0b" + integrity sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" + integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" + integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-sticky-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" + integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + +"@babel/plugin-transform-template-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" + integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typeof-symbol@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" + integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typescript@^7.9.0": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.4.tgz#4bb4dde4f10bbf2d787fce9707fb09b483e33359" + integrity sha512-yeWeUkKx2auDbSxRe8MusAG+n4m9BFY/v+lPjmQDgOFX5qnySkUY5oXzkp6FwPdsYqnKay6lorXYdC0n3bZO7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-typescript" "^7.8.3" + +"@babel/plugin-transform-unicode-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" + integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/preset-env@^7.7.4": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.5.tgz#8ddc76039bc45b774b19e2fc548f6807d8a8919f" + integrity sha512-eWGYeADTlPJH+wq1F0wNfPbVS1w1wtmMJiYk55Td5Yu28AsdR9AsC97sZ0Qq8fHqQuslVSIYSGJMcblr345GfQ== + dependencies: + "@babel/compat-data" "^7.9.0" + "@babel/helper-compilation-targets" "^7.8.7" + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-proposal-async-generator-functions" "^7.8.3" + "@babel/plugin-proposal-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-json-strings" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-numeric-separator" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.9.5" + "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-chaining" "^7.9.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.8.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.8.3" + "@babel/plugin-transform-async-to-generator" "^7.8.3" + "@babel/plugin-transform-block-scoped-functions" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-classes" "^7.9.5" + "@babel/plugin-transform-computed-properties" "^7.8.3" + "@babel/plugin-transform-destructuring" "^7.9.5" + "@babel/plugin-transform-dotall-regex" "^7.8.3" + "@babel/plugin-transform-duplicate-keys" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator" "^7.8.3" + "@babel/plugin-transform-for-of" "^7.9.0" + "@babel/plugin-transform-function-name" "^7.8.3" + "@babel/plugin-transform-literals" "^7.8.3" + "@babel/plugin-transform-member-expression-literals" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.9.0" + "@babel/plugin-transform-modules-commonjs" "^7.9.0" + "@babel/plugin-transform-modules-systemjs" "^7.9.0" + "@babel/plugin-transform-modules-umd" "^7.9.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.8.3" + "@babel/plugin-transform-object-super" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.9.5" + "@babel/plugin-transform-property-literals" "^7.8.3" + "@babel/plugin-transform-regenerator" "^7.8.7" + "@babel/plugin-transform-reserved-words" "^7.8.3" + "@babel/plugin-transform-shorthand-properties" "^7.8.3" + "@babel/plugin-transform-spread" "^7.8.3" + "@babel/plugin-transform-sticky-regex" "^7.8.3" + "@babel/plugin-transform-template-literals" "^7.8.3" + "@babel/plugin-transform-typeof-symbol" "^7.8.4" + "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.9.5" + browserslist "^4.9.1" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/preset-modules@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" + integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.7.4": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.9.4.tgz#c6c97693ac65b6b9c0b4f25b948a8f665463014d" + integrity sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-transform-react-display-name" "^7.8.3" + "@babel/plugin-transform-react-jsx" "^7.9.4" + "@babel/plugin-transform-react-jsx-development" "^7.9.0" + "@babel/plugin-transform-react-jsx-self" "^7.9.0" + "@babel/plugin-transform-react-jsx-source" "^7.9.0" + +"@babel/preset-typescript@^7.7.4": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz#87705a72b1f0d59df21c179f7c3d2ef4b16ce192" + integrity sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-transform-typescript" "^7.9.0" "@babel/runtime@7.0.0": version "7.0.0" @@ -63,43 +863,43 @@ dependencies: regenerator-runtime "^0.12.0" -"@babel/runtime@^7.3.1": - version "7.7.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a" - integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw== - dependencies: - regenerator-runtime "^0.13.2" - -"@babel/template@^7.4.0", "@babel/template@^7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.0.tgz#4fadc1b8e734d97f56de39c77de76f2562e597d0" - integrity sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.0" - "@babel/types" "^7.7.0" - -"@babel/traverse@^7.4.3": - version "7.7.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.2.tgz#ef0a65e07a2f3c550967366b3d9b62a2dcbeae09" - integrity sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.2" - "@babel/helper-function-name" "^7.7.0" - "@babel/helper-split-export-declaration" "^7.7.0" - "@babel/parser" "^7.7.2" - "@babel/types" "^7.7.2" +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.7.4", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.4.0", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" + integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.6" + "@babel/types" "^7.8.6" + +"@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2" + integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.5" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.9.0" + "@babel/types" "^7.9.5" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.4.0", "@babel/types@^7.7.0", "@babel/types@^7.7.2": - version "7.7.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.2.tgz#550b82e5571dcd174af576e23f0adba7ffc683f7" - integrity sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA== +"@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" + integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== dependencies: - esutils "^2.0.2" + "@babel/helper-validator-identifier" "^7.9.5" lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -156,6 +956,225 @@ fs-extra "^4.0.1" viz.js "^1.8.0" +"@csstools/convert-colors@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" + integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== + +"@docusaurus/core@2.0.0-alpha.49": + version "2.0.0-alpha.49" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-alpha.49.tgz#9d8c87eda6d920e47720fb8363ad6dfad8262c58" + integrity sha512-gZRC7VqT1cel4GruMntluduXoG4H5ROwYLA+yyq3rHt/VgXdBr5BA45Eu11LNvvjbymbeXLY2U/swdsAZj9e0A== + dependencies: + "@babel/core" "^7.7.4" + "@babel/plugin-syntax-dynamic-import" "^7.7.4" + "@babel/plugin-transform-runtime" "^7.7.4" + "@babel/preset-env" "^7.7.4" + "@babel/preset-react" "^7.7.4" + "@babel/preset-typescript" "^7.7.4" + "@babel/runtime" "^7.7.4" + "@docusaurus/utils" "^2.0.0-alpha.49" + "@endiliey/static-site-generator-webpack-plugin" "^4.0.0" + array-flat-polyfill "^1.0.1" + babel-loader "^8.0.6" + babel-plugin-dynamic-import-node "^2.3.0" + cache-loader "^4.1.0" + chalk "^3.0.0" + chokidar "^3.3.0" + classnames "^2.2.6" + commander "^4.0.1" + copy-webpack-plugin "^5.0.5" + core-js "^2.6.5" + css-loader "^3.2.0" + del "^5.1.0" + ejs "^3.0.1" + express "^4.17.1" + fs-extra "^8.1.0" + globby "^10.0.1" + html-minifier-terser "^5.0.2" + html-tags "^3.1.0" + html-webpack-plugin "^4.0.0-beta.11" + import-fresh "^3.2.1" + lodash "^4.17.15" + mini-css-extract-plugin "^0.8.0" + nprogress "^0.2.0" + null-loader "^3.0.0" + optimize-css-assets-webpack-plugin "^5.0.3" + portfinder "^1.0.25" + postcss-loader "^3.0.0" + postcss-preset-env "^6.7.0" + react-dev-utils "^10.2.1" + react-helmet "^6.0.0-beta" + react-loadable "^5.5.0" + react-loadable-ssr-addon "^0.2.0" + react-router "^5.1.2" + react-router-config "^5.1.1" + react-router-dom "^5.1.2" + semver "^6.3.0" + shelljs "^0.8.3" + std-env "^2.2.1" + terser-webpack-plugin "^2.2.1" + wait-file "^1.0.5" + webpack "^4.41.2" + webpack-bundle-analyzer "^3.6.0" + webpack-dev-server "^3.9.0" + webpack-merge "^4.2.2" + webpackbar "^4.0.0" + +"@docusaurus/mdx-loader@^2.0.0-alpha.50": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-alpha.50.tgz#77d179404c7e9a16c0f59b4b637a9fdd74d6dca3" + integrity sha512-S4e+zR0yWnMQQCcmQzWZi3Bl74FeF1E3qzyE4laQHXD1+napUo0j6eY8gFSI8h4mY2AaMQ3smCxqmqUU5IvtpQ== + dependencies: + "@babel/parser" "^7.9.4" + "@babel/traverse" "^7.9.0" + "@mdx-js/mdx" "^1.5.8" + "@mdx-js/react" "^1.5.8" + escape-html "^1.0.3" + fs-extra "^8.1.0" + github-slugger "^1.3.0" + gray-matter "^4.0.2" + loader-utils "^1.2.3" + mdast-util-to-string "^1.1.0" + remark-emoji "^2.1.0" + stringify-object "^3.3.0" + unist-util-visit "^2.0.2" + +"@docusaurus/plugin-content-blog@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-alpha.50.tgz#847c7abebaa364b8394060c0c1255c3f0a125f6f" + integrity sha512-dHlahBYhCt0Orfj1bnrNAViUzylCV0oLpZlFAiJnv3vYSYpJsNtz6MFrhd3k/1agO6VRsUzMOkS7huFrZcIamw== + dependencies: + "@docusaurus/mdx-loader" "^2.0.0-alpha.50" + "@docusaurus/utils" "^2.0.0-alpha.50" + feed "^4.1.0" + fs-extra "^8.1.0" + globby "^10.0.1" + loader-utils "^1.2.3" + lodash.kebabcase "^4.1.1" + +"@docusaurus/plugin-content-docs@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-alpha.50.tgz#67a18a5ee203c66b9211552f73daa9edb009f757" + integrity sha512-jBOq9kqntE+7RSZ4urivohFTEpb37pYVyRETtNFm+ArisBCclEOnxQetk3n36kNDijBKDnGxAVWPJF+zaEAe4Q== + dependencies: + "@docusaurus/mdx-loader" "^2.0.0-alpha.50" + "@docusaurus/utils" "^2.0.0-alpha.50" + execa "^3.4.0" + fs-extra "^8.1.0" + globby "^10.0.1" + import-fresh "^3.2.1" + loader-utils "^1.2.3" + lodash "^4.17.15" + shelljs "^0.8.3" + +"@docusaurus/plugin-content-pages@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-alpha.50.tgz#34a77bc036e9e9a08815c6fef4e9be1c3abe619e" + integrity sha512-fSoQTcJdCh1EFGVvAvNMPaU6eilXM2pRaOgP9GthBktB8iasVxnUM5bpCuFjHXqvEZux1ho8XqrvQgTh06jPsA== + dependencies: + "@docusaurus/types" "^2.0.0-alpha.50" + "@docusaurus/utils" "^2.0.0-alpha.50" + globby "^10.0.1" + +"@docusaurus/plugin-google-analytics@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-alpha.50.tgz#862989777cb1f62b81fb1462dd5cfba34237cddf" + integrity sha512-99tw5yGRhrMfeTFny4dkjLdgeTrE/jbTQGUNHKaTiKQxm+APhiwDoKlptUrkMy4lxwPc3IW1cEcgF8j0oao1EQ== + +"@docusaurus/plugin-google-gtag@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-alpha.50.tgz#4b69c23ef6818f96e6a13588c0e53f43ff264611" + integrity sha512-fdW6ycdnLbWah31vx1e8/n4oopwmo5H89zzC9xVsJB0snBPQnMNbuZ82ob9TXYN57JcnK5bZeVPR/RYiAh0CWQ== + +"@docusaurus/plugin-sitemap@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-alpha.50.tgz#a9ab066286e96ef7ef51cdadb0429562d95514e0" + integrity sha512-aGTeDohtjPc/nnwG4QxMem/xmbqi345Cdvkij3HZi2/kfEb0e6VxZ/YXa6Vvdb7f4ezav2N5bY7uqQojGDz8vg== + dependencies: + "@docusaurus/types" "^2.0.0-alpha.50" + sitemap "^3.2.2" + +"@docusaurus/preset-classic@2.0.0-alpha.49": + version "2.0.0-alpha.49" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-alpha.49.tgz#0968064ea4cc742b65c908efc4f4116b3314da79" + integrity sha512-gKf6B/rT4XmD1NaV/DoyrAha4T3FJHBemCt/T01uC2L7duVGJf65jkjJm/pb9y7Hz2ucQUB8yYGqrjoY++XlLg== + dependencies: + "@docusaurus/plugin-content-blog" "^2.0.0-alpha.49" + "@docusaurus/plugin-content-docs" "^2.0.0-alpha.49" + "@docusaurus/plugin-content-pages" "^2.0.0-alpha.49" + "@docusaurus/plugin-google-analytics" "^2.0.0-alpha.49" + "@docusaurus/plugin-google-gtag" "^2.0.0-alpha.49" + "@docusaurus/plugin-sitemap" "^2.0.0-alpha.49" + "@docusaurus/theme-classic" "^2.0.0-alpha.49" + "@docusaurus/theme-search-algolia" "^2.0.0-alpha.49" + remark-admonitions "^1.2.1" + +"@docusaurus/theme-classic@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.0.0-alpha.50.tgz#244062bde993a62dc31fe0d4593a7c3354b13b0b" + integrity sha512-tcMnUsdHi3jKg7DLqohftYnwDGa8Tyrtrx0zcMDXOUcYF9NCdoz3DT/39c5Fo5tDIvvGAKVkuSBJX8O/UnzDIQ== + dependencies: + "@mdx-js/mdx" "^1.5.8" + "@mdx-js/react" "^1.5.8" + classnames "^2.2.6" + clipboard "^2.0.6" + infima "0.2.0-alpha.6" + parse-numeric-range "^0.0.2" + prism-react-renderer "^1.0.2" + prismjs "^1.19.0" + react-router-dom "^5.1.2" + react-toggle "^4.1.1" + +"@docusaurus/theme-live-codeblock@2.0.0-alpha.49": + version "2.0.0-alpha.49" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-2.0.0-alpha.49.tgz#16ee6fbbb8fc2ba09dd85497568a423ea76a8f4d" + integrity sha512-QSUqrg32YBpsGYibjpsGyHk+Az+8251tx40ttPy12zxDrMX9VSnyW/G04VinXKkvFaYOM5M3rSObS7V9UyyFBg== + dependencies: + "@philpl/buble" "^0.19.7" + classnames "^2.2.6" + clipboard "^2.0.4" + parse-numeric-range "^0.0.2" + prism-react-renderer "^1.0.2" + react-live "^2.2.1" + +"@docusaurus/theme-search-algolia@^2.0.0-alpha.49": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-alpha.50.tgz#da7105a0b751fcb127aba945d178abbdd58b63cf" + integrity sha512-ixceplsidNabn0L15XYaOHzf+DCPM0+0sESW7xXK2X/K/IQaP1Mrcrs/MPovjCqylt/0UuHro1CUJimQi/x8yw== + dependencies: + classnames "^2.2.6" + docsearch.js "^2.6.3" + +"@docusaurus/types@^2.0.0-alpha.50": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-alpha.50.tgz#0ea5dde077faa2e70a694eb7562bcdf21f35bc2e" + integrity sha512-wrbBGRfTF/Obt4oaCBoVY2yKaU6jEJlqdAqSj346vBUTxKyHNVDp2EgFrjZ+nesmWlHfP6ELfIvjc/cgRwliLQ== + dependencies: + "@types/webpack" "^4.41.0" + commander "^4.0.1" + querystring "0.2.0" + +"@docusaurus/utils@2.0.0-alpha.49": + version "2.0.0-alpha.49" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-alpha.49.tgz#b79a8e84aac2e994636837ef9c4c5bf1ef48706b" + integrity sha512-b724k+Ev3KYbGXbJHmwD8op8Y5Wpn/Z3I2XJZ9Z4Y4rquixU3yKSkyAOcbULDJJuNLs17DbZfHlai6KBoF4/gw== + dependencies: + escape-string-regexp "^2.0.0" + fs-extra "^8.1.0" + gray-matter "^4.0.2" + lodash "^4.17.15" + +"@docusaurus/utils@^2.0.0-alpha.49", "@docusaurus/utils@^2.0.0-alpha.50": + version "2.0.0-alpha.50" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.0.0-alpha.50.tgz#1ceba4fafe70a7617e4d2b1e5ffa2d51d0d99927" + integrity sha512-1mQgmlF7/kB4Ud1EpUcS5C9fafI2TpjChIUEvT6Qgi5m1XOxiOTiV8x0JY/SCewR3aSmiR5nP/+hiiv57CxIFA== + dependencies: + escape-string-regexp "^2.0.0" + fs-extra "^8.1.0" + gray-matter "^4.0.2" + lodash "^4.17.15" + "@dsherret/to-absolute-glob@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1f6475dc8bd974cea07a2daf3864b317b1dd332c" @@ -164,6 +1183,39 @@ is-absolute "^1.0.0" is-negated-glob "^1.0.0" +"@emotion/is-prop-valid@^0.8.3": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/stylis@^0.8.4": + version "0.8.5" + resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" + integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== + +"@emotion/unitless@^0.7.4": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@endiliey/static-site-generator-webpack-plugin@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@endiliey/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.0.tgz#94bfe58fd83aeda355de797fcb5112adaca3a6b1" + integrity sha512-3MBqYCs30qk1OBRC697NqhGouYbs71D1B8hrk/AFJC6GwF2QaJOQZtA1JYAaGSe650sZ8r5ppRTtCRXepDWlng== + dependencies: + bluebird "^3.7.1" + cheerio "^0.22.0" + eval "^0.1.4" + url "^0.11.0" + webpack-sources "^1.4.3" + "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" @@ -238,6 +1290,38 @@ unique-filename "^1.1.1" which "^1.3.1" +"@hapi/address@2.x.x": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== + +"@hapi/bourne@1.x.x": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" + integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== + +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" + integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== + +"@hapi/joi@^15.1.0": + version "15.1.1" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" + integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ== + dependencies: + "@hapi/address" "2.x.x" + "@hapi/bourne" "1.x.x" + "@hapi/hoek" "8.x.x" + "@hapi/topo" "3.x.x" + +"@hapi/topo@3.x.x": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== + dependencies: + "@hapi/hoek" "^8.3.0" + "@iamstarkov/listr-update-renderer@0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz#d7c48092a2dcf90fd672b6c8b458649cb350c77e" @@ -252,6 +1336,21 @@ log-update "^2.3.0" strip-ansi "^3.0.1" +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" + integrity sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + "@lerna/add@3.16.2": version "3.16.2" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.16.2.tgz#90ecc1be7051cfcec75496ce122f656295bd6e94" @@ -930,6 +2029,40 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" +"@mdx-js/mdx@^1.5.8": + version "1.5.8" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.5.8.tgz#40740eaf0b0007b461cee8df13a7ae5a1af8064a" + integrity sha512-OzanPTN0p9GZOEVeEuEa8QsjxxGyfFOOnI/+V1oC1su9UIN4KUg1k4n/hWTZC+VZhdW1Lfj6+Ho8nIs6L+pbDA== + dependencies: + "@babel/core" "7.8.4" + "@babel/plugin-syntax-jsx" "7.8.3" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "^1.5.8" + babel-plugin-apply-mdx-type-prop "^1.5.8" + babel-plugin-extract-import-names "^1.5.8" + camelcase-css "2.0.1" + detab "2.0.3" + hast-util-raw "5.0.2" + lodash.uniq "4.5.0" + mdast-util-to-hast "7.0.0" + remark-mdx "^1.5.8" + remark-parse "7.0.2" + remark-squeeze-paragraphs "3.0.4" + style-to-object "0.3.0" + unified "8.4.2" + unist-builder "2.0.3" + unist-util-visit "2.0.2" + +"@mdx-js/react@^1.5.8": + version "1.5.8" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.5.8.tgz#fc38fe0eb278ae24666b2df3c751e726e33f5fac" + integrity sha512-L3rehITVxqDHOPJFGBSHKt3Mv/p3MENYlGIwLNYU89/iVqTLMD/vz8hL9RQtKqRoMbKuWpzzLlKIObqJzthNYg== + +"@mdx-js/util@^1.5.8": + version "1.5.8" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.5.8.tgz#cbadda0378af899c17ce1aa69c677015cab28448" + integrity sha512-a7Gjjw8bfBSertA/pTWBA/9WKEhgaSxvQE2NTSUzaknrzGFOhs4alZSHh3RHmSFdSWv5pUuzAgsWseMLhWEVkQ== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -964,48 +2097,88 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@octokit/endpoint@^5.5.0": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.5.1.tgz#2eea81e110ca754ff2de11c79154ccab4ae16b3f" - integrity sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg== +"@octokit/auth-token@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f" + integrity sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg== + dependencies: + "@octokit/types" "^2.0.0" + +"@octokit/endpoint@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.0.tgz#4c7acd79ab72df78732a7d63b09be53ec5a2230b" + integrity sha512-3nx+MEYoZeD0uJ+7F/gvELLvQJzLXhep2Az0bBSXagbApDvDW0LWwpnAIY/hb0Jwe17A0fJdz0O12dPh05cj7A== dependencies: "@octokit/types" "^2.0.0" is-plain-object "^3.0.0" - universal-user-agent "^4.0.0" + universal-user-agent "^5.0.0" "@octokit/plugin-enterprise-rest@^3.6.1": version "3.6.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz#74de25bef21e0182b4fa03a8678cd00a4e67e561" integrity sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA== -"@octokit/request-error@^1.0.1", "@octokit/request-error@^1.0.2": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.0.tgz#a64d2a9d7a13555570cd79722de4a4d76371baaa" - integrity sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg== +"@octokit/plugin-paginate-rest@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz#004170acf8c2be535aba26727867d692f7b488fc" + integrity sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q== + dependencies: + "@octokit/types" "^2.0.1" + +"@octokit/plugin-request-log@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" + integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw== + +"@octokit/plugin-rest-endpoint-methods@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz#3288ecf5481f68c494dd0602fc15407a59faf61e" + integrity sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ== + dependencies: + "@octokit/types" "^2.0.1" + deprecation "^2.3.1" + +"@octokit/request-error@^1.0.2": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" + integrity sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA== + dependencies: + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request-error@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.0.tgz#94ca7293373654400fbb2995f377f9473e00834b" + integrity sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw== dependencies: "@octokit/types" "^2.0.0" deprecation "^2.0.0" once "^1.4.0" "@octokit/request@^5.2.0": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.3.1.tgz#3a1ace45e6f88b1be4749c5da963b3a3b4a2f120" - integrity sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg== + version "5.3.4" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.3.4.tgz#fbc950bf785d59da3b0399fc6d042c8cf52e2905" + integrity sha512-qyj8G8BxQyXjt9Xu6NvfvOr1E0l35lsXtwm3SopsYg/JWXjlsnwqLc8rsD2OLguEL/JjLfBvrXr4az7z8Lch2A== dependencies: - "@octokit/endpoint" "^5.5.0" - "@octokit/request-error" "^1.0.1" + "@octokit/endpoint" "^6.0.0" + "@octokit/request-error" "^2.0.0" "@octokit/types" "^2.0.0" deprecation "^2.0.0" is-plain-object "^3.0.0" node-fetch "^2.3.0" once "^1.4.0" - universal-user-agent "^4.0.0" + universal-user-agent "^5.0.0" "@octokit/rest@^16.28.4": - version "16.35.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.35.0.tgz#7ccc1f802f407d5b8eb21768c6deca44e7b4c0d8" - integrity sha512-9ShFqYWo0CLoGYhA1FdtdykJuMzS/9H6vSbbQWDX4pWr4p9v+15MsH/wpd/3fIU+tSxylaNO48+PIHqOkBRx3w== - dependencies: + version "16.43.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.43.1.tgz#3b11e7d1b1ac2bbeeb23b08a17df0b20947eda6b" + integrity sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw== + dependencies: + "@octokit/auth-token" "^2.4.0" + "@octokit/plugin-paginate-rest" "^1.1.1" + "@octokit/plugin-request-log" "^1.0.0" + "@octokit/plugin-rest-endpoint-methods" "2.4.0" "@octokit/request" "^5.2.0" "@octokit/request-error" "^1.0.2" atob-lite "^2.0.0" @@ -1019,10 +2192,10 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@octokit/types@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.0.1.tgz#0caf0364e010296265621593ac9a37f40ef75dad" - integrity sha512-YDYgV6nCzdGdOm7wy43Ce8SQ3M5DMKegB8E5sTB/1xrxOdo2yS/KgUgML2N2ZGD621mkbdrAglwTyA4NDOlFFA== +"@octokit/types@^2.0.0", "@octokit/types@^2.0.1": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.8.0.tgz#4a7125e258f13317cb527ebf074cc102990d0d50" + integrity sha512-GH+0DPEQGPE9ABwQ48+nbnamegydVJCqWUNhL5lmpyiLJCFLn5jKWi2y+SRCZh8SW46WRAfWuyQNO3bMQrOdMw== dependencies: "@types/node" ">= 8" @@ -1031,6 +2204,21 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-2.4.0.tgz#7270f79ed1463370fe6e664d36a779aa4d3ee896" integrity sha512-xeKP59REgow5TPBJh3S9BRIm7DDG+Rz3Nt4ANWGUkjk4305DHpyUD5CyMJ6nd2JMmZuFyx4mjvvlCtSJLRnN6w== +"@philpl/buble@^0.19.7": + version "0.19.7" + resolved "https://registry.yarnpkg.com/@philpl/buble/-/buble-0.19.7.tgz#27231e6391393793b64bc1c982fc7b593198b893" + integrity sha512-wKTA2DxAGEW+QffRQvOhRQ0VBiYU2h2p8Yc1oBNlqSKws48/8faxqKNIuub0q4iuyTuLwtB8EkwiKwhlfV1PBA== + dependencies: + acorn "^6.1.1" + acorn-class-fields "^0.2.1" + acorn-dynamic-import "^4.0.0" + acorn-jsx "^5.0.1" + chalk "^2.4.2" + magic-string "^0.25.2" + minimist "^1.2.0" + os-homedir "^1.0.1" + regexpu-core "^4.5.4" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -1043,14 +2231,21 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" - integrity sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" + integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw== dependencies: type-detect "4.0.8" -"@sinonjs/formatio@^3.1.0", "@sinonjs/formatio@^3.2.1": +"@sinonjs/fake-timers@^6.0.0": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^3.2.1": version "3.2.2" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== @@ -1058,43 +2253,78 @@ "@sinonjs/commons" "^1" "@sinonjs/samsam" "^3.1.0" -"@sinonjs/samsam@^3.0.2", "@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.1", "@sinonjs/samsam@^3.3.3": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" - integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== +"@sinonjs/formatio@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-4.0.1.tgz#50ac1da0c3eaea117ca258b06f4f88a471668bdb" + integrity sha512-asIdlLFrla/WZybhm0C8eEzaDNNrzymiTqHMeJl6zPW2881l3uuVRpm0QlRQEjqYWv6CcKMGYME3LbrLJsORBw== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^4.2.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@^3.1.0", "@sinonjs/samsam@^3.3.3": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" + integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== dependencies: "@sinonjs/commons" "^1.3.0" array-from "^2.1.1" lodash "^4.17.15" +"@sinonjs/samsam@^4.2.0", "@sinonjs/samsam@^4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-4.2.2.tgz#0f6cb40e467865306d8a20a97543a94005204e23" + integrity sha512-z9o4LZUzSD9Hl22zV38aXNykgFeVj8acqfFabCY6FY83n/6s/XwNJyYYldz6/9lBJanpno9h+oL6HTISkviweA== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938" + integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + "@sinonjs/text-encoding@^0.7.1": version "0.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== -"@stryker-mutator/api@^2.1.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@stryker-mutator/api/-/api-2.3.0.tgz#8f4010471f8cd22ceec8efe78429d39e7af12ccc" - integrity sha512-N+XlEZAxMq4GKWt8OOn7R+124ieCiicLG3heiX2JQXNjn7uTVDfI5rSGn0oPvpwYtgeV9x6F//vzniRGhsOWIQ== +"@stryker-mutator/api@^2.1.0", "@stryker-mutator/api@^2.3.0", "@stryker-mutator/api@^2.4.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/api/-/api-2.5.0.tgz#5108d738054d17d27d89d470dcd54ae42465394c" + integrity sha512-6z2AZeGcI+ZzqZEBX6h+vWt11uBzLfAg24kV0b/CJZ69zNELRLkjJnLaE4aU1o5uE6OyFGKJJ0OVxtZrnvZSuQ== dependencies: - mutation-testing-report-schema "^1.0.0" + mutation-testing-report-schema "^1.1.0" tslib "~1.10.0" -"@stryker-mutator/core@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@stryker-mutator/core/-/core-2.1.0.tgz#38b4a1fc4319d2d6f9e955688415d6c7a3c8da98" - integrity sha512-CucANfEfYKuc3gDPJlrMw0UElr8YD3LrrC/dGN9/GyGgPFdgb4zX9XHw0Q/deGQTk51h9BieuSMLqleVxP5UqQ== +"@stryker-mutator/core@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/core/-/core-2.4.0.tgz#533a2e42a2a6c9ada820a6ceb91b08285e7ccfaa" + integrity sha512-Z/PsUC/X2BOMJsbno9zWeNqx5sYeBYExtUQW2JL0niYNbs0xkhcU7S7UC2npjwGFOCrjjWzek5TQVGQtrlDhbw== dependencies: - "@stryker-mutator/api" "^2.1.0" - "@stryker-mutator/util" "^2.1.0" - chalk "~2.4.1" - commander "~3.0.1" + "@stryker-mutator/api" "^2.4.0" + "@stryker-mutator/util" "^2.4.0" + chalk "~3.0.0" + commander "~4.0.0" get-port "~5.0.0" glob "~7.1.2" inquirer "~7.0.0" istanbul-lib-instrument "~3.3.0" - lodash "~4.17.4" - log4js "~5.1.0" + lodash.flatmap "^4.5.0" + lodash.groupby "^4.6.0" + log4js "~6.1.0" mkdirp "~0.5.1" mutation-testing-metrics "^1.1.1" progress "~2.0.0" @@ -1104,7 +2334,7 @@ surrial "~1.0.0" tree-kill "~1.2.0" tslib "~1.10.0" - typed-inject "~2.0.0" + typed-inject "~2.1.1" typed-rest-client "~1.5.0" "@stryker-mutator/html-reporter@2.1.0": @@ -1135,21 +2365,21 @@ multimatch "~4.0.0" tslib "~1.10.0" -"@stryker-mutator/typescript@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@stryker-mutator/typescript/-/typescript-2.1.0.tgz#7ef27eaa01bf58648d1cfa37f2f09e486b3df281" - integrity sha512-ReD59cKIiWoKeh7ZdSD6dngs8i4OzUsjn2x1T+mSWK3rn9smc4FZ3X6wm3abweGOhABATzPBVrdTU/j7VWMyZw== +"@stryker-mutator/typescript@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/typescript/-/typescript-2.3.0.tgz#1f13fb63f5dc68a29ca0d1815dc8dafc36dca4b1" + integrity sha512-rDF005coMvCaCWHt3pxgXf5iySPvvR7NRpY5WtqVYWEoOpbX0nhxBm4+hHbbotbdd7sUsYySa9qAkhKeDtGW/A== dependencies: - "@stryker-mutator/api" "^2.1.0" - "@stryker-mutator/util" "^2.1.0" + "@stryker-mutator/api" "^2.3.0" + "@stryker-mutator/util" "^2.3.0" lodash.flatmap "~4.5.0" semver "~6.3.0" tslib "~1.10.0" -"@stryker-mutator/util@^2.1.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@stryker-mutator/util/-/util-2.3.0.tgz#854bba4444bdb49471ae5dd33ad6aa271bb46b6b" - integrity sha512-mQLkbHy6HmN+/qb1aR02TUProbhcYASDLrthHILZriwZshCnxgfwrEOOssXlOQR/QAPXjZCnSA+fKjJwpngQQA== +"@stryker-mutator/util@^2.1.0", "@stryker-mutator/util@^2.3.0", "@stryker-mutator/util@^2.4.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/util/-/util-2.5.0.tgz#486808420b30f671d17748fd3b94473107c9adf1" + integrity sha512-l3bTvN2YbhwPPO0F6WBbUkgY6NC/34/jxpVQGlw0rZn72MPuRQBJtrfAH7KFN0OBG4QmIA6PC6c4z0X5IaWfow== "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -1168,22 +2398,34 @@ web3 "1.2.1" websocket "^1.0.28" -"@types/bluebird@3.5.27": - version "3.5.27" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.27.tgz#61eb4d75dc6bfbce51cf49ee9bbebe941b2cb5d0" - integrity sha512-6BmYWSBea18+tSjjSC3QIyV93ZKAeNWGM7R6aYt1ryTZXrlHF+QLV0G2yV0viEGVyRkyQsWfMoJ0k/YghBX5sQ== +"@types/anymatch@*": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" + integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== + +"@types/bluebird@3.5.29": + version "3.5.29" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.29.tgz#7cd933c902c4fc83046517a1bef973886d00bdb6" + integrity sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw== -"@types/bn.js@4.11.5", "@types/bn.js@^4.11.3": +"@types/bn.js@4.11.5": version "4.11.5" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.5.tgz#40e36197433f78f807524ec623afcf0169ac81dc" integrity sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng== dependencies: "@types/node" "*" +"@types/bn.js@4.11.6", "@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + "@types/body-parser@*": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897" - integrity sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w== + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== dependencies: "@types/connect" "*" "@types/node" "*" @@ -1203,19 +2445,24 @@ "@types/chai" "*" "@types/chai@*": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.5.tgz#f8da153ebbe30babb0adc9a528b9ad32be3175a2" - integrity sha512-YvbLiIc0DbbhiANrfVObdkLEHJksQZVq0Uvfg550SRAKVYaEJy+V70j65BVe2WNp6E3HtKsUczeijHFCjba3og== + version "4.2.11" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.11.tgz#d3614d6c5f500142358e6ed24e1bf16657536c50" + integrity sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw== "@types/chai@4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a" integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA== +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + "@types/connect@*": - version "3.4.32" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" - integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== + 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== dependencies: "@types/node" "*" @@ -1231,26 +2478,27 @@ dependencies: "@types/express" "*" -"@types/events@*": +"@types/events@*", "@types/events@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== "@types/express-serve-static-core@*": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.0.tgz#e80c25903df5800e926402b7e8267a675c54a281" - integrity sha512-Xnub7w57uvcBqFdIGoRg1KhNOeEj0vB6ykUM7uFWyxvbdE89GFyqgmUcanAriMr4YOxNFZBAWkfcWIb4WBPt3g== + version "4.17.4" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.4.tgz#157c79c2d28b632d6418497c57c93185e392e444" + integrity sha512-dPs6CaRWxsfHbYDVU51VjEJaUJEcli4UI0fFMT4oWmgCvHj+j7oIxz5MLHVL0Rv++N004c21ylJNdWQvPkkb5w== dependencies: "@types/node" "*" "@types/range-parser" "*" "@types/express@*": - version "4.17.2" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.2.tgz#a0fb7a23d8855bac31bc01d5a58cadd9b2173e6c" - integrity sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA== + version "4.17.5" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.5.tgz#f7457497c72ca7bf98b13bb6f1ef2898ed550bd9" + integrity sha512-u4Si7vYAjy5/UyRFa8EoqLHh6r82xOZPbWRQHlSf6alob0rlyza7EkU0RbR8kOZqgWp6R5+aRcHMYYby7w12Bg== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" + "@types/qs" "*" "@types/serve-static" "*" "@types/express@4.16.1": @@ -1262,10 +2510,10 @@ "@types/express-serve-static-core" "*" "@types/serve-static" "*" -"@types/fetch-mock@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.1.tgz#df7421e8bcb351b430bfbfa5c52bb353826ac94f" - integrity sha512-2U4vZWHNbsbK7TRmizgr/pbKe0FKopcxu+hNDtIBDiM1wvrKRItybaYj7VQ6w/hZJStU/JxRiNi5ww4YDEvKbA== +"@types/fetch-mock@7.3.2": + version "7.3.2" + resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.2.tgz#58805ba36a9357be92cc8c008dbfda937e9f7d8f" + integrity sha512-NCEfv49jmDsBAixjMjEHKVgmVQlJ+uK56FOc+2roYPExnXCZDpi6mJOHQ3v23BiO84hBDStND9R2itJr7PNoow== "@types/form-data@2.2.1": version "2.2.1" @@ -1283,6 +2531,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/html-minifier-terser@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz#7532440c138605ced1b555935c3115ddd20e8bef" + integrity sha512-q95SP4FdkmF0CwO0F2q0H6ZgudsApaY/yCtAQNRn1gduef5fGpyEphzy0YCq/N0UFvDSnLg5V8jFK/YGXlDiCw== + "@types/keyv@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.0.tgz#1961f73b3bf1084c044f79a070b45a5bfa6578b9" @@ -1300,15 +2553,20 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== -"@types/minimatch@*", "@types/minimatch@^3.0.3": +"@types/minimatch@*", "@types/minimatch@3.0.3", "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/mocha@5.2.6": - version "5.2.6" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.6.tgz#b8622d50557dd155e9f2f634b7d68fd38de5e94b" - integrity sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw== +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + +"@types/mocha@5.2.7": + version "5.2.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" + integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== "@types/node-fetch@2.1.4": version "2.1.4" @@ -1318,9 +2576,9 @@ "@types/node" "*" "@types/node@*", "@types/node@>= 8": - version "12.12.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.8.tgz#dab418655af39ce2fa99286a0bed21ef8072ac9d" - integrity sha512-XLla8N+iyfjvsa0KKV+BP/iGSoTmwxsu5Ci5sM33z9TjohF72DEz95iNvD6pPmemvbQgxAv/909G73gUn8QR7w== + version "13.11.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7" + integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g== "@types/node@10.12.21": version "10.12.21" @@ -1338,15 +2596,25 @@ integrity sha512-zkOxCS/fA+3SsdA+9Yun0iANxzhQRiNwTvJSr6N95JhuJ/x27z9G2URx1Jpt3zYFfCGUXZGL5UDxt5eyLE7wgw== "@types/node@^10.3.2": - version "10.17.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.5.tgz#c1920150f7b90708a7d0f3add12a06bc9123c055" - integrity sha512-RElZIr/7JreF1eY6oD5RF3kpmdcreuQPjg5ri4oQ5g9sq7YWU8HkfB3eH8GwAwxf5OaCh0VPi7r4N/yoTGelrA== + version "10.17.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.19.tgz#1d31ddd5503dba2af7a901aafef3392e4955620e" + integrity sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ== "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@types/qs@*": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.1.tgz#937fab3194766256ee09fcd40b781740758617e7" + integrity sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw== + "@types/range-parser@*": version "1.2.3" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" @@ -1365,10 +2633,15 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" -"@types/sinon@7.0.5": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.5.tgz#f7dea19400c193a3b36a804a7f1f4b26dacf452b" - integrity sha512-4DShbH857bZVOY4tPi1RQJNrLcf89hEtU0klZ9aYTMbtt95Ok4XdPqqcbtGOHIbAHMLSzQP8Uw/6qtBBqyloww== +"@types/sinon@7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.0.tgz#f5a10c27175465a0b001b68d8b9f761582967cc6" + integrity sha512-NyzhuSBy97B/zE58cDw4NyGvByQbAHNP9069KVSgnXt/sc0T6MFRh0InKAeBVHJWdSXG1S3+PxgVIgKo9mTHbw== + +"@types/source-list-map@*": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== "@types/strip-bom@^3.0.0": version "3.0.0" @@ -1381,9 +2654,9 @@ integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== "@types/superagent@*": - version "4.1.4" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.4.tgz#63f74955a28073870cfd9c100bcacb26d72b3764" - integrity sha512-SRH2q6/5/nhOkAuLXm3azRGjBYpoKCZWh138Rt1AxSIyE6/1b9uClIH2V+JfyDtjIvgr5yQqYgNUmdpbneJoZQ== + version "4.1.7" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.7.tgz#a7d92d98c490ee0f802a127fdf149b9a114f77a5" + integrity sha512-JSwNPgRYjIC4pIeOqLwWwfGj6iP1n5NE6kNBEbGx2V8H78xCPwx7QpNp9plaI30+W3cFEzJO7BIIsXE+dbtaGg== dependencies: "@types/cookiejar" "*" "@types/node" "*" @@ -1395,6 +2668,44 @@ dependencies: "@types/superagent" "*" +"@types/tapable@*", "@types/tapable@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" + integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== + +"@types/uglify-js@*": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.5.tgz#2c70d5c68f6e002e3b2e4f849adc5f162546f633" + integrity sha512-L7EbSkhSaWBpkl+PZAEAqZTqtTeIsq7s/oX/q0LNnxxJoRVKQE0T81XDVyaxjiiKQwiV2vhVeYRqxdRNqGOGJw== + dependencies: + source-map "^0.6.1" + +"@types/unist@^2.0.0", "@types/unist@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" + integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== + +"@types/webpack-sources@*": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.7.tgz#0a330a9456113410c74a5d64180af0cbca007141" + integrity sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.6.1" + +"@types/webpack@^4.41.0", "@types/webpack@^4.41.8": + version "4.41.10" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.10.tgz#2e1f6b3508a249854efe3dcc7690905ac5ee10be" + integrity sha512-vIy0qaq8AjOjZLuFPqpo7nAJzcoVXMdw3mvpNN07Uvdy0p1IpJeLNBe3obdRP7FX2jIusDE7z1pZa0A6qYUgnA== + dependencies: + "@types/anymatch" "*" + "@types/node" "*" + "@types/tapable" "*" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + source-map "^0.6.0" + "@types/yargs@12.0.8": version "12.0.8" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.8.tgz#0b45bf0607a5509b921335658d87489c12955176" @@ -1426,21 +2737,45 @@ "@webassemblyjs/helper-wasm-bytecode" "1.8.5" "@webassemblyjs/wast-parser" "1.8.5" +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@webassemblyjs/floating-point-hex-parser@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + "@webassemblyjs/helper-api-error@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + "@webassemblyjs/helper-buffer@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + "@webassemblyjs/helper-code-frame@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" @@ -1448,11 +2783,23 @@ dependencies: "@webassemblyjs/wast-printer" "1.8.5" +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + "@webassemblyjs/helper-fsm@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + "@webassemblyjs/helper-module-context@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" @@ -1461,11 +2808,23 @@ "@webassemblyjs/ast" "1.8.5" mamacro "^0.0.3" +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + "@webassemblyjs/helper-wasm-section@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" @@ -1476,6 +2835,16 @@ "@webassemblyjs/helper-wasm-bytecode" "1.8.5" "@webassemblyjs/wasm-gen" "1.8.5" +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/ieee754@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" @@ -1483,6 +2852,13 @@ dependencies: "@xtuc/ieee754" "^1.2.0" +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + "@webassemblyjs/leb128@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" @@ -1490,11 +2866,23 @@ dependencies: "@xtuc/long" "4.2.2" +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== + dependencies: + "@xtuc/long" "4.2.2" + "@webassemblyjs/utf8@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + "@webassemblyjs/wasm-edit@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" @@ -1509,6 +2897,20 @@ "@webassemblyjs/wasm-parser" "1.8.5" "@webassemblyjs/wast-printer" "1.8.5" +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + "@webassemblyjs/wasm-gen@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" @@ -1520,6 +2922,17 @@ "@webassemblyjs/leb128" "1.8.5" "@webassemblyjs/utf8" "1.8.5" +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + "@webassemblyjs/wasm-opt@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" @@ -1530,6 +2943,16 @@ "@webassemblyjs/wasm-gen" "1.8.5" "@webassemblyjs/wasm-parser" "1.8.5" +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wasm-parser@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" @@ -1542,6 +2965,18 @@ "@webassemblyjs/leb128" "1.8.5" "@webassemblyjs/utf8" "1.8.5" +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + "@webassemblyjs/wast-parser@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" @@ -1554,6 +2989,18 @@ "@webassemblyjs/helper-fsm" "1.8.5" "@xtuc/long" "4.2.2" +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" + "@xtuc/long" "4.2.2" + "@webassemblyjs/wast-printer@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" @@ -1563,6 +3010,15 @@ "@webassemblyjs/wast-parser" "1.8.5" "@xtuc/long" "4.2.2" +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@xtuc/long" "4.2.2" + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -1600,7 +3056,7 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= -accepts@~1.3.4, accepts@~1.3.7: +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== @@ -1608,10 +3064,20 @@ accepts@~1.3.4, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-jsx@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" - integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== +acorn-class-fields@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/acorn-class-fields/-/acorn-class-fields-0.2.1.tgz#748058bceeb0ef25164bbc671993984083f5a085" + integrity sha512-US/kqTe0H8M4LN9izoL+eykVAitE68YMuYZ3sHn3i1fjniqR7oQ3SPvuMK/VT1kjOQHrx5Q88b90TtOKgAv2hQ== + +acorn-dynamic-import@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" + integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== + +acorn-jsx@^5.0.0, acorn-jsx@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== acorn-node@^1.3.0: version "1.8.2" @@ -1627,25 +3093,30 @@ acorn-walk@^6.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== -acorn-walk@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.0.0.tgz#c8ba6f0f1aac4b0a9e32d1f0af12be769528f36b" - integrity sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg== +acorn-walk@^7.0.0, acorn-walk@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" + integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@7.1.0, acorn@^7.0.0: +acorn@7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== -acorn@^5.0.0: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== +acorn@^6.0.7, acorn@^6.1.1, acorn@^6.2.0, acorn@^6.2.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^6.0.7, acorn@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" - integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== +acorn@^7.0.0, acorn@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + +address@1.1.2, address@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== aes-js@3.0.0: version "3.0.0" @@ -1666,6 +3137,11 @@ agent-base@~4.2.1: dependencies: es6-promisify "^5.0.0" +agentkeepalive@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" + integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8= + agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -1673,12 +3149,20 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +aggregate-error@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" + integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-errors@^1.0.0, ajv-errors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv-keywords@^3.1.0: +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== @@ -1693,7 +3177,7 @@ ajv@6.8.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^5.2.2: +ajv@^5.2.2, ajv@^5.5.2: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= @@ -1703,16 +3187,42 @@ ajv@^5.2.2: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.3, ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.3, ajv@^6.5.5: + version "6.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" + integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== dependencies: - fast-deep-equal "^2.0.1" + fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" +algoliasearch@^3.24.5: + version "3.35.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.35.1.tgz#297d15f534a3507cab2f5dfb996019cac7568f0c" + integrity sha512-K4yKVhaHkXfJ/xcUnil04xiSrB8B8yHZoFEhWNpXg23eiCnqvTZw1tn/SqvdsANlYHLJlKl0qi3I/Q2Sqo7LwQ== + dependencies: + agentkeepalive "^2.2.0" + debug "^2.6.9" + envify "^4.0.0" + es6-promise "^4.1.0" + events "^1.1.0" + foreach "^2.0.5" + global "^4.3.2" + inherits "^2.0.1" + isarray "^2.0.1" + load-script "^1.0.0" + object-keys "^1.0.11" + querystring-es3 "^0.2.1" + reduce "^1.0.1" + semver "^5.1.0" + tunnel-agent "^0.6.0" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + amd-loader@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/amd-loader/-/amd-loader-0.0.8.tgz#1022928040e567e8e6a6fb58bda052f819189206" @@ -1723,6 +3233,11 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + ansi-colors@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" @@ -1730,17 +3245,22 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-escapes@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" - integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== + 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.8.1" + type-fest "^0.11.0" ansi-gray@^0.1.1: version "0.1.1" @@ -1749,6 +3269,11 @@ ansi-gray@^0.1.1: dependencies: ansi-wrap "0.1.0" +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1781,6 +3306,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" @@ -1821,28 +3354,28 @@ anymatch@~3.1.1: picomatch "^2.0.4" apache-crypt@^1.1.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/apache-crypt/-/apache-crypt-1.2.1.tgz#d6fc72aa6d27d99c95a94fd188d731eefffa663c" - integrity sha1-1vxyqm0n2ZyVqU/RiNcx7v/6Zjw= + version "1.2.4" + resolved "https://registry.yarnpkg.com/apache-crypt/-/apache-crypt-1.2.4.tgz#fc0aacb7877d64d26420cadf923bcd53e79fb34e" + integrity sha512-Icze5ny5W5uv3xgMgl8U+iGmRCC0iIDrb2PVPuRBtL3Zy1Y5TMewXP1Vtc4r5X9eNNBEk7KYPu0Qby9m/PmcHg== dependencies: - unix-crypt-td-js "^1.0.0" + unix-crypt-td-js "^1.1.4" apache-md5@^1.0.6: - version "1.1.2" - resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.2.tgz#ee49736b639b4f108b6e9e626c6da99306b41692" - integrity sha1-7klza2ObTxCLbp5ibG2pkwa0FpI= + version "1.1.5" + resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.5.tgz#5d6365ece2ccc32b612f886b2b292e1c96ff3ffb" + integrity sha512-sbLEIMQrkV7RkIruqTPXxeCMkAAycv4yzTkBzRgOR1BrR5UB7qZtupqxkersTJSf0HZ3sbaNRrNV80TnnM7cUw== app-module-path@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= -append-transform@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" - integrity sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw== +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== dependencies: - default-require-extensions "^2.0.0" + default-require-extensions "^3.0.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -1868,9 +3401,9 @@ are-we-there-yet@~1.1.2: readable-stream "^2.0.6" arg@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.1.tgz#485f8e7c390ce4c5f78257dbea80d4be11feda4c" - integrity sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw== + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" @@ -1921,11 +3454,21 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-flat-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz#1e3a4255be619dfbffbfd1d635c1cf357cd034e7" + integrity sha512-hfJmKupmQN0lwi0xG6FQ5U8Rd97RnIERplymOv/qpq8AoNKPPAnxJadjFA23FNWm88wykh9HmpLJUUwUtNU/iw== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + array-from@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" @@ -1973,7 +3516,7 @@ arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.0: +asap@^2.0.0, asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= @@ -2051,6 +3594,13 @@ async@1.x: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= +async@^2.6.2: + 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" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2061,11 +3611,41 @@ atob-lite@^2.0.0: resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY= -atob@^2.1.1: +atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +aurelius@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/aurelius/-/aurelius-0.1.3.tgz#36049644b2c12443c10b64c42a1808e2238a54ba" + integrity sha512-xFzrp6OH+ZOOUMNwM+Q4Rz/IxpBmpFMWW8u9Q0wfqHuttT9yq3VvAnkJZhJQ8uTfv2BAZetRUN24WdUgMG+FLw== + dependencies: + glob "^7.1.6" + minimist "^1.2.0" + mkdirp "^1.0.3" + ora "^4.0.3" + +autocomplete.js@0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.36.0.tgz#94fe775fe64b6cd42e622d076dc7fd26bedd837b" + integrity sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q== + dependencies: + immediate "^3.2.3" + +autoprefixer@^9.6.1: + version "9.7.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.6.tgz#63ac5bbc0ce7934e6997207d5bb00d68fa8293a4" + integrity sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ== + dependencies: + browserslist "^4.11.1" + caniuse-lite "^1.0.30001039" + chalk "^2.4.2" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.27" + postcss-value-parser "^4.0.3" + awesome-typescript-loader@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz#a41daf7847515f4925cdbaa3075d61f289e913fc" @@ -2086,9 +3666,9 @@ aws-sign2@~0.7.0: integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== axios-mock-adapter@1.16.0: version "1.16.0" @@ -2097,10 +3677,10 @@ axios-mock-adapter@1.16.0: dependencies: deep-equal "^1.0.1" -axios@0.18.1: - version "0.18.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" - integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== +axios@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== dependencies: follow-redirects "1.5.10" is-buffer "^2.0.2" @@ -2114,6 +3694,54 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.2" +babel-loader@^8.0.6: + version "8.1.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" + integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== + dependencies: + find-cache-dir "^2.1.0" + loader-utils "^1.4.0" + mkdirp "^0.5.3" + pify "^4.0.1" + schema-utils "^2.6.5" + +babel-plugin-apply-mdx-type-prop@^1.5.8: + version "1.5.8" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.5.8.tgz#f5ff6d9d7a7fcde0e5f5bd02d3d3cd10e5cca5bf" + integrity sha512-xYp5F9mAnZdDRFSd1vF3XQ0GQUbIulCpnuht2jCmK30GAHL8szVL7TgzwhEGamQ6yJmP/gEyYNM9OR5D2n26eA== + dependencies: + "@babel/helper-plugin-utils" "7.8.3" + "@mdx-js/util" "^1.5.8" + +babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-extract-import-names@^1.5.8: + version "1.5.8" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.5.8.tgz#418057261346451d689dff5036168567036b8cf6" + integrity sha512-LcLfP8ZRBZMdMAXHLugyvvd5PY0gMmLMWFogWAUsG32X6TYW2Eavx+il2bw73KDbW+UdCC1bAJ3NuU25T1MI3g== + dependencies: + "@babel/helper-plugin-utils" "7.8.3" + +"babel-plugin-styled-components@>= 1": + version "1.10.7" + resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz#3494e77914e9989b33cc2d7b3b29527a949d635c" + integrity sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-module-imports" "^7.0.0" + babel-plugin-syntax-jsx "^6.18.0" + lodash "^4.17.11" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + babel-polyfill@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" @@ -2131,15 +3759,27 @@ babel-runtime@6.26.0, babel-runtime@^6.11.6, babel-runtime@^6.26.0: core-js "^2.4.0" regenerator-runtime "^0.11.0" +backbone@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.0.tgz#54db4de9df7c3811c3f032f34749a4cd27f3bd12" + integrity sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ== + dependencies: + underscore ">=1.8.3" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base-x@^3.0.4: - version "3.0.7" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.7.tgz#1c5a7fafe8f66b4114063e8da102799d4e7c408f" - integrity sha512-zAKJGuQPihXW22fkrfOclUUZXM2g92z5GzlSMHxhO6r6Qj+Nm0ccaGNBzDZojzwOMkpjAv4J0fOv1U4go+a4iw== + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== dependencies: safe-buffer "^5.0.1" @@ -2166,7 +3806,7 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@~2.0.0: +basic-auth@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== @@ -2203,6 +3843,19 @@ benchmark@2.1.4: lodash "^4.17.4" platform "^1.3.3" +better-ajv-errors@^0.6.1, better-ajv-errors@^0.6.7: + version "0.6.7" + resolved "https://registry.yarnpkg.com/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz#b5344af1ce10f434fe02fc4390a5a9c811e470d1" + integrity sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/runtime" "^7.0.0" + chalk "^2.4.1" + core-js "^3.2.1" + json-to-ast "^2.0.3" + jsonpointer "^4.0.1" + leven "^3.1.0" + bfj@^6.1.1: version "6.1.2" resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" @@ -2265,10 +3918,10 @@ bluebird@3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== -bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: - version "3.7.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" - integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== +bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5, bluebird@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bn.js@4.11.6: version "4.11.6" @@ -2280,6 +3933,11 @@ bn.js@4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.0, bn.js@^4. resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bn.js@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.1.tgz#48efc4031a9c4041b9c99c6941d903463ab62eb5" + integrity sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA== + body-parser@1.19.0, body-parser@^1.16.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -2296,7 +3954,19 @@ body-parser@1.19.0, body-parser@^1.16.0: raw-body "2.4.0" type-is "~1.6.17" -boolbase@~1.0.0: +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.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= @@ -2341,7 +4011,7 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brfs@^1.3.0, brfs@^1.4.0: +brfs@^1.4.0: version "1.6.1" resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3" integrity sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ== @@ -2466,11 +4136,43 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.10.0.tgz#f179737913eaf0d2b98e4926ac1ca6a15cbcc6a9" + integrity sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA== + dependencies: + caniuse-lite "^1.0.30001035" + electron-to-chromium "^1.3.378" + node-releases "^1.1.52" + pkg-up "^3.1.0" + +browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.9.1: + version "4.11.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.1.tgz#92f855ee88d6e050e7e7311d987992014f1a1f1b" + integrity sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g== + dependencies: + caniuse-lite "^1.0.30001038" + electron-to-chromium "^1.3.390" + node-releases "^1.1.53" + pkg-up "^2.0.0" + btoa-lite@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= +buble@0.19.6: + version "0.19.6" + resolved "https://registry.yarnpkg.com/buble/-/buble-0.19.6.tgz#915909b6bd5b11ee03b1c885ec914a8b974d34d3" + integrity sha512-9kViM6nJA1Q548Jrd06x0geh+BG2ru2+RMDkIHHgJY/8AcyCs34lTHwra9BX7YdPrZXd5aarkpr/SY8bmPgPdg== + dependencies: + chalk "^2.4.1" + magic-string "^0.25.1" + minimist "^1.2.0" + os-homedir "^1.0.1" + regexpu-core "^4.2.0" + vlq "^1.0.0" + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -2504,6 +4206,16 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-json@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-json/-/buffer-json-2.0.0.tgz#f73e13b1e42f196fe2fd67d001c7d7107edd7c23" + integrity sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw== + buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" @@ -2524,9 +4236,9 @@ buffer@^4.3.0: isarray "^1.0.0" buffer@^5.0.5, buffer@^5.2.1: - version "5.4.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" - integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== + version "5.5.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce" + integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -2556,6 +4268,11 @@ byte-size@^5.0.1: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191" integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw== +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -2582,9 +4299,9 @@ cacache@^11.3.2: y18n "^4.0.0" cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: - version "12.0.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" - integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== dependencies: bluebird "^3.5.5" chownr "^1.1.1" @@ -2602,6 +4319,30 @@ cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^13.0.1: + version "13.0.1" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" + integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== + dependencies: + chownr "^1.1.2" + figgy-pudding "^3.5.1" + fs-minipass "^2.0.0" + glob "^7.1.4" + graceful-fs "^4.2.2" + infer-owner "^1.0.4" + lru-cache "^5.1.1" + minipass "^3.0.0" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + p-map "^3.0.0" + promise-inflight "^1.0.1" + rimraf "^2.7.1" + ssri "^7.0.0" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2617,6 +4358,18 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cache-loader@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-4.1.0.tgz#9948cae353aec0a1fcb1eafda2300816ec85387e" + integrity sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw== + dependencies: + buffer-json "^2.0.0" + find-cache-dir "^3.0.0" + loader-utils "^1.2.3" + mkdirp "^0.5.1" + neo-async "^2.6.1" + schema-utils "^2.0.0" + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -2630,15 +4383,15 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -caching-transform@^3.0.1, caching-transform@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-3.0.2.tgz#601d46b91eca87687a281e71cef99791b0efca70" - integrity sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w== +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== dependencies: - hasha "^3.0.0" - make-dir "^2.0.0" - package-hash "^3.0.0" - write-file-atomic "^2.4.2" + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" call-me-maybe@^1.0.1: version "1.0.1" @@ -2669,6 +4422,19 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camel-case@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" + integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + dependencies: + pascal-case "^3.1.1" + tslib "^1.10.0" + +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -2686,6 +4452,15 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" +camelcase-keys@^6.1.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -2696,16 +4471,41 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^5.0.0: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelize@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" + integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001038, caniuse-lite@^1.0.30001039: + version "1.0.30001039" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001039.tgz#b3814a1c38ffeb23567f8323500c09526a577bbe" + integrity sha512-SezbWCTT34eyFoWHgx8UWso7YtvtM7oosmFoXbCkdC6qJzRfBTeTgE9REtKtiuKXuMwWTZEvdnFNGAyVMorv8Q== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +ccount@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17" + integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw== + chai-as-promised@7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" @@ -2740,7 +4540,7 @@ chai@4.2.0, chai@^4.2.0: pathval "^1.1.0" type-detect "^4.0.5" -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2, chalk@~2.4.1: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2760,6 +4560,29 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^3.0.0, chalk@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -2775,6 +4598,28 @@ check-types@^8.0.3: resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== +cheerio@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + cheerio@^1.0.0-rc.3: version "1.0.0-rc.3" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" @@ -2803,7 +4648,7 @@ chokidar@^1.6.0: optionalDependencies: fsevents "^1.0.0" -chokidar@^2.0.2, chokidar@^2.0.4: +chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -2822,10 +4667,10 @@ chokidar@^2.0.2, chokidar@^2.0.4: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.0.2: - version "3.3.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" - integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== +chokidar@^3.0.2, chokidar@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" + integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -2833,22 +4678,27 @@ chokidar@^3.0.2: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.2.0" + readdirp "~3.3.0" optionalDependencies: - fsevents "~2.1.1" + fsevents "~2.1.2" chownr@^1.1.1, chownr@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" - integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chrome-trace-event@^1.0.0: +chrome-trace-event@^1.0.0, chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== dependencies: tslib "^1.9.0" +ci-info@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2877,6 +4727,23 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@2.2.6, classnames@^2.2.0, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +clean-css@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" + integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2891,6 +4758,11 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-spinners@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5" + integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w== + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -2904,6 +4776,15 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +clipboard@^2.0.0, clipboard@^2.0.4, clipboard@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" + integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -2922,6 +4803,15 @@ 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== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -2939,16 +4829,35 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + code-block-writer@^7.2.0: version "7.3.1" resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-7.3.1.tgz#b3b921b885fe7fb869866c0648491eae6c08cfa9" integrity sha512-3Jfe6ZmmGzvdQWFo3MUzobn3WdX++jc3Tj0rsviJWYPnP7NGMFEE4qheNeOXeJgB1TTgxYT8XuNvhS/u596yGg== +code-error-fragment@0.0.230: + version "0.0.230" + resolved "https://registry.yarnpkg.com/code-error-fragment/-/code-error-fragment-0.0.230.tgz#d736d75c832445342eca1d1fedbf17d9618b14d7" + integrity sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw== + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +collapse-white-space@^1.0.0, collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -2957,23 +4866,51 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +color@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + colors@^1.1.2, colors@^1.3.3, colors@latest: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -2994,6 +4931,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + commander@2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -3009,6 +4951,11 @@ commander@^2.12.1, commander@^2.14.1, commander@^2.18.0, commander@^2.20.0, comm resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^4.0.1, commander@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commander@~2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" @@ -3016,10 +4963,10 @@ commander@~2.8.1: dependencies: graceful-readlink ">= 1.0.0" -commander@~3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" - integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== +commander@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c" + integrity sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA== commondir@^1.0.1: version "1.0.1" @@ -3039,6 +4986,36 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +component-props@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/component-props/-/component-props-1.1.1.tgz#f9b7df9b9927b6e6d97c9bd272aa867670f34944" + integrity sha1-+bffm5kntubZfJvScqqGdnDzSUQ= + +component-xor@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/component-xor/-/component-xor-0.0.4.tgz#c55d83ccc1b94cd5089a4e93fa7891c7263e59aa" + integrity sha1-xV2DzMG5TNUImk6T+niRxyY+Wao= + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3072,6 +5049,11 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + connect@^3.6.6: version "3.7.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" @@ -3082,6 +5064,11 @@ connect@^3.6.6: parseurl "~1.3.3" utils-merge "1.0.1" +consola@^2.10.0: + version "2.11.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.11.3.tgz#f7315836224c143ac5094b47fd4c816c2cd1560e" + integrity sha512-aoW0YIIAmeftGR8GSpw6CGQluNdkWMWh3yEFjH/hmynTYnMtibXszii3lxCXmk8YxJtI3FAK5aTiquA5VH68Gw== + console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" @@ -3192,7 +5179,7 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.5.1, convert-source-map@^1.6.0: +convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -3231,10 +5218,46 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js@^2.4.0, core-js@^2.5.0: - version "2.6.10" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f" - integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA== +copy-webpack-plugin@^5.0.5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88" + integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg== + dependencies: + cacache "^12.0.3" + find-cache-dir "^2.1.0" + glob-parent "^3.1.0" + globby "^7.1.1" + is-glob "^4.0.1" + loader-utils "^1.2.3" + minimatch "^3.0.4" + normalize-path "^3.0.0" + p-limit "^2.2.1" + schema-utils "^1.0.0" + serialize-javascript "^2.1.2" + webpack-log "^2.0.0" + +core-js-compat@^3.6.2: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" + integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== + dependencies: + browserslist "^4.8.3" + semver "7.0.0" + +core-js@3.6.4, core-js@^3.2.1: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" + integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= + +core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.6.5: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3249,7 +5272,7 @@ cors@2.8.5, cors@^2.8.1, cors@latest: object-assign "^4" vary "^1" -cosmiconfig@^5.0.2, cosmiconfig@^5.1.0, cosmiconfig@^5.2.0, cosmiconfig@^5.2.1: +cosmiconfig@^5.0.0, cosmiconfig@^5.0.2, cosmiconfig@^5.1.0, cosmiconfig@^5.2.0, cosmiconfig@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== @@ -3271,17 +5294,6 @@ coveralls@3.0.3: minimist "^1.2.0" request "^2.86.0" -cp-file@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-6.2.0.tgz#40d5ea4a1def2a9acdd07ba5c0b0246ef73dc10d" - integrity sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA== - dependencies: - graceful-fs "^4.1.2" - make-dir "^2.0.0" - nested-error-stacks "^2.0.0" - pify "^4.0.1" - safe-buffer "^5.0.1" - create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -3313,6 +5325,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-context@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca" + integrity sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A== + dependencies: + fbjs "^0.8.0" + gud "^1.0.0" + cross-env@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" @@ -3331,13 +5351,14 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^4: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= +cross-spawn@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== dependencies: - lru-cache "^4.0.1" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" cross-spawn@^5.0.1: version "5.1.0" @@ -3348,6 +5369,15 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@3.12.0, crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -3366,11 +5396,75 @@ crypto-browserify@3.12.0, crypto-browserify@^3.11.0: randomfill "^1.0.3" crypto-js@^3.1.9-1: - version "3.1.9-1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" - integrity sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg= + 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-blank-pseudo@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" + integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== + dependencies: + postcss "^7.0.5" + +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= -css-select@~1.2.0: +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-has-pseudo@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" + integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^5.0.0-rc.4" + +css-loader@^3.2.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.1.tgz#db2b2336f4169edb68e6a829ad4fd36552647b77" + integrity sha512-0G4CbcZzQ9D1Q6ndOfjFuMDo8uLYMu5vc9Abs5ztyHcKvmil6GJrMiNjzzi3tQvUF+mVRuDg7bE6Oc0Prolgig== + dependencies: + camelcase "^5.3.1" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.27" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.0.3" + schema-utils "^2.6.5" + semver "^6.3.0" + +css-prefers-color-scheme@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" + integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== + dependencies: + postcss "^7.0.5" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +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= @@ -3380,11 +5474,141 @@ css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" +css-select@^2.0.0: + 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 "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-to-react-native@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" + integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== + dependencies: + camelize "^1.0.0" + css-color-keywords "^1.0.0" + postcss-value-parser "^4.0.2" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@1.0.0-alpha.39: + version "1.0.0-alpha.39" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + 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-what@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" + integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== + +cssdb@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" + integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903" + integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== + dependencies: + css-tree "1.0.0-alpha.39" + currency-codes@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/currency-codes/-/currency-codes-1.5.1.tgz#7ed12dc00be91414937c71855ff5ea6059b88d47" @@ -3420,6 +5644,11 @@ dargs@^4.0.1: dependencies: number-is-nan "^1.0.0" +dash-ast@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" + integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -3437,6 +5666,11 @@ date-format@^2.1.0: resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== +date-format@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" + integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== + dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -3455,7 +5689,7 @@ debounce@^1.0.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -3469,7 +5703,7 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" -debug@^3.1.0, debug@^3.2.6: +debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3488,7 +5722,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0: +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -3501,6 +5735,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decko@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decko/-/decko-1.2.0.tgz#fd43c735e967b8013306884a56fbe665996b6817" + integrity sha1-/UPHNelnuAEzBohKVvvmZZlraBc= + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -3553,9 +5792,9 @@ decompress-unzip@^4.0.1: yauzl "^2.4.2" decompress@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" - integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== dependencies: decompress-tar "^4.0.0" decompress-tarbz2 "^4.0.0" @@ -3590,22 +5829,25 @@ deep-equal@^1.0.0, deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -default-require-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" - integrity sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc= +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== dependencies: - strip-bom "^3.0.0" + execa "^1.0.0" + ip-regex "^2.1.0" + +default-require-extensions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" + integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== + dependencies: + strip-bom "^4.0.0" defaults@^1.0.3: version "1.0.3" @@ -3615,9 +5857,9 @@ defaults@^1.0.3: clone "^1.0.2" defer-to-connect@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.0.tgz#b41bd7efa8508cef13f8456975f7a278c72833fd" - integrity sha512-WE2sZoctWm/v4smfCAdjYbrfS55JiMRdlY9ZubFhsYbteCK9+BvAx4YV7nPjYM6ZnX5BcoVKwfmyx9sIFTgQMQ== + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" @@ -3660,11 +5902,43 @@ del@^3.0.0: pify "^3.0.0" rimraf "^2.2.8" +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +del@^5.0.0, del@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" + integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== + dependencies: + globby "^10.0.1" + graceful-fs "^4.2.2" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.1" + p-map "^3.0.0" + rimraf "^3.0.0" + slash "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -3675,7 +5949,12 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecation@^2.0.0: +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== @@ -3693,6 +5972,13 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detab@2.0.3, detab@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.3.tgz#33e5dd74d230501bd69985a0d2b9a3382699a130" + integrity sha512-Up8P0clUVwq0FnFjDclzZsy9PadzRn5FFxrr47tQQvMHqyiFYVbpH8oXDzWtF0Q7pYy3l+RPmtBl+BsFF6wH0A== + dependencies: + repeat-string "^1.5.4" + detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -3703,10 +5989,18 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" dezalgo@^1.0.0: version "1.0.3" @@ -3731,10 +6025,10 @@ diff@3.5.0, diff@^3.2.0, diff@^3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" - integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== +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== diffie-hellman@^5.0.0: version "5.0.3" @@ -3753,7 +6047,7 @@ dir-glob@2.0.0: arrify "^1.0.1" path-type "^3.0.0" -dir-glob@^2.2.2: +dir-glob@^2.0.0, dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== @@ -3772,6 +6066,39 @@ dirty-chai@2.0.1: resolved "https://registry.yarnpkg.com/dirty-chai/-/dirty-chai-2.0.1.tgz#6b2162ef17f7943589da840abc96e75bda01aff3" integrity sha512-ys79pWKvDMowIDEPC6Fig8d5THiC0DJ2gmTeGzVAoEH18J8OzLud0Jh7I9IWg3NSk8x2UocznUuFmfHCXYZx9w== +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +docsearch.js@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/docsearch.js/-/docsearch.js-2.6.3.tgz#57cb4600d3b6553c677e7cbbe6a734593e38625d" + integrity sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A== + dependencies: + algoliasearch "^3.24.5" + autocomplete.js "0.36.0" + hogan.js "^3.0.2" + request "^2.87.0" + stack-utils "^1.0.1" + to-factory "^1.0.0" + zepto "^1.2.0" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -3779,6 +6106,21 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +dom-converter@^0.2: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dom-iterator/-/dom-iterator-1.0.0.tgz#9c09899846ec41c2d257adc4d6015e4759ef05ad" + integrity sha512-7dsMOQI07EMU98gQM8NSB3GsAiIeBYIPKpnxR3c9xOvdvBjChAcOM0iJ222I3p5xyiZO9e5oggkNaCusuTdYig== + dependencies: + component-props "1.1.1" + component-xor "0.0.4" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -3787,7 +6129,7 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@~0.1.1: +dom-serializer@~0.1.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== @@ -3796,9 +6138,9 @@ dom-serializer@~0.1.1: entities "^1.1.1" dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" - integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== domain-browser@^1.1.1: version "1.2.0" @@ -3822,6 +6164,11 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +dompurify@^2.0.7: + version "2.0.8" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.8.tgz#6ef89d2d227d041af139c7b01d9f67ed59c2eb3c" + integrity sha512-vIOSyOXkMx81ghEalh4MLBtDHMx1bhKlaqHDMqM2yeitJ996SLOk5mGdDpI9ifJAgokred8Rmu219fX4OltqXw== + domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -3830,7 +6177,7 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" -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== @@ -3838,6 +6185,14 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" +dot-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" + integrity sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + dot-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" @@ -3852,10 +6207,17 @@ dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + dependencies: + is-obj "^2.0.0" + dot@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/dot/-/dot-1.1.2.tgz#c7377019fc4e550798928b2b9afeb66abfa1f2f9" - integrity sha1-xzdwGfxOVQeYkosrmv62ar+h8vk= + version "1.1.3" + resolved "https://registry.yarnpkg.com/dot/-/dot-1.1.3.tgz#351360e00a748bce9a1f8f27c00c394a7e4e1e9f" + integrity sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg== dotenv@8.0.0: version "8.0.0" @@ -3923,10 +6285,10 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -eccrypto@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/eccrypto/-/eccrypto-1.1.2.tgz#7962402f923044d25a802d7369447b0a563873b1" - integrity sha512-4z/uF18h2TFdqqtFSUvlwRD9epzmeEEUZ4nVMv3ox+jy+V7AxU9s3nLoEDDbptTUlkAbAp5bPfhaS7H4naoFqg== +eccrypto@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/eccrypto/-/eccrypto-1.1.3.tgz#010cb4e9d239ce9752b82c0ac6d8af207fffaf3e" + integrity sha512-Xtyj039Xp2NDZwoe9IcD7pT1EwM4DILdxPCN2H7Rk1wgJNtTkFpk+cpX1QpuHTMaIhkatOBlGGKzGw/DUCDdqg== dependencies: acorn "7.1.0" elliptic "6.5.1" @@ -3941,9 +6303,19 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= ejs@^2.6.1: - version "2.7.2" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.2.tgz#749037c4c09bd57626a6140afbe6b7e650661614" - integrity sha512-rHGwtpl67oih3xAHbZlpw5rQAt+YV1mSCu2fUZ9XNrfaGEhom7E+AUiMci+ByP4aSfuAWx7hE0BPuJLMrpXwOw== + version "2.7.4" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + +ejs@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.0.2.tgz#745b01cdcfe38c1c6a2da3bbb2d9957060a31226" + integrity sha512-IncmUpn1yN84hy2shb0POJ80FWrfGNY0cxO9f4v+/sG7qcBvAtVWUA1IdzY/8EYUmOVhoKJVdJjNd3AZcnxOjA== + +electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.390: + version "1.3.399" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.399.tgz#f77945634de0df9e8a10b79345dc167cdabe8b80" + integrity sha512-+NBhK0/v17pls7CSh3Cx5Ir3tsGmtLPMMAO4Nz272bre2wzdykLEsev5wjOd3rYMt2/kSS681ufFT7Dywxq1sw== elegant-spinner@^1.0.1: version "1.0.1" @@ -3960,7 +6332,7 @@ elliptic@6.3.3: hash.js "^1.0.0" inherits "^2.0.1" -elliptic@6.5.1, elliptic@^6.0.0, elliptic@^6.4.0, elliptic@^6.4.1: +elliptic@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b" integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg== @@ -3973,6 +6345,24 @@ elliptic@6.5.1, elliptic@^6.0.0, elliptic@^6.4.0, elliptic@^6.4.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +elliptic@6.5.2, elliptic@^6.0.0, elliptic@^6.4.0, elliptic@^6.4.1, elliptic@^6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + 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" + +"emoji-regex@>=6.0.0 <=6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" + integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -3988,6 +6378,16 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +emoticon@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" + integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4035,10 +6435,18 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== -env-paths@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" - integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= +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== + +envify@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" + integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== + dependencies: + esprima "^4.0.0" + through "~2.3.4" eol@^0.9.1: version "0.9.1" @@ -4050,6 +6458,11 @@ err-code@^1.0.0: resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= +err-code@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.0.tgz#452dadddde12356b1dd5a85f33b28ddda377ef2a" + integrity sha512-MsMOijQ4v0xlmrz1fc7lyPEy7jFhoNF7EVaRSP7mPzs20LaFOwG6qNjGRy3Ie85n9DARlcUnB1zbsBv5sJrIvw== + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -4064,23 +6477,24 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.4.3, es-abstract@^1.5.1: - version "1.16.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" - integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg== +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== dependencies: - es-to-primitive "^1.2.0" + es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" object-keys "^1.1.1" - string.prototype.trimleft "^2.1.0" - string.prototype.trimright "^2.1.0" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" -es-to-primitive@^1.2.0: +es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== @@ -4090,12 +6504,12 @@ es-to-primitive@^1.2.0: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14: - version "0.10.52" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f" - integrity sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag== + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== dependencies: es6-iterator "~2.0.3" - es6-symbol "~3.1.2" + es6-symbol "~3.1.3" next-tick "~1.0.0" es6-error@^4.0.1: @@ -4129,11 +6543,16 @@ es6-object-assign@^1.0.3: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= -es6-promise@4.2.8, es6-promise@^4.0.3: +es6-promise@4.2.8, es6-promise@^4.0.3, es6-promise@^4.1.0: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== +es6-promise@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= + es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" @@ -4160,7 +6579,7 @@ es6-symbol@3.1.1: d "1" es5-ext "~0.10.14" -es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.2: +es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== @@ -4168,7 +6587,7 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.2: d "^1.0.1" ext "^1.1.2" -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -4178,6 +6597,11 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" @@ -4190,12 +6614,12 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -escodegen@^1.8.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" - integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== +escodegen@^1.11.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" + integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== dependencies: - esprima "^3.1.3" + esprima "^4.0.1" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" @@ -4225,10 +6649,10 @@ escodegen@~1.9.0: optionalDependencies: source-map "~0.6.1" -eslint-plugin-spellcheck@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/eslint-plugin-spellcheck/-/eslint-plugin-spellcheck-0.0.11.tgz#f3fcff70a0446e1f04eacd5ebaa8cab23e691b4a" - integrity sha512-P8NCB9Y9u3PLSdW5PeVY6kcBR/WkP/FB3toik5ahaGc13s+Di+Op+ItinB89Z+61+IrXQX//qeGh1mgCGrp7LA== +eslint-plugin-spellcheck@0.0.14: + version "0.0.14" + resolved "https://registry.yarnpkg.com/eslint-plugin-spellcheck/-/eslint-plugin-spellcheck-0.0.14.tgz#8910ae9daa05993a99802b7467ae68aa07edd1d6" + integrity sha512-bjhLw5H67/kTv3g8NYrqLk0MMIFchLqzgBaGOKi3rQQflGaXoP8/rtmHDWOXX5AJCwIj7+OVqe5JM0GB+5vWFg== dependencies: globals "^11.3.0" hunspell-spellchecker "^1.0.2" @@ -4241,7 +6665,7 @@ eslint-plugin-typescript@0.14.0: dependencies: requireindex "~1.1.0" -eslint-scope@^4.0.0: +eslint-scope@^4.0.0, eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== @@ -4322,7 +6746,7 @@ esprima@^3.1.3: resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -4333,11 +6757,11 @@ esprima@~1.0.4: integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" + integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q== dependencies: - estraverse "^4.0.0" + estraverse "^5.0.0" esrecurse@^4.1.0: version "4.2.1" @@ -4351,11 +6775,16 @@ estraverse@^1.9.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" + integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== + estraverse@~1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" @@ -4381,24 +6810,29 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eth-contract-metadata@1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/eth-contract-metadata/-/eth-contract-metadata-1.11.0.tgz#4d23a8208d5d53be9d4c0696ed8492b505c6bca1" - integrity sha512-Bbvio71M+lH+qXd8XXddpTc8hhjL9m4fNPOxmZFIX8z0/VooUdwV8YmmDAbkU5WVioZi+Jp1XaoO7VwzXnDboA== +eth-contract-metadata@1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/eth-contract-metadata/-/eth-contract-metadata-1.12.1.tgz#41014c8c0123453cee15acbcc14299c4d470c759" + integrity sha512-9u2jUcdxaKIv4RvA9RtjyD4+M2yWt4yCulR5bpdQTiG3HUFnN9lHtNL5NIRDpvQVJKerFhexrgEM2WdGP3a6VA== -eth-crypto@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/eth-crypto/-/eth-crypto-1.5.0.tgz#f3ec292c87c10980cff358b6f15d8a330214622f" - integrity sha512-BSTlT/dnyB8it3MBDf1X/RCwVjbah44KYgPZNxIBpXRr7jEIXe1eF8h6ShXT+a3mpCb7SWd9aczhSB4KioCSZw== +eth-contract-metadata@1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/eth-contract-metadata/-/eth-contract-metadata-1.13.0.tgz#9819d0e556ea2187da91d6b49ce96abc5fce2a73" + integrity sha512-9CjXHX8IdXysUEvOHdbCsjdAwM1E98jaeK2HeOqm/9S/vOZ8YryaBBt/YSiBq3MkpCwf+d1pEQ53p96rsdy52w== + +eth-crypto@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/eth-crypto/-/eth-crypto-1.5.2.tgz#bf9f7aece69d68752097498310c921621fbe5726" + integrity sha512-xhu7rt3CdNKSQ5VcqiFwfp50YVtc1+VPiqYEfMa8Omx9rGn5QHdIjImSoEnlej3VbfHv25Kvpu6A5oPoSI6iUA== dependencies: - "@types/bn.js" "4.11.5" + "@types/bn.js" "4.11.6" babel-runtime "6.26.0" - eccrypto "1.1.2" + eccrypto "1.1.3" eth-lib "0.2.8" - ethereumjs-tx "2.1.1" - ethereumjs-util "6.1.0" - ethers "4.0.37" - secp256k1 "3.7.1" + ethereumjs-tx "2.1.2" + ethereumjs-util "6.2.0" + ethers "4.0.44" + secp256k1 "3.8.0" eth-ens-namehash@2.0.8: version "2.0.8" @@ -4408,7 +6842,7 @@ eth-ens-namehash@2.0.8: idna-uts46-hx "^2.3.1" js-sha3 "^0.5.7" -eth-lib@0.1.27, eth-lib@^0.1.26: +eth-lib@0.1.27: version "0.1.27" resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" integrity sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA== @@ -4439,17 +6873,29 @@ eth-lib@0.2.8: elliptic "^6.4.0" xhr-request-promise "^0.1.2" -ethereumjs-common@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.4.0.tgz#a940685f88f3c2587e4061630fe720b089c965b8" - integrity sha512-ser2SAplX/YI5W2AnzU8wmSjKRy4KQd4uxInJ36BzjS3m18E/B9QedPUIresZN1CSEQb/RgNQ2gN7C/XbpTafA== +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" -ethereumjs-tx@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.1.tgz#7d204e2b319156c9bc6cec67e9529424a26e8ccc" - integrity sha512-QtVriNqowCFA19X9BCRPMgdVNJ0/gMBS91TQb1DfrhsbR748g4STwxZptFAwfqehMyrF8rDwB23w87PQwru0wA== +ethereumjs-common@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz#d3e82fc7c47c0cef95047f431a99485abc9bb1cd" + integrity sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ== + +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== dependencies: - ethereumjs-common "^1.3.1" + ethereumjs-common "^1.5.0" ethereumjs-util "^6.0.0" ethereumjs-util@6.1.0: @@ -4465,7 +6911,7 @@ ethereumjs-util@6.1.0: safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-util@^6.0.0: +ethereumjs-util@6.2.0, ethereumjs-util@^6.0.0: version "6.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.0.tgz#23ec79b2488a7d041242f01e25f24e5ad0357960" integrity sha512-vb0XN9J2QGdZGIEKG2vXM+kUdEivUfU6Wmi5y0cg+LRhDYKnXIZ/Lz7XjFbHRR9VIKq2lVGLzGBkA++y2nOdOQ== @@ -4510,15 +6956,14 @@ ethers@4.0.0-beta.3: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@4.0.37: - version "4.0.37" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.37.tgz#dfa70d59498663878c5e4a977d14356660ca5b90" - integrity sha512-B7bDdyQ45A5lPr6k2HOkEKMtYOuqlfy+nNf8glnRvWidkDQnToKw1bv7UyrwlbsIgY2mE03UxTVtouXcT6Vvcw== +ethers@4.0.44: + version "4.0.44" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.44.tgz#f2608cbc0b4d099b7e10a01c0efc3a1037013b4e" + integrity sha512-kCkMPkpYjBkxzqjcuYUfDY7VHDbf5EXnfRPUOazdqdf59SvXaT+w5lgauxLlk1UjxnAiNfeNS87rkIXnsTaM7Q== dependencies: - "@types/node" "^10.3.2" aes-js "3.0.0" bn.js "^4.4.0" - elliptic "6.3.3" + elliptic "6.5.2" hash.js "1.1.3" js-sha3 "0.5.7" scrypt-js "2.0.4" @@ -4526,15 +6971,14 @@ ethers@4.0.37: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@4.0.38: - version "4.0.38" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.38.tgz#3fabafb4f79c435205b143b66b4a1af035043e37" - integrity sha512-l7l7RIfk2/rIFgRRVLFY3H06S9dhXXPUdMlYm6SCelB6oG+ABmoRig7xSVOLcHLayBfSwssjAAYLKxf1jWhbuQ== +ethers@4.0.45: + version "4.0.45" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.45.tgz#8d4cd764d7c7690836b583d4849203c225eb56e2" + integrity sha512-N/Wmc6Mw4pQO+Sss1HnKDCSS6KSCx0luoBMiPNq+1GbOaO3YaZOyplBEhj+NEoYsizZYODtkITg2oecPeNnidQ== dependencies: - "@types/node" "^10.3.2" aes-js "3.0.0" bn.js "^4.4.0" - elliptic "6.3.3" + elliptic "6.5.2" hash.js "1.1.3" js-sha3 "0.5.7" scrypt-js "2.0.4" @@ -4567,6 +7011,13 @@ ethjs-util@0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" +eval@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.4.tgz#e05dbe0dab4b9330215cbb7bf4886eb24bd58700" + integrity sha512-npGsebJejyjMRnLdFu+T/97dnigqIU0Ov3IGrZ8ygd1v7RL1vGkEKtvyWZobqUH1AQgKlg0Yqqe2BtMA9/QZLw== + dependencies: + require-like ">= 0.1.1" + event-emitter@~0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -4598,10 +7049,27 @@ eventemitter3@3.1.2, eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -events@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" - integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== +eventemitter3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" + integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== + +events@3.1.0, events@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== + +events@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" @@ -4637,6 +7105,37 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-2.1.0.tgz#e5d3ecd837d2a60ec50f3da78fd39767747bbe99" + integrity sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^3.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -4671,7 +7170,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -express@4.17.1, express@^4.14.0, express@^4.16.3: +express@4.17.1, express@^4.14.0, express@^4.16.3, express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -4708,9 +7207,9 @@ express@4.17.1, express@^4.14.0, express@^4.16.3: vary "~1.1.2" ext@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.2.0.tgz#8dd8d2dd21bcced3045be09621fa0cbf73908ba4" - integrity sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA== + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== dependencies: type "^2.0.0" @@ -4775,13 +7274,13 @@ extsprintf@^1.2.0: integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= falafel@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.1.0.tgz#96bb17761daba94f46d001738b3cedf3a67fe06c" - integrity sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw= + version "2.2.4" + resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.2.4.tgz#b5d86c060c2412a43166243cb1bce44d1abd2819" + integrity sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ== dependencies: - acorn "^5.0.0" + acorn "^7.1.1" foreach "^2.0.5" - isarray "0.0.1" + isarray "^2.0.1" object-keys "^1.0.6" fancy-log@^1.3.2, fancy-log@^1.3.3: @@ -4804,6 +7303,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + fast-glob@^2.0.2, fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -4817,40 +7321,66 @@ fast-glob@^2.0.2, fast-glob@^2.2.6: micromatch "^3.1.10" fast-glob@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.1.0.tgz#77375a7e3e6f6fc9b18f061cddd28b8d1eec75ae" - integrity sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" + integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" glob-parent "^5.1.0" merge2 "^1.3.0" micromatch "^4.0.2" + picomatch "^2.2.1" fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-safe-stringify@^2.0.7: + version "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== + fastq@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" - integrity sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.7.0.tgz#fcd79a08c5bd7ec5b55cd3f5c4720db551929801" + integrity sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ== dependencies: - reusify "^1.0.0" + reusify "^1.0.4" -faye-websocket@0.11.x: +faye-websocket@0.11.x, faye-websocket@~0.11.1: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== dependencies: websocket-driver ">=0.5.1" +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +fbjs@^0.8.0: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -4858,6 +7388,13 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +feed@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/feed/-/feed-4.1.0.tgz#58f1c9cc2b44715d14ac59234e1bf20c5d757aa7" + integrity sha512-dAXWXM8QMxZ1DRnAxDmy1MaWZFlh1Ku7TU3onbXgHrVJynsxkNGPUed1AxszVW8AXo43xExronVkIqK+ACsoBA== + dependencies: + xml-js "^1.6.11" + fetch-mock@7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-7.3.3.tgz#d1abdf309cdef8adb4b0fc0596d3f97fcfbd4528" @@ -4869,9 +7406,9 @@ fetch-mock@7.3.3: whatwg-url "^6.5.0" figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figures@^1.7.0: version "1.7.0" @@ -4889,9 +7426,9 @@ figures@^2.0.0: escape-string-regexp "^1.0.5" figures@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" - integrity sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -4933,6 +7470,11 @@ filename-regex@^2.0.0: resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= +filesize@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" + integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg== + filesize@^3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" @@ -4995,6 +7537,15 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-cache-dir@^3.0.0, find-cache-dir@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-parent-dir@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" @@ -5005,6 +7556,21 @@ find-root@^1.0.0: resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@4.1.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" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -5020,21 +7586,6 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.0.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" - findit2@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6" @@ -5065,10 +7616,22 @@ flat-cache@^1.2.1: rimraf "~2.6.2" write "^0.2.1" +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + flatted@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +flatten@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== flush-write-stream@^1.0.0: version "1.1.1" @@ -5090,7 +7653,14 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -fontkit@^1.0.0: +follow-redirects@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb" + integrity sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA== + dependencies: + debug "^3.0.0" + +fontkit@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/fontkit/-/fontkit-1.8.0.tgz#deb9351619e90ddc91707b6156a9f14c8ab11554" integrity sha512-EFDRCca7khfQWYu1iFhsqeABpi87f03MBdkT93ZE6YhqCdMzb5Eojb6c4dlJikGv5liuhByyzA7ikpIPTSBWbQ== @@ -5119,24 +7689,38 @@ for-own@^0.1.4: dependencies: for-in "^1.0.1" -foreach@^2.0.5: +foreach@^2.0.4, foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= -foreground-child@^1.5.6: - version "1.5.6" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" - integrity sha1-T9ca0t/elnibmApcCilZN8svXOk= +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== dependencies: - cross-spawn "^4" - signal-exit "^3.0.0" + cross-spawn "^7.0.0" + signal-exit "^3.0.2" forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +fork-ts-checker-webpack-plugin@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" + integrity sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^3.3.0" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + form-data@2.3.3, form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -5155,10 +7739,15 @@ form-data@^2.3.1: combined-stream "^1.0.6" mime-types "^2.1.12" +format-util@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + formidable@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" - integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== + version "1.2.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" + integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== forwarded@~0.1.2: version "0.1.2" @@ -5190,22 +7779,16 @@ from@~0: resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= +fromentries@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.2.0.tgz#e6aa06f240d6267f913cea422075ef88b63e7897" + integrity sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ== + fs-constants@^1.0.0: version "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.1, fs-extra@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -5240,6 +7823,13 @@ fs-minipass@^1.2.5: dependencies: minipass "^2.6.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -5256,19 +7846,19 @@ fs.realpath@^1.0.0: integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.0.0, fsevents@^1.2.7: - version "1.2.9" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" - integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + version "1.2.12" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c" + integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q== dependencies: + bindings "^1.5.0" nan "^2.12.1" - node-pre-gyp "^0.12.0" -fsevents@~2.1.1: +fsevents@~2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== -function-bind@^1.0.2, function-bind@^1.1.1: +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== @@ -5287,14 +7877,14 @@ g-status@^2.0.2: matcher "^1.0.0" simple-git "^1.85.0" -ganache-cli@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.3.0.tgz#574f9d35aaec8da6e01c2be49db8fe73129eb561" - integrity sha512-8SyzfX2ipRVBx1fBZLg3j8I3E334U3Vazk5mEpYcWqnIjC2ace6jtOXHG4aTuAvSz3+HzQ8p8pRjOJxdDZ2pnQ== +ganache-cli@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.7.0.tgz#b59845578221bdf686cf124d007c5ee62e85a62f" + integrity sha512-9CZsClo9hl5MxGL7hkk14mie89Q94P0idh92jcV7LmppTYTCG7SHatuwcfqN7emFHArMt3fneN4QbH2do2N6Ow== dependencies: - bn.js "4.11.8" - source-map-support "0.5.9" - yargs "11.1.0" + ethereumjs-util "6.1.0" + source-map-support "0.5.12" + yargs "13.2.4" gauge@~2.7.3: version "2.7.4" @@ -5315,6 +7905,11 @@ genfun@^5.0.0: resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + get-assigned-identifiers@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" @@ -5336,9 +7931,9 @@ get-func-name@^2.0.0: integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= get-own-enumerable-property-symbols@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz#6f7764f88ea11e0b514bd9bd860a132259992ca4" - integrity sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA== + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== get-pkg-repo@^1.0.0: version "1.4.0" @@ -5393,7 +7988,7 @@ get-stream@^4.0.0, get-stream@^4.1.0: dependencies: pump "^3.0.0" -get-stream@^5.1.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== @@ -5461,6 +8056,13 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" +github-slugger@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" + integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== + dependencies: + emoji-regex ">=6.0.0 <=6.1.1" + glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -5485,9 +8087,9 @@ glob-parent@^3.1.0: path-dirname "^1.0.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" - integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" @@ -5513,6 +8115,18 @@ glob@7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -5524,7 +8138,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.2: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.2: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -5572,6 +8186,14 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" +global@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + global@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" @@ -5585,10 +8207,23 @@ globals@^11.1.0, globals@^11.3.0, globals@^11.7.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globby@8.0.2, globby@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + globby@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" - integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A== + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== dependencies: "@types/glob" "^7.1.1" array-union "^2.1.0" @@ -5610,14 +8245,13 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" - integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= dependencies: array-union "^1.0.1" - dir-glob "2.0.0" - fast-glob "^2.0.2" + dir-glob "^2.0.0" glob "^7.1.2" ignore "^3.3.5" pify "^3.0.0" @@ -5637,6 +8271,13 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + got@9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -5674,7 +8315,7 @@ 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.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== @@ -5684,6 +8325,21 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1. resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +gray-matter@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.2.tgz#9aa379e3acaf421193fce7d2a28cebd4518ac454" + integrity sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw== + dependencies: + js-yaml "^3.11.0" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + growl@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" @@ -5699,7 +8355,12 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -gzip-size@^5.0.0: +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== + +gzip-size@5.1.1, gzip-size@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== @@ -5707,14 +8368,20 @@ gzip-size@^5.0.0: duplexer "^0.1.1" pify "^4.0.1" -handlebars@^4.0.1, handlebars@^4.1.2, handlebars@^4.4.0: - version "4.5.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" - integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +handlebars@^4.0.1, handlebars@^4.1.2, handlebars@^4.4.0, handlebars@^4.7.2, handlebars@^4.7.3: + version "4.7.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" + integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== dependencies: + minimist "^1.2.5" neo-async "^2.6.0" - optimist "^0.6.1" source-map "^0.6.1" + wordwrap "^1.0.0" optionalDependencies: uglify-js "^3.1.4" @@ -5723,7 +8390,7 @@ har-schema@^2.0.0: resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0: +har-validator@~5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== @@ -5731,6 +8398,11 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +hard-rejection@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -5753,12 +8425,17 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== -has-symbols@^1.0.0: +has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== @@ -5806,7 +8483,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.1, has@^1.0.3: +has@^1.0.0, has@^1.0.1, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -5837,18 +8514,109 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hasha@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hasha/-/hasha-3.0.0.tgz#52a32fab8569d41ca69a61ff1a214f8eb7c8bd39" - integrity sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk= +hasha@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.0.tgz#33094d1f69c40a4a6ac7be53d5fe3ff95a269e0c" + integrity sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw== dependencies: - is-stream "^1.0.1" + is-stream "^2.0.0" + type-fest "^0.8.0" + +hast-to-hyperscript@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-7.0.4.tgz#7c4c037d9a8ea19b0a3fdb676a26448ad922353d" + integrity sha512-vmwriQ2H0RPS9ho4Kkbf3n3lY436QKLq6VaGA1pzBh36hBi3tm1DO9bR+kaJIbpT10UqaANDkMjxvjVfr+cnOA== + dependencies: + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.2.1" + unist-util-is "^3.0.0" + web-namespaces "^1.1.2" + +hast-util-from-parse5@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" + integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== + dependencies: + ccount "^1.0.3" + hastscript "^5.0.0" + property-information "^5.0.0" + web-namespaces "^1.1.2" + xtend "^4.0.1" + +hast-util-parse-selector@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz#60c99d0b519e12ab4ed32e58f150ec3f61ed1974" + integrity sha512-gW3sxfynIvZApL4L07wryYF4+C9VvH3AUi7LAnVXV4MneGEgwOByXvFo18BgmTWnm7oHAe874jKbIB1YhHSIzA== + +hast-util-raw@5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-5.0.2.tgz#62288f311ec2f35e066a30d5e0277f963ad43a67" + integrity sha512-3ReYQcIHmzSgMq8UrDZHFL0oGlbuVGdLKs8s/Fe8BfHFAyZDrdv1fy/AGn+Fim8ZuvAHcJ61NQhVMtyfHviT/g== + dependencies: + hast-util-from-parse5 "^5.0.0" + hast-util-to-parse5 "^5.0.0" + html-void-elements "^1.0.0" + parse5 "^5.0.0" + unist-util-position "^3.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-5.1.2.tgz#09d27bee9ba9348ea05a6cfcc44e02f9083969b6" + integrity sha512-ZgYLJu9lYknMfsBY0rBV4TJn2xiwF1fXFFjbP6EE7S0s5mS8LIKBVWzhA1MeIs1SWW6GnnE4In6c3kPb+CWhog== + dependencies: + hast-to-hyperscript "^7.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" + integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== + dependencies: + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +he@1.2.0, he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +highlight.js@^9.17.1: + version "9.18.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c" + integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg== + +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -5858,6 +8626,21 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hogan.js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + integrity sha1-TNnhq9QpQUbnZ55B14mHMrAse/0= + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -5871,16 +8654,84 @@ hoopy@^0.1.4: integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: - version "2.8.5" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" - integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== html-entities@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= -htmlparser2@^3.9.1: +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier-terser@^5.0.1, html-minifier-terser@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.0.5.tgz#8f12f639789f04faa9f5cf2ff9b9f65607f21f8b" + integrity sha512-cBSFFghQh/uHcfSiL42KxxIRMF7A144+3E44xdlctIjxEmkEfCvouxNyFH2wysXk1fCGBPwtcr3hDWlGTfkDew== + dependencies: + camel-case "^4.1.1" + clean-css "^4.2.3" + commander "^4.1.1" + he "^1.2.0" + param-case "^3.0.3" + relateurl "^0.2.7" + terser "^4.6.3" + +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^4.0.0-beta.11: + version "4.0.4" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.4.tgz#90cdfb168094e93e047174d9baca098ec5540636" + integrity sha512-BREQzUbFfIQS39KqxkT2L1Ot0tuu1isako1CaCQLrgEQ43zi2ScHAe3SMTnVBWsStnIsGtl8jprDdxwZkNhrwQ== + dependencies: + "@types/html-minifier-terser" "^5.0.0" + "@types/tapable" "^1.0.5" + "@types/webpack" "^4.41.8" + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.15" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + +htmlparser2@^3.3.0, htmlparser2@^3.9.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -5908,9 +8759,14 @@ http-cache-semantics@^3.8.1: integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== http-cache-semantics@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" - integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= http-errors@1.7.2: version "1.7.2" @@ -5962,6 +8818,25 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" + integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -5976,6 +8851,11 @@ http-status-codes@1.3.0: resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.3.0.tgz#9cd0e71391773d0671b489d41cbc5094aa4163b6" integrity sha1-nNDnE5F3PQZxtInUHLxQlKpBY7Y= +http2-client@^1.2.5: + version "1.3.3" + resolved "https://registry.yarnpkg.com/http2-client/-/http2-client-1.3.3.tgz#90fc15d646cca86956b156d07c83947d57d659a9" + integrity sha512-nUxLymWQ9pzkzTmir24p2RtsgruLmhje7lH3hLX1IpwvyTg77fW+1brenPPP3USAR+rQ36p5sTA/x7sjCJVkAA== + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -5989,6 +8869,11 @@ https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -6041,20 +8926,27 @@ i18next@^17.0.3: dependencies: "@babel/runtime" "^7.3.1" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" - integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== +iconv-lite@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.1.tgz#b2425d3c7b18f7219f2ca663d103bddb91718d64" + integrity sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q== dependencies: safer-buffer ">= 2.1.2 < 3" +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + idna-uts46-hx@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" @@ -6094,6 +8986,23 @@ ignore@^5.1.1, ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== +immediate@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +immer@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" + integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -6102,7 +9011,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0: +import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -6110,6 +9019,13 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + import-local@2.0.0, import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -6135,11 +9051,26 @@ indent-string@^3.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== +infima@0.2.0-alpha.6: + version "0.2.0-alpha.6" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.6.tgz#69233fc055a7e9eb08fd70e1497ff8e2a68dd928" + integrity sha512-5Oin586QeBa5VdP8xpPuHB/BDg1D66+B5bFG67XPqKV8mD0hwKt2LJFYqoSJKGPBccxGBQpHEOFUd2sUSdhdGA== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -6148,7 +9079,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.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.0, 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== @@ -6163,7 +9094,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -6182,6 +9113,30 @@ init-package-json@^1.10.3: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +inquirer@7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" + integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + inquirer@^6.1.0, inquirer@^6.2.0: version "6.5.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" @@ -6202,22 +9157,22 @@ inquirer@^6.1.0, inquirer@^6.2.0: through "^2.3.6" inquirer@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a" - integrity sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ== + version "7.0.7" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.7.tgz#5653be3bcf11221118b0c474b5d8d008ed0ba449" + integrity sha512-+lV+BE+M3bDVi6Vtejd7sA572D8gJI5qQUciWDqGKvy6Q6GjuEeJAoHRdOSwqtHUvZlNZssnVvTqQyEXKzki/g== dependencies: ansi-escapes "^4.2.1" - chalk "^2.4.2" + chalk "^3.0.0" cli-cursor "^3.1.0" cli-width "^2.0.0" external-editor "^3.0.3" figures "^3.0.0" lodash "^4.17.15" mute-stream "0.0.8" - run-async "^2.2.0" - rxjs "^6.4.0" + run-async "^2.4.0" + rxjs "^6.5.3" string-width "^4.1.0" - strip-ansi "^5.1.0" + strip-ansi "^6.0.0" through "^2.3.6" inside@^1.0.0: @@ -6225,11 +9180,26 @@ inside@^1.0.0: resolved "https://registry.yarnpkg.com/inside/-/inside-1.0.0.tgz#db45e993573cdb3db70b9832e8285bad46424770" integrity sha1-20Xpk1c82z23C5gy6ChbrUZCR3A= +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + interpret@1.2.0, interpret@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -6240,28 +9210,44 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ip@1.1.5: +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@1.1.5, ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" - integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: + 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== -ipfs-unixfs@0.1.16: - version "0.1.16" - resolved "https://registry.yarnpkg.com/ipfs-unixfs/-/ipfs-unixfs-0.1.16.tgz#41140f4359f1b8fe7a970052663331091c5f54c4" - integrity sha512-TX9Dyu77MxpLzGh/LcQne95TofOyvOeW0oOi72aBMMcV1ItP3684e6NTG9KY1qzdrC+ZUR8kT7y18J058n8KXg== +ipfs-unixfs@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/ipfs-unixfs/-/ipfs-unixfs-0.3.0.tgz#8cb0c8031c50f4d4c01d844b4877a5fde5fd23df" + integrity sha512-0lyU0V7dMh9JAN9NPUrsT6Bf8nHpH5YPAbXByrf3snZzY5J5wuvKd9CIBgYAq1eIgTRmJIqfXGFtbD8RDchuoA== dependencies: - protons "^1.0.1" + err-code "^2.0.0" + protons "^1.1.0" irregular-plurals@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872" integrity sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw== +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -6284,6 +9270,19 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-arguments@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" @@ -6294,6 +9293,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -6313,15 +9317,15 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.2: +is-buffer@^2.0.0, is-buffer@^2.0.2, is-buffer@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== -is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== is-ci@^2.0.0: version "2.0.0" @@ -6330,6 +9334,18 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -6345,9 +9361,14 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== is-descriptor@^0.1.0: version "0.1.6" @@ -6372,6 +9393,11 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= +is-docker@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" + integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -6407,11 +9433,9 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^1.0.0: version "1.0.0" @@ -6461,6 +9485,16 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-natural-number@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" @@ -6500,6 +9534,11 @@ is-obj@^1.0.0, is-obj@^1.0.1: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + 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" @@ -6517,6 +9556,11 @@ is-path-cwd@^1.0.0: resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= +is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + is-path-in-cwd@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" @@ -6524,6 +9568,13 @@ is-path-in-cwd@^1.0.0: dependencies: is-path-inside "^1.0.0" +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + is-path-inside@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" @@ -6531,15 +9582,27 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.0.0.tgz#7fd1a7f1b69e160cde9181d2313f445c68aa2679" - integrity sha512-EYisGhpgSCwspmIuRHGjROWTon2Xp8Z7U03Wubk/bTL5TTRC5R1rGVgyjzBrk9+ULdH6cRD06KRcw/xfqhVYKQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" @@ -6570,12 +9633,12 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-regex@^1.0.4, is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== dependencies: - has "^1.0.1" + has "^1.0.3" is-regexp@^1.0.0: version "1.0.0" @@ -6589,11 +9652,21 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + is-retry-allowed@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-root@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + is-ssh@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3" @@ -6606,12 +9679,24 @@ is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== dependencies: - has-symbols "^1.0.0" + has-symbols "^1.0.1" is-text-path@^1.0.1: version "1.0.1" @@ -6637,16 +9722,31 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-wsl@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" + integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -6657,6 +9757,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -6679,24 +9784,50 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.3, istanbul-lib-coverage@^2.0.5: +istanbul-lib-coverage@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== -istanbul-lib-hook@^2.0.3, istanbul-lib-hook@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz#c95695f383d4f8f60df1f04252a9550e15b5b133" - integrity sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== dependencies: - append-transform "^1.0.0" + append-transform "^2.0.0" -istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0, istanbul-lib-instrument@~3.3.0: +istanbul-lib-instrument@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" + integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== + dependencies: + "@babel/core" "^7.7.5" + "@babel/parser" "^7.7.5" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-instrument@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== @@ -6709,32 +9840,44 @@ istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0, istanbul-lib-ins istanbul-lib-coverage "^2.0.5" semver "^6.0.0" -istanbul-lib-report@^2.0.4, istanbul-lib-report@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-processinfo@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" + integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + archy "^1.0.0" + cross-spawn "^7.0.0" + istanbul-lib-coverage "^3.0.0-alpha.1" + make-dir "^3.0.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^3.3.3" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" -istanbul-lib-source-maps@^3.0.2, istanbul-lib-source-maps@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.1.0, istanbul-reports@^2.2.4: - version "2.2.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" - integrity sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA== +istanbul-reports@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: - handlebars "^4.1.2" + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" istanbul@0.4.5: version "0.4.5" @@ -6764,6 +9907,19 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" +jest-worker@^25.1.0: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.2.6.tgz#d1292625326794ce187c38f51109faced3846c58" + integrity sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA== + dependencies: + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jquery@^3.4.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.0.tgz#9980b97d9e4194611c36530e7dc46a58d7340fc9" + integrity sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ== + js-sha3@0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a" @@ -6784,17 +9940,17 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@3.x, js-yaml@^3.11.0, js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.7.0: +js-yaml@3.13.1, js-yaml@3.x, js-yaml@^3.11.0, js-yaml@^3.12.0, js-yaml@^3.12.1, js-yaml@^3.13.1, js-yaml@^3.7.0: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -6812,16 +9968,42 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-pointer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.0.tgz#8e500550a6aac5464a473377da57aa6cc22828d7" + integrity sha1-jlAFUKaqxUZKRzN32leqbMIoKNc= + dependencies: + foreach "^2.0.4" + +json-schema-ref-parser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-6.1.0.tgz#30af34aeab5bee0431da805dac0eb21b574bf63d" + integrity sha512-pXe9H1m6IgIpXmE5JSb8epilNTGsmTb2iPohAXpOdhqGFbQjNeHHsZxU+C8w6T81GZxSPFLeUoqDJmzxx5IGuw== + dependencies: + call-me-maybe "^1.0.1" + js-yaml "^3.12.1" + ono "^4.0.11" + json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" @@ -6847,6 +10029,19 @@ json-stringify-safe@^5.0.1, 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-to-ast@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json-to-ast/-/json-to-ast-2.1.0.tgz#041a9fcd03c0845036acb670d29f425cea4faaf9" + integrity sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ== + dependencies: + code-error-fragment "0.0.230" + grapheme-splitter "^1.0.4" + +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -6854,19 +10049,17 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" - integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== +json5@^2.1.0, json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== dependencies: - minimist "^1.2.0" + minimist "^1.2.5" -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" +jsonc-parser@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== jsonfile@^4.0.0: version "4.0.0" @@ -6880,6 +10073,11 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +jsonpointer@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -6896,9 +10094,9 @@ jssha@2.3.1: integrity sha1-FHshJTaQNcpLL30hDcU58Amz3po= just-extend@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" - integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" + integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== keccak@^1.0.2: version "1.4.0" @@ -6911,14 +10109,14 @@ keccak@^1.0.2: safe-buffer "^5.1.0" keccak@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-2.0.0.tgz#7456ea5023284271e6f362b4397e8df4d2bb994c" - integrity sha512-rKe/lRr0KGhjoz97cwg+oeT1Rj/Y4cjae6glArioUC8JBF9ROGZctwIaaruM7d7naovME4Q8WcQSO908A8qcyQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-2.1.0.tgz#734ea53f2edcfd0f42cdb8d5f4c358fef052752b" + integrity sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q== dependencies: - bindings "^1.2.1" - inherits "^2.0.3" - nan "^2.2.1" - safe-buffer "^5.1.0" + bindings "^1.5.0" + inherits "^2.0.4" + nan "^2.14.0" + safe-buffer "^5.2.0" keccakjs@^0.2.1: version "0.2.3" @@ -6944,6 +10142,18 @@ keyv@3.1.0, keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.0.tgz#2d1dab694926b2d427e4c74804a10850be44c12f" + integrity sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog== + dependencies: + json-buffer "3.0.1" + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -6964,16 +10174,17 @@ kind-of@^5.0.0: integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -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" +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" lcid@^1.0.0: version "1.0.0" @@ -7017,6 +10228,18 @@ lerna@3.16.4: import-local "^2.0.0" npmlog "^4.1.2" +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -7025,15 +10248,6 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -linebreak@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/linebreak/-/linebreak-0.3.0.tgz#0526480a62c05bd679f3e9d99830e09c6a7d0ed6" - integrity sha1-BSZICmLAW9Z58+nZmDDgnGp9DtY= - dependencies: - base64-js "0.0.8" - brfs "^1.3.0" - unicode-trie "^0.3.0" - linebreak@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/linebreak/-/linebreak-1.0.2.tgz#4b5781733e9a9eb2849dba2f963e47c887f8aa06" @@ -7079,6 +10293,26 @@ lint-staged@8.1.3: stringify-object "^3.2.2" yup "^0.26.10" +lint-staged@9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-9.5.0.tgz#290ec605252af646d9b74d73a0fa118362b05a33" + integrity sha512-nawMob9cb/G1J98nb8v3VC/E8rcX1rryUYXVZ69aT9kde6YWX+uvNOEHY5yf2gcWcTJGiD0kqXmCnS3oD75GIA== + dependencies: + chalk "^2.4.2" + commander "^2.20.0" + cosmiconfig "^5.2.1" + debug "^4.1.1" + dedent "^0.7.0" + del "^5.0.0" + execa "^2.0.3" + listr "^0.14.3" + log-symbols "^3.0.0" + micromatch "^4.0.2" + normalize-path "^3.0.0" + please-upgrade-node "^3.1.1" + string-argv "^0.3.0" + stringify-object "^3.3.0" + listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" @@ -7108,7 +10342,7 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" -listr@^0.14.2: +listr@^0.14.2, listr@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== @@ -7174,12 +10408,17 @@ load-json-file@^5.3.0: strip-bom "^3.0.0" type-fest "^0.3.0" -loader-runner@^2.3.0: +load-script@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" + integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= + +loader-runner@^2.3.0, loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@1.2.3, loader-utils@^1.1.0, loader-utils@^1.2.3: +loader-utils@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== @@ -7188,6 +10427,15 @@ loader-utils@1.2.3, loader-utils@^1.1.0, loader-utils@^1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" +loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7216,21 +10464,56 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= + +lodash.chunk@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" + integrity sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw= + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.flatmap@~4.5.0: +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= + +lodash.flatmap@^4.5.0, lodash.flatmap@~4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= +lodash.foreach@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -7246,11 +10529,56 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.merge@^4.4.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.padstart@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs= + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -7271,12 +10599,17 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.toarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= + lodash.unescape@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash.uniq@^4.5.0: +lodash.uniq@4.5.0, lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= @@ -7286,7 +10619,7 @@ lodash@4.17.13: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93" integrity sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA== -lodash@^4.14.2, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.4: +lodash@^4.14.2, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -7296,6 +10629,13 @@ log-driver@^1.2.7: resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== +log-symbols@2.2.0, log-symbols@^2.1.0, log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -7303,13 +10643,6 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^2.1.0, log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -7326,16 +10659,21 @@ log-update@^2.3.0: cli-cursor "^2.0.0" wrap-ansi "^3.0.1" -log4js@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-5.1.0.tgz#3fa5372055a4c2611ab92d80496bffc100841508" - integrity sha512-QtXrBGZiIwfwBrH9zF2uQarvBuJ5+Icqx9fW+nQL4pnmPITJw8n6kh3bck5IkcTDBQatDeKqUMXXX41fp0TIqw== +log4js@~6.1.0: + version "6.1.2" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.1.2.tgz#04688e1f4b8080c127b7dccb0db1c759cbb25dc4" + integrity sha512-knS4Y30pC1e0n7rfx3VxcLOdBCsEo0o6/C7PVTGxdVK+5b1TYOSGQPn9FDcrhkoQBV29qwmA2mtkznPAQKnxQg== dependencies: - date-format "^2.1.0" + date-format "^3.0.0" debug "^4.1.1" flatted "^2.0.1" rfdc "^1.1.4" - streamroller "^2.1.0" + streamroller "^2.2.3" + +loglevel@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" + integrity sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== loglevelnext@^1.0.1: version "1.0.5" @@ -7345,16 +10683,25 @@ loglevelnext@^1.0.1: es6-symbol "^3.1.1" object.assign "^4.1.0" -lolex@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-3.1.0.tgz#1a7feb2fefd75b3e3a7f79f0e110d9476e294434" - integrity sha512-zFo5MgCJ0rZ7gQg69S4pqBsLURbFw11X68C18OcJjJQbqaXm2NoTrGl1IMM3TIz0/BnN1tIs2tzmmqvCsOMMjw== - -lolex@^4.0.1, lolex@^4.1.0, lolex@^4.2.0: +lolex@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== +lolex@^5.0.1, lolex@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" + integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== + dependencies: + "@sinonjs/commons" "^1.7.0" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -7363,6 +10710,13 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lower-case@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" + integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== + dependencies: + tslib "^1.10.0" + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -7388,7 +10742,7 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lunr@^2.3.6: +lunr@2.3.8, lunr@^2.3.6, lunr@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072" integrity sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg== @@ -7405,7 +10759,14 @@ magic-string@^0.22.4: dependencies: vlq "^0.2.2" -make-dir@^1.0.0, make-dir@^1.3.0: +magic-string@^0.25.1, magic-string@^0.25.2: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== @@ -7420,10 +10781,17 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" + integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + dependencies: + semver "^6.0.0" + make-error@^1.1.1: - version "1.3.5" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" - integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== make-fetch-happen@^5.0.0: version "5.0.2" @@ -7469,6 +10837,11 @@ map-obj@^2.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" @@ -7481,11 +10854,26 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +mark.js@^8.11.1: + version "8.11.1" + resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5" + integrity sha1-GA8fnr74sOY45BZq1S24eb6y/8U= + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + marked@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== +marked@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355" + integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw== + matcher@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/matcher/-/matcher-1.1.1.tgz#51d8301e138f840982b338b116bb0c09af62c1c2" @@ -7507,6 +10895,55 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" +mdast-squeeze-paragraphs@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-3.0.5.tgz#f428b6b944f8faef454db9b58f170c4183cb2e61" + integrity sha512-xX6Vbe348Y/rukQlG4W3xH+7v4ZlzUbSY4HUIQCuYrF2DrkcHx584mCaFxkWoDZKNUfyLZItHC9VAqX3kIP7XA== + dependencies: + unist-util-remove "^1.0.0" + +mdast-util-definitions@^1.2.0: + version "1.2.5" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-1.2.5.tgz#3fe622a4171c774ebd06f11e9f8af7ec53ea5c74" + integrity sha512-CJXEdoLfiISCDc2JB6QLb79pYfI6+GcIH+W2ox9nMc7od0Pz+bovcHsiq29xAQY6ayqe/9CsK2VzkSJdg1pFYA== + dependencies: + unist-util-visit "^1.0.0" + +mdast-util-to-hast@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-7.0.0.tgz#589b562ce1ae0a7849e6c38536a9e7bc4f415e54" + integrity sha512-vxnXKSZgvPG2grZM3kxaF052pxsLtq8TPAkiMkqYj1nFTOazYUPXt3LFYIEB6Ws/IX7Uyvljzk64kD6DwZl/wQ== + dependencies: + collapse-white-space "^1.0.0" + detab "^2.0.0" + mdast-util-definitions "^1.2.0" + mdurl "^1.0.1" + trim-lines "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -7528,7 +10965,12 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" -memory-fs@^0.4.0, memory-fs@~0.4.1: +memoize-one@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" + integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== + +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= @@ -7595,6 +11037,23 @@ meow@^5.0.0: trim-newlines "^2.0.0" yargs-parser "^10.0.0" +meow@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.0.tgz#4ff4641818d3502afcddc631f94cb6971a581cb3" + integrity sha512-iIAoeI01v6pmSfObAAWFoITAA4GgiT45m4SmJgoxtZfvI0fyZwhV4d0lTwiUXvAKIPlma05Feb2Xngl52Mj5Cg== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.1.1" + decamelize-keys "^1.1.0" + hard-rejection "^2.0.0" + minimist-options "^4.0.1" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.0" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.8.1" + yargs-parser "^18.1.1" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -7607,12 +11066,10 @@ merge-source-map@1.0.4: dependencies: source-map "^0.5.6" -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.2.3, merge2@^1.3.0: version "1.3.0" @@ -7624,6 +11081,11 @@ methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -7678,23 +11140,28 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.42.0: - version "1.42.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" - integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== +mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.25" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.25.tgz#39772d46621f93e2a80a856c53b86a62156a6437" - integrity sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg== + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== dependencies: - mime-db "1.42.0" + mime-db "1.43.0" mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -7717,6 +11184,30 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" +min-indent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" + integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + +mini-create-react-context@^0.3.0: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189" + integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw== + dependencies: + "@babel/runtime" "^7.4.0" + gud "^1.0.0" + tiny-warning "^1.0.2" + +mini-css-extract-plugin@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz#a875e169beb27c88af77dd962771c9eedc3da161" + integrity sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw== + dependencies: + loader-utils "^1.1.0" + normalize-url "1.9.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -7742,20 +11233,44 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" +minimist-options@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.0.2.tgz#29c4021373ded40d546186725e57761e4b1984a7" + integrity sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a" + integrity sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA== + dependencies: + minipass "^3.0.0" minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" @@ -7765,6 +11280,13 @@ minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^3.0.0, minipass@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" + integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== + dependencies: + yallist "^4.0.0" + minizlib@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -7803,13 +11325,47 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*, mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@*, mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" +mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mobx-react-lite@2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.0.5.tgz#a17296938d34c5dd5e1f2666d57e8f799741cad3" + integrity sha512-7ifvIAHqxGDgVidRiSNIKLenZaspfhSDz9nkyWiyyZlqHbVTnxqNcB1jnQHEE9Kycl75Z//dN3IoQNeqWWsZ4g== + +mobx-react@^6.1.4: + version "6.2.2" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.2.2.tgz#45e8e7c4894cac8399bba0a91060d7cfb8ea084b" + integrity sha512-Us6V4ng/iKIRJ8pWxdbdysC6bnS53ZKLKlVGBqzHx6J+gYPYbOotWvhHZnzh/W5mhpYXxlXif4kL2cxoWJOplQ== + dependencies: + mobx-react-lite "2" + +mobx@5.15.4: + version "5.15.4" + resolved "https://registry.yarnpkg.com/mobx/-/mobx-5.15.4.tgz#9da1a84e97ba624622f4e55a0bf3300fb931c2ab" + integrity sha512-xRFJxSU2Im3nrGCdjSuOTFmxVDGeqOHL+TyADCGbT0k4HHqGmx5u2yaHNryvoORpI4DfbzjJ5jPmuv+d7sioFw== + mocha@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" @@ -7827,7 +11383,36 @@ mocha@5.2.0: mkdirp "0.5.1" supports-color "5.4.0" -mocha@^4.0.1, mocha@^4.1.0: +mocha@6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" + integrity sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" + minimatch "3.0.4" + mkdirp "0.5.1" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.0" + yargs-parser "13.1.1" + yargs-unparser "1.6.0" + +mocha@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" integrity sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA== @@ -7844,9 +11429,9 @@ mocha@^4.0.1, mocha@^4.1.0: supports-color "4.4.0" mock-fs@^4.1.0: - version "4.10.3" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.10.3.tgz#d0550663dd2b5d33a7c1b8713c6925aab07a04ae" - integrity sha512-bcukePBvuA3qovmq0Qtqu9+1APCIGkFHnsozrPIVromt5XFGGgkQSfaN0H6RI8gStHkO/hRgimvS3tooNes4pQ== + version "4.11.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.11.0.tgz#0828107e4b843a6ba855ecebfe3c6e073b69db92" + integrity sha512-Yp4o3/ZA15wsXqJTT+R+9w2AYIkD1i80Lds47wDbuUhOvQvm+O2EfjFZSz0pMgZZSPHRhGxgcd2+GL4+jZMtdw== modify-values@^1.0.0: version "1.0.1" @@ -7854,15 +11439,15 @@ modify-values@^1.0.0: integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== morgan@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" - integrity sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== dependencies: - basic-auth "~2.0.0" + basic-auth "~2.0.1" debug "2.6.9" - depd "~1.1.2" + depd "~2.0.0" on-finished "~2.3.0" - on-headers "~1.0.1" + on-headers "~1.0.2" move-concurrently@^1.0.1: version "1.0.1" @@ -7891,6 +11476,19 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + multimatch@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" @@ -7923,22 +11521,21 @@ multimatch@~4.0.0: minimatch "^3.0.4" mutation-testing-elements@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mutation-testing-elements/-/mutation-testing-elements-1.2.0.tgz#986ce17ed17385fb62d3e7cfcf6ce5d63df1a2d1" - integrity sha512-yE+sg3BAh3nsijMbSsej4ZJQnnJlbb8QtmtXaXx+DRgRvmDs95SGQkOYc+9poXIQkKZxrAynzk6vSFsZibT/lw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/mutation-testing-elements/-/mutation-testing-elements-1.3.1.tgz#a7796c582bc3a29207b6d17804fa6af02decd0b7" + integrity sha512-XXP/enxyOd8X6lK/lu4nlPGLmwH3wfMwj9eatxLp4er0zrmv0p5gGZVkj4KnuuGfp7rnlVNBI/5EZShPJgK3HA== mutation-testing-metrics@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mutation-testing-metrics/-/mutation-testing-metrics-1.2.0.tgz#1fe748aa1fb6bd7968a8cca6bafe88d22b5325a4" - integrity sha512-agtR/XMhIaZMdfp7ovDoXRb/Jme+YaVDDCFX/nCboCkZBPP12QZOiRCgDHt8DcmTSyDKt4HwiUrOUzj1A0H5Zw== + version "1.3.0" + resolved "https://registry.yarnpkg.com/mutation-testing-metrics/-/mutation-testing-metrics-1.3.0.tgz#e53619128a19194b7640b7bde17d7ecb581d2bb9" + integrity sha512-T7UkUGljyCLMEWGK6YtRTjt4fxqi5+052gjDBkKBR6T5Po6DbwwIx6DAvFyBYzjBzUx6wUhXt7UaxB/wy+JyEg== dependencies: - lodash.groupby "^4.6.0" - mutation-testing-report-schema "^1.2.0" + mutation-testing-report-schema "^1.3.0" -mutation-testing-report-schema@^1.0.0, mutation-testing-report-schema@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mutation-testing-report-schema/-/mutation-testing-report-schema-1.2.0.tgz#ed89cde7302833108b75703d4ef62085b1bb490e" - integrity sha512-4LdCuO33sH8R2YJ1idP+GUTjPR/VeSEml1tboQX0+5dSwH2YccUrw73rzAEOzkbeD+IyzBDHLzQKmanScV4smA== +mutation-testing-report-schema@^1.1.0, mutation-testing-report-schema@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mutation-testing-report-schema/-/mutation-testing-report-schema-1.3.1.tgz#ad8f4ad2f9626e811f3c0710498f4e7fa2ca6968" + integrity sha512-2T2A5qBg+2SZ7CtAvW5m4W95VJxZ/UsSWVwzv3VZpm7c2VoGgIWZGPiTC76a+gorxJobyCzkWv0902UNs4Wl5Q== mute-stream@0.0.7: version "0.0.7" @@ -7996,30 +11593,16 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.5.0, neo-async@^2.6.0: +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== -nested-error-stacks@^2.0.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" @@ -8030,54 +11613,115 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nise@^1.4.10, nise@^1.4.8, nise@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" - integrity sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA== +nise@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" + integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ== dependencies: "@sinonjs/formatio" "^3.2.1" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" - lolex "^4.1.0" + lolex "^5.0.1" + path-to-regexp "^1.7.0" + +nise@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/nise/-/nise-3.0.1.tgz#0659982af515e5aac15592226246243e8da0013d" + integrity sha512-fYcH9y0drBGSoi88kvhpbZEsenX58Yr+wOJ4/Mi1K4cy+iGP/a73gNoyNhu5E9QxPdgTlVChfIaAlnyOy/gHUA== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio" "^4.0.1" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + lolex "^5.0.1" + path-to-regexp "^1.7.0" + +nise@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.3.tgz#9f79ff02fa002ed5ffbc538ad58518fa011dc913" + integrity sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" path-to-regexp "^1.7.0" +no-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" + integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== + dependencies: + lower-case "^2.0.1" + tslib "^1.10.0" + +node-emoji@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" + integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== + dependencies: + lodash.toarray "^4.4.0" + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-fetch-h2@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz#c6188325f9bd3d834020bf0f2d6dc17ced2241ac" + integrity sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg== + dependencies: + http2-client "^1.2.5" + node-fetch-npm@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" - integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" + integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg== dependencies: encoding "^0.1.11" json-parse-better-errors "^1.0.0" safe-buffer "^5.1.1" -node-fetch@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" - integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== - node-fetch@2.6.0, node-fetch@^2.3.0, node-fetch@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -node-gyp@^5.0.2: - version "5.0.5" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.0.5.tgz#f6cf1da246eb8c42b097d7cd4d6c3ce23a4163af" - integrity sha512-WABl9s4/mqQdZneZHVWVG4TVr6QQJZUC6PAx47ITSk9lreZ1n+7Z9mMAIbA3vnO4J9W20P7LhCxtzfWsAD/KDw== +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== dependencies: - env-paths "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" + encoding "^0.1.11" + is-stream "^1.0.1" + +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== + +node-gyp@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" + integrity sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.2" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.1.2" + request "^2.88.0" + rimraf "^2.6.3" + semver "^5.7.1" tar "^4.4.12" - which "1" + which "^1.3.1" -node-libs-browser@^2.0.0: +node-libs-browser@^2.0.0, node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== @@ -8117,23 +11761,33 @@ node-notifier@^5.4.0: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" - integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== +node-preload@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" + process-on-spawn "^1.0.0" + +node-readfiles@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/node-readfiles/-/node-readfiles-0.2.0.tgz#dbbd4af12134e2e635c245ef93ffcf6f60673a5d" + integrity sha1-271K8SE04uY1wkXvk//Pb2BnOl0= + dependencies: + es6-promise "^3.2.1" -"nopt@2 || 3", nopt@3.x: +node-releases@^1.1.52, node-releases@^1.1.53: + version "1.1.53" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" + integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== + +nopt@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= @@ -8141,9 +11795,9 @@ node-pre-gyp@^0.12.0: abbrev "1" nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" osenv "^0.1.4" @@ -8170,7 +11824,22 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^3.3.0: +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +normalize-url@^3.0.0, normalize-url@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== @@ -8181,14 +11850,16 @@ normalize-url@^4.1.0: integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== npm-bundled@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" - integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" npm-lifecycle@^3.1.2: - version "3.1.4" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz#de6975c7d8df65f5150db110b57cce498b0b604c" - integrity sha512-tgs1PaucZwkxECGKhC/stbEgFyc3TGh2TJcg2CDr6jbvQRdteHNhmMeljRzpe4wgFAXQADoy1cSqqi7mtiAa5A== + version "3.1.5" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== dependencies: byline "^5.0.0" graceful-fs "^4.1.15" @@ -8199,6 +11870,11 @@ npm-lifecycle@^3.1.2: umask "^1.1.0" which "^1.3.1" +npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" @@ -8209,32 +11885,34 @@ npm-lifecycle@^3.1.2: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-package-json-lint@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/npm-package-json-lint/-/npm-package-json-lint-4.0.3.tgz#3f869195a238bd67c273c7ecb32b80a1e433a866" - integrity sha512-cuvTR2l5dOjjlRR3a1CCp+mh2A2HyQRxydwdcYi0Z77NRlADpf7wF3Jf8XFLGZM7J6afXNRBofBjQ1UWFyOtKA== +npm-package-json-lint@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/npm-package-json-lint/-/npm-package-json-lint-4.5.0.tgz#f6e296ec16a2d5a034daacb4cf0867c0a425be5a" + integrity sha512-SeATq0XEp6Tbn6EP+iyTZV/JTQdgg/yvs+K0B1JBCri4+HISGK6b161K4tF4MBxAtdXlw67DdprJPLwcctE0dQ== dependencies: ajv "^6.10.2" ajv-errors "^1.0.1" - chalk "^2.4.2" + chalk "^3.0.0" cosmiconfig "^5.2.1" debug "^4.1.1" globby "^10.0.1" ignore "^5.1.4" is-plain-obj "^2.0.0" + jsonc-parser "^2.2.0" log-symbols "^3.0.0" - meow "^5.0.0" + meow "^6.0.0" plur "^3.1.1" - semver "^6.3.0" + semver "^7.0.0" strip-json-comments "^3.0.1" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: - version "1.4.6" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4" - integrity sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg== +npm-packlist@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" npm-path@^2.0.2: version "2.0.4" @@ -8274,6 +11952,20 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-3.1.0.tgz#7f91be317f6a466efed3c9f2980ad8a4ee8b0fa5" + integrity sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg== + dependencies: + path-key "^3.0.0" + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + npm-which@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" @@ -8283,7 +11975,7 @@ npm-which@^3.0.1: npm-path "^2.0.2" which "^1.2.10" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2, npmlog@^4.1.2: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -8293,7 +11985,12 @@ npm-which@^3.0.1: gauge "~2.7.3" set-blocking "~2.0.0" -nth-check@~1.0.1: +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= + +nth-check@^1.0.2, nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== @@ -8305,6 +12002,19 @@ nub@~0.0.0: resolved "https://registry.yarnpkg.com/nub/-/nub-0.0.0.tgz#b369bd32bdde66af59605c3b0520bc219dccc04f" integrity sha1-s2m9Mr3eZq9ZYFw7BSC8IZ3MwE8= +null-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/null-loader/-/null-loader-3.0.0.tgz#3e2b6c663c5bda8c73a54357d8fa0708dc61b245" + integrity sha512-hf5sNLl8xdRho4UPBOOeoIwT3WhjYcMUQm0zj44EhD6UscMAz72o2udpoDFBgykucdEDGIcd6SXbc/G6zssbzw== + dependencies: + loader-utils "^1.2.3" + schema-utils "^1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -8318,66 +12028,86 @@ number-to-bn@1.7.0: bn.js "4.11.6" strip-hex-prefix "1.0.0" -nyc@13.2.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/nyc/-/nyc-13.2.0.tgz#6a4a4b3f5f97b63ab491c665567557debcfa23d6" - integrity sha512-gQBlOqvfpYt9b2PZ7qElrHWt8x4y8ApNfbMBoDPdl3sY4/4RJwCxDGTSqhA9RnaguZjS5nW7taW8oToe86JLgQ== +nyc@15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.0.0.tgz#eb32db2c0f29242c2414fe46357f230121cfc162" + integrity sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg== dependencies: - archy "^1.0.0" - arrify "^1.0.1" - caching-transform "^3.0.1" - convert-source-map "^1.6.0" - find-cache-dir "^2.0.0" - find-up "^3.0.0" - foreground-child "^1.5.6" - glob "^7.1.3" - istanbul-lib-coverage "^2.0.3" - istanbul-lib-hook "^2.0.3" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.4" - istanbul-lib-source-maps "^3.0.2" - istanbul-reports "^2.1.0" - make-dir "^1.3.0" - merge-source-map "^1.1.0" - resolve-from "^4.0.0" - rimraf "^2.6.3" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.0" + js-yaml "^3.13.1" + make-dir "^3.0.0" + node-preload "^0.2.0" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" signal-exit "^3.0.2" - spawn-wrap "^1.4.2" - test-exclude "^5.1.0" - uuid "^3.3.2" - yargs "^12.0.5" - yargs-parser "^11.1.1" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + uuid "^3.3.3" + yargs "^15.0.2" + +oas-kit-common@^1.0.7, oas-kit-common@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/oas-kit-common/-/oas-kit-common-1.0.8.tgz#6d8cacf6e9097967a4c7ea8bcbcbd77018e1f535" + integrity sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ== + dependencies: + fast-safe-stringify "^2.0.7" -nyc@14.1.1: - version "14.1.1" - resolved "https://registry.yarnpkg.com/nyc/-/nyc-14.1.1.tgz#151d64a6a9f9f5908a1b73233931e4a0a3075eeb" - integrity sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw== +oas-linter@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/oas-linter/-/oas-linter-3.1.1.tgz#66422732ebb90f03f0863c76604fe967367419c0" + integrity sha512-bV20TrXYG0QUYL78LGByfyeJAkxIhZBkCXotH2SnnBGYvWvtxpp2DzvBvKIPbeAVItQL6WoxNzGGFecU/A6HRg== dependencies: - archy "^1.0.0" - caching-transform "^3.0.2" - convert-source-map "^1.6.0" - cp-file "^6.2.0" - find-cache-dir "^2.1.0" - find-up "^3.0.0" - foreground-child "^1.5.6" - glob "^7.1.3" - istanbul-lib-coverage "^2.0.5" - istanbul-lib-hook "^2.0.7" - istanbul-lib-instrument "^3.3.0" - istanbul-lib-report "^2.0.8" - istanbul-lib-source-maps "^3.0.6" - istanbul-reports "^2.2.4" - js-yaml "^3.13.1" - make-dir "^2.1.0" - merge-source-map "^1.1.0" - resolve-from "^4.0.0" - rimraf "^2.6.3" - signal-exit "^3.0.2" - spawn-wrap "^1.4.2" - test-exclude "^5.2.3" - uuid "^3.3.2" - yargs "^13.2.2" - yargs-parser "^13.0.0" + should "^13.2.1" + yaml "^1.8.3" + +oas-resolver@^2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/oas-resolver/-/oas-resolver-2.3.2.tgz#990a722ddcafe9b0b85893362963b829190b236f" + integrity sha512-toGCUv8wyZZmUAAsw4jn+511xNpUFW2ZLp4sAZ7xpERIeosrbxBxtkVxot9kXvdUHtPjRafi5+bkJ56TwQeYSQ== + dependencies: + node-fetch-h2 "^2.3.0" + oas-kit-common "^1.0.8" + reftools "^1.1.1" + yaml "^1.8.3" + yargs "^15.3.1" + +oas-schema-walker@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/oas-schema-walker/-/oas-schema-walker-1.1.4.tgz#4b9d090c3622039741334d3e138510ff38197618" + integrity sha512-foVDDS0RJYMfhQEDh/WdBuCzydTcsCnGo9EeD8SpWq1uW10JXiz+8SfYVDA7LO87kjmlnTRZle/2gr5qxabaEA== + +oas-validator@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/oas-validator/-/oas-validator-3.4.0.tgz#7633b02e495af4a4e0224b249288b0928748476d" + integrity sha512-l/SxykuACi2U51osSsBXTxdsFc8Fw41xI7AsZkzgVgWJAzoEFaaNptt35WgY9C3757RUclsm6ye5GvSyYoozLQ== + dependencies: + ajv "^5.5.2" + better-ajv-errors "^0.6.7" + call-me-maybe "^1.0.1" + oas-kit-common "^1.0.7" + oas-linter "^3.1.0" + oas-resolver "^2.3.0" + oas-schema-walker "^1.1.3" + reftools "^1.1.0" + should "^13.2.1" + yaml "^1.8.3" oauth-sign@~0.9.0: version "0.9.0" @@ -8398,7 +12128,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.6.0: +object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== @@ -8409,11 +12139,11 @@ object-inspect@~1.4.0: integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw== object-is@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" - integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.0, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -8425,7 +12155,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: +object.assign@4.1.0, object.assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== @@ -8435,13 +12165,13 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" object.omit@^2.0.0: version "2.0.1" @@ -8458,6 +12188,16 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + oboe@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.3.tgz#2b4865dbd46be81225713f4e9bfe4bcf4f680a4f" @@ -8472,6 +12212,11 @@ oboe@2.1.4: dependencies: http-https "^1.0.0" +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + octokit-pagination-methods@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" @@ -8484,7 +12229,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: +on-headers@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== @@ -8510,6 +12255,28 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +ono@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/ono/-/ono-4.0.11.tgz#c7f4209b3e396e8a44ef43b9cedc7f5d791d221d" + integrity sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g== + dependencies: + format-util "^1.0.3" + +open@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" + integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +openapi-sampler@1.0.0-beta.15: + version "1.0.0-beta.15" + resolved "https://registry.yarnpkg.com/openapi-sampler/-/openapi-sampler-1.0.0-beta.15.tgz#c087143826962fa07a0c7bda9ce5c36d732f45de" + integrity sha512-wUD/vD3iBHKik/sME3uwUu4X3HFA53rDrPcVvLzgEELjHLbnTpSYfm4Jo9qZT1dPfBRowAnrF/VRQfOjL5QRAw== + dependencies: + json-pointer "^0.6.0" + opencollective-postinstall@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" @@ -8535,6 +12302,13 @@ openzeppelin-test-helpers@0.1.2: ethjs-abi "^0.2.1" semver "^5.6.0" +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + opn@latest: version "6.0.0" resolved "https://registry.yarnpkg.com/opn/-/opn-6.0.0.tgz#3c5b0db676d5f97da1233d1ed42d182bc5a27d2d" @@ -8542,13 +12316,13 @@ opn@latest: dependencies: is-wsl "^1.1.0" -optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= +optimize-css-assets-webpack-plugin@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572" + integrity sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA== dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" + cssnano "^4.1.10" + last-call-webpack-plugin "^3.0.0" optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" @@ -8562,11 +12336,32 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" word-wrap "~1.2.3" +ora@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.3.tgz#752a1b7b4be4825546a7a3d59256fa523b6b6d05" + integrity sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg== + dependencies: + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-spinners "^2.2.0" + is-interactive "^1.0.0" + log-symbols "^3.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + original-require@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" integrity sha1-DxMEcVhM0zURxew4yNWSE/msXiA= +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -8636,6 +12431,11 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + p-is-promise@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" @@ -8648,10 +12448,10 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== +p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1, p-limit@^2.2.2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" @@ -8693,6 +12493,13 @@ p-map@^2.0.0, p-map@^2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -8710,6 +12517,13 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + p-timeout@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" @@ -8734,13 +12548,13 @@ p-waterfall@^1.0.0: dependencies: p-reduce "^1.0.0" -package-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-3.0.0.tgz#50183f2d36c9e3e528ea0a8605dff57ce976f88e" - integrity sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA== +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== dependencies: graceful-fs "^4.1.15" - hasha "^3.0.0" + hasha "^5.0.0" lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" @@ -8750,9 +12564,9 @@ pako@^0.2.5: integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= pako@~1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== parallel-transform@^1.1.0: version "1.2.0" @@ -8763,6 +12577,14 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" +param-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" + integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== + dependencies: + dot-case "^3.0.3" + tslib "^1.10.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -8782,6 +12604,18 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-entities@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" + integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" @@ -8832,6 +12666,11 @@ parse-node-version@^1.0.0: resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== +parse-numeric-range@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-0.0.2.tgz#b4f09d413c7adbcd987f6e9233c7b4b210c938e4" + integrity sha1-tPCdQTx6282Yf26SM8e0shDJOOQ= + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -8862,11 +12701,24 @@ parse5@^3.0.1: dependencies: "@types/node" "*" +parse5@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +pascal-case@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" + integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -8914,6 +12766,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -8980,25 +12837,25 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pdfkit@>=0.8.1, pdfkit@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/pdfkit/-/pdfkit-0.10.0.tgz#88f2aa8e3cf9e1cc2caff6447b68dd4e435cb284" - integrity sha512-mRJ6iuDzpIQ4ftKp5GvijLXNVRK86xjnyIPBraYSPrUPubNqWM5/oYmc7FZKUWz3wusRTj3PLR9HJ1X5ooqfsg== +pdfkit@>=0.8.1, pdfkit@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/pdfkit/-/pdfkit-0.11.0.tgz#9cdb2fc42bd2913587fe3ddf48cc5bbb3c36f7de" + integrity sha512-1s9gaumXkYxcVF1iRtSmLiISF2r4nHtsTgpwXiK8Swe+xwk/1pm8FJjYqN7L3x13NsWnGyUFntWcO8vfqq+wwA== dependencies: crypto-js "^3.1.9-1" - fontkit "^1.0.0" - linebreak "^0.3.0" - png-js ">=0.1.0" + fontkit "^1.8.0" + linebreak "^1.0.2" + png-js "^1.0.0" pdfmake@^0.1.57: - version "0.1.62" - resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.1.62.tgz#62f2400eba066cc271736b95c7d0cdf9c8e06983" - integrity sha512-2QIzijdkwFBChTFu5nVmMe+fLBQTAYTPTxi4jGbUTyGxZBq7YR1I17FBk1Cs+3nrYufNKNukT6OR1RNxbovsTA== + version "0.1.65" + resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.1.65.tgz#09c4cf796809ec5fce789343560a36780ff47e37" + integrity sha512-MgzRyiKSP3IEUH7vm4oj3lpikmk5oCD9kYxiJM6Z2Xf6CP9EcikeSDey2rGd4WVvn79Y0TGqz2+to8FtWP8MrA== dependencies: - iconv-lite "^0.5.0" + iconv-lite "^0.5.1" linebreak "^1.0.2" - pdfkit "^0.10.0" - svg-to-pdfkit "^0.1.7" + pdfkit "^0.11.0" + svg-to-pdfkit "^0.1.8" pegjs@^0.10.0: version "0.10.0" @@ -9010,20 +12867,25 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= +perfect-scrollbar@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83" + integrity sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.5: - version "2.1.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" - integrity sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA== +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pidtree@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b" - integrity sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg== + version "0.3.1" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" + integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== pify@^2.0.0, pify@^2.3.0: version "2.3.0" @@ -9066,6 +12928,20 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@3.1.0, pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + platform@^1.3.3: version "1.3.5" resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" @@ -9095,22 +12971,658 @@ plur@^3.1.1: dependencies: irregular-plurals "^2.0.0" -png-js@>=0.1.0: +png-js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== +polished@^3.4.2: + version "3.5.1" + resolved "https://registry.yarnpkg.com/polished/-/polished-3.5.1.tgz#657b6faf4c2308f3e0b1951196803a5e5d67b122" + integrity sha512-GVbvskpBiDV5TknurGL6OyFfLHsCknxbU8w5iMppT8rW0tLEoQHrIRfrPNPqGXNj3HGhkjRvhmg59Fy7HSnCAw== + dependencies: + "@babel/runtime" "^7.8.7" + +portfinder@^1.0.25: + version "1.0.25" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" + integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.1" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +postcss-attribute-case-insensitive@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" + integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^6.0.2" + +postcss-calc@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1" + integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ== + dependencies: + postcss "^7.0.27" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-color-functional-notation@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" + integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-gray@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" + integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-color-hex-alpha@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" + integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== + dependencies: + postcss "^7.0.14" + postcss-values-parser "^2.0.1" + +postcss-color-mod-function@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" + integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-rebeccapurple@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" + integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-custom-media@^7.0.8: + version "7.0.8" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" + integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== + dependencies: + postcss "^7.0.14" + +postcss-custom-properties@^8.0.11: + version "8.0.11" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97" + integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== + dependencies: + postcss "^7.0.17" + postcss-values-parser "^2.0.1" + +postcss-custom-selectors@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" + integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-dir-pseudo-class@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" + integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-double-position-gradients@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" + integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== + dependencies: + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-env-function@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" + integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-focus-visible@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" + integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== + dependencies: + postcss "^7.0.2" + +postcss-focus-within@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" + integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== + dependencies: + postcss "^7.0.2" + +postcss-font-variant@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz#71dd3c6c10a0d846c5eda07803439617bbbabacc" + integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== + dependencies: + postcss "^7.0.2" + +postcss-gap-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" + integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== + dependencies: + postcss "^7.0.2" + +postcss-image-set-function@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" + integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-initial@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.2.tgz#f018563694b3c16ae8eaabe3c585ac6319637b2d" + integrity sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA== + dependencies: + lodash.template "^4.5.0" + postcss "^7.0.2" + +postcss-lab-function@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" + integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-load-config@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" + integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + +postcss-logical@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" + integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== + dependencies: + postcss "^7.0.2" + +postcss-media-minmax@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" + integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== + dependencies: + postcss "^7.0.2" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" + integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.16" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.0" + +postcss-modules-scope@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + +postcss-nesting@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052" + integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== + dependencies: + postcss "^7.0.2" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-overflow-shorthand@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" + integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== + dependencies: + postcss "^7.0.2" + +postcss-page-break@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" + integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== + dependencies: + postcss "^7.0.2" + +postcss-place@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" + integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-preset-env@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5" + integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== + dependencies: + autoprefixer "^9.6.1" + browserslist "^4.6.4" + caniuse-lite "^1.0.30000981" + css-blank-pseudo "^0.1.4" + css-has-pseudo "^0.10.0" + css-prefers-color-scheme "^3.1.1" + cssdb "^4.4.0" + postcss "^7.0.17" + postcss-attribute-case-insensitive "^4.0.1" + postcss-color-functional-notation "^2.0.1" + postcss-color-gray "^5.0.0" + postcss-color-hex-alpha "^5.0.3" + postcss-color-mod-function "^3.0.3" + postcss-color-rebeccapurple "^4.0.1" + postcss-custom-media "^7.0.8" + postcss-custom-properties "^8.0.11" + postcss-custom-selectors "^5.1.2" + postcss-dir-pseudo-class "^5.0.0" + postcss-double-position-gradients "^1.0.0" + postcss-env-function "^2.0.2" + postcss-focus-visible "^4.0.0" + postcss-focus-within "^3.0.0" + postcss-font-variant "^4.0.0" + postcss-gap-properties "^2.0.0" + postcss-image-set-function "^3.0.1" + postcss-initial "^3.0.0" + postcss-lab-function "^2.0.1" + postcss-logical "^3.0.0" + postcss-media-minmax "^4.0.0" + postcss-nesting "^7.0.0" + postcss-overflow-shorthand "^2.0.0" + postcss-page-break "^2.0.0" + postcss-place "^4.0.1" + postcss-pseudo-class-any-link "^6.0.0" + postcss-replace-overflow-wrap "^3.0.0" + postcss-selector-matches "^4.0.0" + postcss-selector-not "^4.0.0" + +postcss-pseudo-class-any-link@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" + integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-replace-overflow-wrap@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" + integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== + dependencies: + postcss "^7.0.2" + +postcss-selector-matches@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" + integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-not@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz#c68ff7ba96527499e832724a2674d65603b645c0" + integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-parser@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" + integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== + dependencies: + dot-prop "^5.2.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d" + integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg== + +postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.27" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" + integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^1.0.1: +prepend-http@^1.0.0, prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= @@ -9130,11 +13642,53 @@ prettier@1.16.4: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== +prettier@1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +pretty-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + +prism-react-renderer@^1.0.1, prism-react-renderer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.0.2.tgz#3bb9a6a42f76fc049b03266298c7068fdd4b7ea9" + integrity sha512-0++pJyRfu4v2OxI/Us/5RLui9ESDkTiLkVCtKuPZYdpB8UQWJpnJQhPrWab053XtsKW3oM0sD69uJ6N9exm1Ag== + +prismjs@^1.17.1, prismjs@^1.19.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.20.0.tgz#9b685fc480a3514ee7198eac6a3bf5024319ff03" + integrity sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ== + optionalDependencies: + clipboard "^2.0.0" + +private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -9145,7 +13699,7 @@ process@~0.5.1: resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= -progress@^2.0.0, progress@~2.0.0: +progress@^2.0.0, progress@^2.0.3, 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== @@ -9163,6 +13717,13 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + promzard@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" @@ -9170,20 +13731,36 @@ promzard@^0.3.0: dependencies: read "1" +prop-types@^15.5.0, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + property-expr@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f" integrity sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g== +property-information@^5.0.0, property-information@^5.3.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.4.0.tgz#16e08f13f4e5c4a7be2e4ec431c01c4f8dba869a" + integrity sha512-nmMWAm/3vKFGmmOWOcdLjgq/Hlxa+hsuR/px1Lp/UGEyc5A22A6l78Shc2C0E71sPmAqglni+HrS7L7VJ7AUCA== + dependencies: + xtend "^4.0.0" + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= protocol-buffers-schema@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz#00434f608b4e8df54c59e070efeefc37fb4bb859" - integrity sha512-Xdayp8sB/mU+sUV4G7ws8xtYMGdQnxbeIfLjyO9TZZRJdztBGhlmbI5x1qcY4TG5hBkIKGnc28i7nXxaugu88w== + version "3.4.0" + resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz#2f0ea31ca96627d680bf2fefae7ebfa2b6453eae" + integrity sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA== protocols@^1.1.0, protocols@^1.4.0: version "1.4.7" @@ -9197,10 +13774,10 @@ protoduck@^5.0.1: dependencies: genfun "^5.0.0" -protons@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/protons/-/protons-1.0.1.tgz#1c107144c07fc2d1cb8b6cb76451e6a938237676" - integrity sha512-+0ZKnfVs+4c43tbAQ5j0Mck8wPcLnlxUYzKQoB4iDW4ocdXGnN4P+0dDbgX1FTpoY9+7P2Tn2scJyHHqj+S/lQ== +protons@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/protons/-/protons-1.1.0.tgz#08f00cb6ea2b9a40d11da2e2d580c3662056a994" + integrity sha512-rxf3et88VGRJkXIcDK1nemQM9OpnKsRVuZW+vkJLRmytA6530hQ+k/r2DpclNJCYF+xUl2MXsvRsK+MJgcbfEg== dependencies: protocol-buffers-schema "^3.3.1" safe-buffer "^5.1.1" @@ -9208,12 +13785,12 @@ protons@^1.0.1: varint "^5.0.0" proxy-addr@~2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" - integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== dependencies: forwarded "~0.1.2" - ipaddr.js "1.9.0" + ipaddr.js "1.9.1" proxy-middleware@latest: version "0.15.0" @@ -9230,10 +13807,10 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24: - version "1.4.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" - integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== public-encrypt@^4.0.0: version "4.0.3" @@ -9282,17 +13859,17 @@ punycode@2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -q@^1.5.1: +q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -9303,15 +13880,23 @@ qs@6.7.0: integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.5.1: - version "6.9.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9" - integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA== + version "6.9.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" + integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + query-string@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" @@ -9321,7 +13906,7 @@ query-string@^5.0.1: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@^0.2.0: +querystring-es3@^0.2.0, querystring-es3@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= @@ -9331,11 +13916,21 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + quote-stream@^1.0.1, quote-stream@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" @@ -9366,38 +13961,197 @@ randomfill@^1.0.3: resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +randomhex@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" + integrity sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU= + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-dev-utils@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19" + integrity sha512-XxTbgJnYZmxuPtY3y/UV0D8/65NKkmaia4rXzViknVnZeVlklSh8u6TnaEYPfAi/Gh1TP4mEOXHI6jQOPbeakQ== + dependencies: + "@babel/code-frame" "7.8.3" + address "1.1.2" + browserslist "4.10.0" + chalk "2.4.2" + cross-spawn "7.0.1" + detect-port-alt "1.1.6" + escape-string-regexp "2.0.0" + filesize "6.0.1" + find-up "4.1.0" + fork-ts-checker-webpack-plugin "3.1.1" + global-modules "2.0.0" + globby "8.0.2" + gzip-size "5.1.1" + immer "1.10.0" + inquirer "7.0.4" + is-root "2.1.0" + loader-utils "1.2.3" + open "^7.0.2" + pkg-up "3.1.0" + react-error-overlay "^6.0.7" + recursive-readdir "2.2.2" + shell-quote "1.7.2" + strip-ansi "6.0.0" + text-table "0.2.0" + +react-dom@16.8.4: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.4.tgz#1061a8e01a2b3b0c8160037441c3bf00a0e3bc48" + integrity sha512-Ob2wK7XG2tUDt7ps7LtLzGYYB6DXMCLj0G5fO6WeEICtT4/HdpOi7W/xLzZnR6RCG1tYza60nMdqtxzA8FaPJQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.4" + +react-dropdown@^1.6.4: + version "1.7.0" + resolved "https://registry.yarnpkg.com/react-dropdown/-/react-dropdown-1.7.0.tgz#20287aafabdece49a6595ebe40e3fa1a37c26456" + integrity sha512-zFZ73pgLA32hArpE4j/7DtOEhOMg240XG5QvbAb0/VinGekkHDVIakMyAFUKC5jDz8jqXEltgriqFW9R5iCtPQ== + dependencies: + classnames "^2.2.3" + +react-error-overlay@^6.0.7: + version "6.0.7" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" + integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== + +react-fast-compare@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + +react-helmet@^6.0.0-beta: + version "6.0.0-beta.2" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.0.0-beta.2.tgz#8f2d464472e405e5f04efe10883c384d826258b7" + integrity sha512-rRAeil8HsddijqG2+1jeR5qY9wGQvK1zPMf+8MkiTCyhpEP/2qhAAPolEsveeAlz0xSv5C5xYypHtgCtFn0euw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^2.0.4" + react-side-effect "^2.1.0" + +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-live@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/react-live/-/react-live-2.2.2.tgz#834edf1c11204e49fa7468166316b2e70da1a6b0" + integrity sha512-kJYAzKnPsR4oXleAX9lLsJA330BhTmSWHhr3ienZA2E/0eFDRodGl3I7sge8pp1vjc2K5Aaz73KpFUnV7Lq/DQ== + dependencies: + buble "0.19.6" + core-js "^2.4.1" + create-react-context "0.2.2" + dom-iterator "^1.0.0" + prism-react-renderer "^1.0.1" + prop-types "^15.5.8" + react-simple-code-editor "^0.10.0" + unescape "^1.0.1" + +react-loadable-ssr-addon@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon/-/react-loadable-ssr-addon-0.2.0.tgz#fbf4ebfa9cfd6eadb3c346f0459e1cee01c9cae8" + integrity sha512-gTfPaxWZa5mHKeSOE61RpoLe7hyjcJHgNa5m0ZZGV3OCkWsOKlfYgoBxXzu9ENg/ePR/kFd5H3ncF4K5eyyNTQ== + +react-loadable@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/react-loadable/-/react-loadable-5.5.0.tgz#582251679d3da86c32aae2c8e689c59f1196d8c4" + integrity sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg== + dependencies: + prop-types "^15.5.0" + +react-router-config@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" + integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== + dependencies: + "@babel/runtime" "^7.1.2" -randomhex@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" - integrity sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU= +react-router-dom@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" + integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.1.2" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.1.2, react-router@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418" + integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.3.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +react-side-effect@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.0.tgz#1ce4a8b4445168c487ed24dab886421f74d380d3" + integrity sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +react-simple-code-editor@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.10.0.tgz#73e7ac550a928069715482aeb33ccba36efe2373" + integrity sha512-bL5W5mAxSW6+cLwqqVWY47Silqgy2DKDTR4hDBrLrUqC5BXc29YVx17l2IZk5v36VcDEq1Bszu2oHm1qBwKqBA== + +react-tabs@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-3.1.0.tgz#ecc50f034c1d6da2606fab9293055bbc861b382e" + integrity sha512-9RKc77HCPsjQDVPyZEw37g3JPtg26oSQ9o4mtaVXjJuLedDX5+TQcE+MRNKR+4aO3GMAY4YslCePGG1//MQ3Jg== dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" + classnames "^2.2.0" + prop-types "^15.5.0" -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== +react-toggle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/react-toggle/-/react-toggle-4.1.1.tgz#2317f67bf918ea3508a96b09dd383efd9da572af" + integrity sha512-+wXlMcSpg8SmnIXauMaZiKpR+r2wp2gMUteroejp2UTSqGTVvZLN+m9EhMzFARBKEw7KpQOwzCyfzeHeAndQGw== dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" + classnames "^2.2.5" + +react@16.8.4: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.4.tgz#fdf7bd9ae53f03a9c4cd1a371432c206be1c4768" + integrity sha512-0GQ6gFXfUH7aZcjGVymlPOASTuSjlQL4ZtVC5YKH+3JL6bBLCVO21DknzmaPlI90LN253ojj02nsapy+j7wIjg== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.4" read-cmd-shim@^1.0.1: version "1.0.5" @@ -9407,14 +14161,14 @@ read-cmd-shim@^1.0.1: graceful-fs "^4.1.2" "read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13: - version "2.1.0" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.0.tgz#e3d42e6c35ea5ae820d9a03ab0c7291217fc51d5" - integrity sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A== + version "2.1.1" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.1.tgz#16aa66c59e7d4dad6288f179dd9295fd59bb98f1" + integrity sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A== dependencies: glob "^7.1.1" json-parse-better-errors "^1.0.1" normalize-package-data "^2.0.0" - slash "^1.0.0" + npm-normalize-package-bin "^1.0.0" optionalDependencies: graceful-fs "^4.1.2" @@ -9443,13 +14197,14 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== +read-pkg-up@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" read-pkg@^1.0.0: version "1.1.0" @@ -9469,7 +14224,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^5.1.1: +read-pkg@^5.1.1, read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -9487,9 +14242,9 @@ read@1, read@~1.0.1: mute-stream "~0.0.4" "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + 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== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -9499,10 +14254,10 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.1.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" - integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== +"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -9527,12 +14282,12 @@ readdirp@^2.0.0, readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" - integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== +readdirp@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" + integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== dependencies: - picomatch "^2.0.4" + picomatch "^2.0.7" rechoir@^0.6.2: version "0.6.2" @@ -9541,6 +14296,13 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -9557,6 +14319,67 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +redoc@2.0.0-rc.23: + version "2.0.0-rc.23" + resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.0.0-rc.23.tgz#3b8a47d9357e6ee52a0d93d08fb03ce00f424728" + integrity sha512-ifrewYzrCrGBv6bFSh5FEnDTywVm6IL/VEb7PUpVPCC1lMceMAB1HrlyKlBCyNqjq9LcRO+y9q881LfPXS4PUw== + dependencies: + classnames "^2.2.6" + decko "^1.2.0" + dompurify "^2.0.7" + eventemitter3 "^4.0.0" + json-pointer "^0.6.0" + json-schema-ref-parser "^6.1.0" + lunr "2.3.8" + mark.js "^8.11.1" + marked "^0.7.0" + memoize-one "~5.1.1" + mobx-react "^6.1.4" + openapi-sampler "1.0.0-beta.15" + perfect-scrollbar "^1.4.0" + polished "^3.4.2" + prismjs "^1.17.1" + prop-types "^15.7.2" + react-dropdown "^1.6.4" + react-tabs "^3.0.0" + slugify "^1.3.6" + stickyfill "^1.1.1" + swagger2openapi "^5.3.1" + tslib "^1.10.0" + url-template "^2.0.8" + +reduce@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" + integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== + dependencies: + object-keys "^1.1.0" + +reftools@^1.1.0, reftools@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.1.1.tgz#09cff48f62c09cae40bd1fe9683f5412d2a4656d" + integrity sha512-7ySkzK7YpUeJP16rzJqEXTZ7IrAq/AL/p+wWejD9wdKQOe+mYYVAOB3w5ZTs2eoHfmAidwr/6PcC+q+LzPF/DQ== + +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + regenerator-runtime@^0.10.5: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" @@ -9572,10 +14395,18 @@ regenerator-runtime@^0.12.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== -regenerator-runtime@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + +regenerator-transform@^0.14.2: + version "0.14.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" + integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== + dependencies: + "@babel/runtime" "^7.8.4" + private "^0.1.8" regex-cache@^0.4.2: version "0.4.4" @@ -9593,17 +14424,56 @@ regex-not@^1.0.0, regex-not@^1.0.2: safe-regex "^1.1.0" regexp.prototype.flags@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c" - integrity sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA== + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== dependencies: - define-properties "^1.1.2" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpu-core@^4.2.0, regexpu-core@^4.5.4, regexpu-core@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" + integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +regjsgen@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== + dependencies: + jsesc "~0.5.0" + +rehype-parse@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" + integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== + dependencies: + hast-util-from-parse5 "^5.0.0" + parse5 "^5.0.0" + xtend "^4.0.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" @@ -9622,17 +14492,88 @@ remap-istanbul@0.13.0: source-map "0.6.1" through2 "3.0.0" +remark-admonitions@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" + integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== + dependencies: + rehype-parse "^6.0.2" + unified "^8.4.2" + unist-util-visit "^2.0.1" + +remark-emoji@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.1.0.tgz#69165d1181b98a54ad5d9ef811003d53d7ebc7db" + integrity sha512-lDddGsxXURV01WS9WAiS9rO/cedO1pvr9tahtLhr6qCGFhHG4yZSJW3Ha4Nw9Uk1hLNmUBtPC0+m45Ms+xEitg== + dependencies: + emoticon "^3.2.0" + node-emoji "^1.10.0" + unist-util-visit "^2.0.2" + +remark-mdx@^1.5.8: + version "1.5.8" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.5.8.tgz#81fd9085e56ea534b977d08d6f170899138b3f38" + integrity sha512-wtqqsDuO/mU/ucEo/CDp0L8SPdS2oOE6PRsMm+lQ9TLmqgep4MBmyH8bLpoc8Wf7yjNmae/5yBzUN1YUvR/SsQ== + dependencies: + "@babel/core" "7.8.4" + "@babel/helper-plugin-utils" "7.8.3" + "@babel/plugin-proposal-object-rest-spread" "7.8.3" + "@babel/plugin-syntax-jsx" "7.8.3" + "@mdx-js/util" "^1.5.8" + is-alphabetical "1.0.4" + remark-parse "7.0.2" + unified "8.4.2" + +remark-parse@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-7.0.2.tgz#41e7170d9c1d96c3d32cf1109600a9ed50dba7cf" + integrity sha512-9+my0lQS80IQkYXsMA8Sg6m9QfXYJBnXjWYN5U+kFc5/n69t+XZVXU/ZBYr3cYH8FheEGf1v87rkFDhJ8bVgMA== + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remark-squeeze-paragraphs@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-3.0.4.tgz#9fe50c3bf3b572dd88754cd426ada007c0b8dc5f" + integrity sha512-Wmz5Yj9q+W1oryo8BV17JrOXZgUKVcpJ2ApE2pwnoHwhFKSk4Wp2PmFNbmJMgYSqAdFwfkoe+TSYop5Fy8wMgA== + dependencies: + mdast-squeeze-paragraphs "^3.0.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= +renderkid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + dependencies: + css-select "^1.1.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" + strip-ansi "^3.0.0" + utila "^0.4.0" + repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -9644,10 +14585,15 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.79.0, request@^2.86.0, request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= + +request@^2.79.0, request@^2.86.0, request@^2.87.0, request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -9656,7 +14602,7 @@ request@^2.79.0, request@^2.86.0, request@^2.87.0: extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.0" + har-validator "~5.1.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" @@ -9666,7 +14612,7 @@ request@^2.79.0, request@^2.86.0, request@^2.87.0: performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" - tough-cookie "~2.4.3" + tough-cookie "~2.5.0" tunnel-agent "^0.6.0" uuid "^3.3.2" @@ -9675,10 +14621,10 @@ 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: - 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-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha1-rW8wwTvs15cBDEaK+ndcDAprR/o= require-main-filename@^1.0.1: version "1.0.1" @@ -9695,6 +14641,11 @@ requireindex@~1.1.0: resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI= +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -9720,6 +14671,16 @@ 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-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -9730,10 +14691,10 @@ resolve@1.1.7, resolve@1.1.x: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.0.0, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2: - version "1.12.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" - integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== +resolve@^1.0.0, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" + integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== dependencies: path-parse "^1.0.6" @@ -9777,7 +14738,12 @@ retry@^0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= -reusify@^1.0.0: +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== @@ -9787,12 +14753,15 @@ rfdc@^1.1.4: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: - 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" +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= rimraf@2.6.3, rimraf@~2.6.2: version "2.6.3" @@ -9801,10 +14770,17 @@ rimraf@2.6.3, rimraf@~2.6.2: dependencies: glob "^7.1.3" -rimraf@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.0.tgz#614176d4b3010b75e5c390eb0ee96f6dc0cebb9b" - integrity sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg== +rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: + 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" + +rimraf@^3.0.0, rimraf@~3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" @@ -9823,10 +14799,10 @@ rlp@^2.0.0, rlp@^2.2.3: dependencies: bn.js "^4.11.1" -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= +run-async@^2.2.0, run-async@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" + integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== dependencies: is-promise "^2.1.0" @@ -9847,10 +14823,15 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.4.0, rxjs@~6.5.1: - version "6.5.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" - integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== +rx@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= + +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@~6.5.1: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== dependencies: tslib "^1.9.0" @@ -9883,11 +14864,19 @@ satoshi-bitcoin@1.0.4: dependencies: big.js "^3.1.3" -sax@^1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +scheduler@^0.13.4: + version "0.13.6" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" + integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -9897,12 +14886,21 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^2.0.0, schema-utils@^2.6.4, schema-utils@^2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" + integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== + dependencies: + ajv "^6.12.0" + ajv-keywords "^3.4.1" + scope-analyzer@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/scope-analyzer/-/scope-analyzer-2.0.5.tgz#72c9c6770c3e66984f84c7d3c7045998a1a7db8a" - integrity sha512-+U5H0417mnTEstCD5VwOYO7V4vYuSqwqjFap40ythe67bhMFL5C3UgPwyBv7KDJsqUBIKafOD57xMlh1rN7eaw== + version "2.1.1" + resolved "https://registry.yarnpkg.com/scope-analyzer/-/scope-analyzer-2.1.1.tgz#5156c27de084d74bf75af9e9506aaf95c6e73dd6" + integrity sha512-azEAihtQ9mEyZGhfgTJy3IbOWEzeOrYbg7NcYEshPKnKd+LZmC3TNd5dmDxbLBsTG/JVWmCp+vDJ03vJjeXMHg== dependencies: array-from "^2.1.1" + dash-ast "^1.0.0" es6-map "^0.1.5" es6-set "^0.1.5" es6-symbol "^3.1.1" @@ -9927,6 +14925,15 @@ scrypt.js@0.2.0: scrypt "^6.0.2" scryptsy "^1.2.1" +scrypt.js@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.3.0.tgz#6c62d61728ad533c8c376a2e5e3e86d41a95c4c0" + integrity sha512-42LTc1nyFsyv/o0gcHtDztrn+aqpkaCNt5Qh7ATBZfhEZU7IC/0oT/qbBH+uRNoAPvs2fwiOId68FDEoSRA8/A== + dependencies: + scryptsy "^1.2.1" + optionalDependencies: + scrypt "^6.0.2" + scrypt@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" @@ -9946,7 +14953,7 @@ scryptsy@^1.2.1: dependencies: pbkdf2 "^3.0.3" -secp256k1@3.7.1, secp256k1@^3.0.1: +secp256k1@3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.7.1.tgz#12e473e0e9a7c2f2d4d4818e722ad0e14cc1e2f1" integrity sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g== @@ -9960,6 +14967,28 @@ secp256k1@3.7.1, secp256k1@^3.0.1: nan "^2.14.0" safe-buffer "^5.1.2" +secp256k1@3.8.0, secp256k1@^3.0.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== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.2" + nan "^2.14.0" + safe-buffer "^5.1.2" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + seek-bzip@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" @@ -9967,12 +14996,29 @@ seek-bzip@^1.0.5: dependencies: commander "~2.8.1" +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== + dependencies: + node-forge "0.9.0" + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9992,15 +15038,20 @@ semver@6.2.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + semver@^6.0.0, semver@^6.1.1, semver@^6.2.0, semver@^6.3.0, semver@~6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= +semver@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.2.1.tgz#d997aa36bdbb00b501ae4ac4c7d17e9f7a587ae5" + integrity sha512-aHhm1pD02jXXkyIpq25qBZjr3CQgg8KST8uX0OWXch3xE6jw+1bfbWnCjzMwojsTquroUmKFHNzU6x26mEiRxw== send@0.17.1, send@latest: version "0.17.1" @@ -10026,6 +15077,11 @@ serialize-javascript@^1.7.0: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A== +serialize-javascript@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" + integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -10104,9 +15160,9 @@ sha.js@^2.4.0, sha.js@^2.4.8: safe-buffer "^5.0.1" sha3@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.3.tgz#ed5958fa8331df1b1b8529ca9fdf225a340c5418" - integrity sha512-sOWDZi8cDBRkLfWOw18wvJyNblXDHzwMGnRWut8zNNeIeLnmMRO17bjpLc7OzMuj1ASUgx2IyohzUCAl+Kx5vA== + version "1.2.6" + resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.6.tgz#102aa3e47dc793e2357902c3cce8760822f9e905" + integrity sha512-KgLGmJGrmNB4JWVsAV11Yk6KbvsAiygWJc7t5IebWva/0NukNrjJqhtKhzy3Eiv2AKuGvhZZt7dt1mDo7HkoiQ== dependencies: nan "2.13.2" @@ -10115,6 +15171,11 @@ shallow-copy@~0.0.1: resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" integrity sha1-QV9CcC1z2BAzApLMXuhurhoRoXA= +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -10122,17 +15183,29 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shell-quote@^1.6.1: +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@1.7.2, shell-quote@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shelljs@0.8.3, shelljs@^0.8.1: +shelljs@0.8.3, shelljs@^0.8.1, shelljs@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== @@ -10146,6 +15219,50 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== +should-equal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" + integrity sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA== + dependencies: + should-type "^1.4.0" + +should-format@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1" + integrity sha1-m/yPdPo5IFxT04w01xcwPidxJPE= + dependencies: + should-type "^1.3.0" + should-type-adaptors "^1.0.1" + +should-type-adaptors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz#401e7f33b5533033944d5cd8bf2b65027792e27a" + integrity sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA== + dependencies: + should-type "^1.3.0" + should-util "^1.0.0" + +should-type@^1.3.0, should-type@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3" + integrity sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM= + +should-util@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.1.tgz#fb0d71338f532a3a149213639e2d32cbea8bcb28" + integrity sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g== + +should@^13.2.1: + version "13.2.3" + resolved "https://registry.yarnpkg.com/should/-/should-13.2.3.tgz#96d8e5acf3e97b49d89b51feaa5ae8d07ef58f10" + integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== + dependencies: + should-equal "^2.0.0" + should-format "^3.0.3" + should-type "^1.4.0" + should-type-adaptors "^1.0.1" + should-util "^1.0.0" + shx@0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/shx/-/shx-0.3.2.tgz#40501ce14eb5e0cbcac7ddbd4b325563aad8c123" @@ -10156,9 +15273,9 @@ shx@0.3.2: shelljs "^0.8.1" signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== signed-varint@^2.0.1: version "2.0.1" @@ -10182,9 +15299,9 @@ simple-get@^2.7.0: simple-concat "^1.0.0" simple-git@^1.85.0: - version "1.126.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.126.0.tgz#0c345372275139c8433b8277f4b3e155092aa434" - integrity sha512-47mqHxgZnN8XRa9HbpWprzUv3Ooqz9RY/LSZgvA7jCkW8jcwLahMz7LKugY91KZehfG0sCVPtgXiU72hd6b1Bw== + version "1.132.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.132.0.tgz#53ac4c5ec9e74e37c2fd461e23309f22fcdf09b1" + integrity sha512-xauHm1YqCTom1sC9eOjfq3/9RKiUA9iPnxBbrY2DdL8l4ADMu0jjM5l5lphQP5YWNqAL2aXC/OeuQ76vHtW5fg== dependencies: debug "^4.0.1" @@ -10193,31 +15310,12 @@ simple-statistics@7.0.2: resolved "https://registry.yarnpkg.com/simple-statistics/-/simple-statistics-7.0.2.tgz#46671f45cb6f9b68207dccd6a3c4f7e745e6e638" integrity sha512-wqTjlmbiaL6Fqaw28tSjQrthjxVV17MMfi/H/qvE0jAvtLspB2S7gEtcR27uvhSRAa64LhjhoO169rX8sFW3pg== -sinon@7.2.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.2.3.tgz#f8bfd956df32ddf592f8c102fd46982366412d8e" - integrity sha512-i6j7sqcLEqTYqUcMV327waI745VASvYuSuQMCjbAwlpAeuCgKZ3LtrjDxAbu+GjNQR0FEDpywtwGCIh8GicNyg== - dependencies: - "@sinonjs/commons" "^1.3.0" - "@sinonjs/formatio" "^3.1.0" - "@sinonjs/samsam" "^3.0.2" - diff "^3.5.0" - lolex "^3.0.0" - nise "^1.4.8" - supports-color "^5.5.0" - -sinon@7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.3.2.tgz#82dba3a6d85f6d2181e1eca2c10d8657c2161f28" - integrity sha512-thErC1z64BeyGiPvF8aoSg0LEnptSaWE7YhdWWbWXgelOyThent7uKOnnEh9zBxDbKixtr5dEko+ws1sZMuFMA== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= dependencies: - "@sinonjs/commons" "^1.4.0" - "@sinonjs/formatio" "^3.2.1" - "@sinonjs/samsam" "^3.3.1" - diff "^3.5.0" - lolex "^4.0.1" - nise "^1.4.10" - supports-color "^5.5.0" + is-arrayish "^0.3.1" sinon@7.5.0: version "7.5.0" @@ -10232,6 +15330,42 @@ sinon@7.5.0: nise "^1.5.2" supports-color "^5.5.0" +sinon@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-8.1.1.tgz#21fffd5ad0a2d072a8aa7f8a3cf7ed2ced497497" + integrity sha512-E+tWr3acRdoe1nXbHMu86SSqA1WGM7Yw3jZRLvlCMnXwTHP8lgFFVn5BnKnF26uc5SfZ3D7pA9sN7S3Y2jG4Ew== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio" "^4.0.1" + "@sinonjs/samsam" "^4.2.2" + diff "^4.0.2" + lolex "^5.1.2" + nise "^3.0.1" + supports-color "^7.1.0" + +sinon@9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.1.tgz#dbb18f7d8f5835bcf91578089c0a97b2fffdd73b" + integrity sha512-iTTyiQo5T94jrOx7X7QLBZyucUJ2WvL9J13+96HMfm2CGoJYbIPqRfl6wgNcqmzk0DI28jeGx5bUTXizkrqBmg== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.0.3" + diff "^4.0.2" + nise "^4.0.1" + supports-color "^7.1.0" + +sitemap@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-3.2.2.tgz#3f77c358fa97b555c879e457098e39910095c62b" + integrity sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg== + dependencies: + lodash.chunk "^4.2.0" + lodash.padstart "^4.6.1" + whatwg-url "^7.0.0" + xmlbuilder "^13.0.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -10266,6 +15400,11 @@ slide@^1.1.6: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= +slugify@^1.3.6: + version "1.4.0" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.0.tgz#c9557c653c54b0c7f7a8e786ef3431add676d2cb" + integrity sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ== + smart-buffer@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" @@ -10301,6 +15440,26 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.0.1" + socks-proxy-agent@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" @@ -10327,18 +15486,6 @@ sol-explore@1.6.1: resolved "https://registry.yarnpkg.com/sol-explore/-/sol-explore-1.6.1.tgz#b59f073c69fe332560d5a10c32ba8ca7f2986cfb" integrity sha1-tZ8HPGn+MyVg1aEMMrqMp/KYbPs= -solc@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.5.0.tgz#2deb2ae992acac3afb909f85c38d00f01dcb335e" - integrity sha512-mdLHDl9WeYrN+FIKcMc9PlPfnA9DG9ur5QpCDKcv6VC4RINAsTF4EMuXMZMKoQTvZhtLyJIVH/BZ+KU830Z8Xg== - dependencies: - fs-extra "^0.30.0" - keccak "^1.0.2" - memorystream "^0.3.1" - require-from-string "^2.0.0" - semver "^5.5.0" - yargs "^11.0.0" - solium-plugin-security@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/solium-plugin-security/-/solium-plugin-security-0.1.1.tgz#2a87bcf8f8c3abf7d198e292e4ac080284e3f3f6" @@ -10372,6 +15519,13 @@ solparse@2.2.8: pegjs "^0.10.0" yargs "^10.0.3" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -10385,28 +15539,28 @@ source-list-map@^2.0.0: integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== dependencies: - atob "^2.1.1" + atob "^2.1.2" decode-uri-component "^0.2.0" resolve-url "^0.2.1" source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" - integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -10424,7 +15578,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -10453,17 +15607,27 @@ source-map@~0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -spawn-wrap@^1.4.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.3.tgz#81b7670e170cca247d80bf5faf0cfb713bdcf848" - integrity sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw== +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== dependencies: - foreground-child "^1.5.6" - mkdirp "^0.5.0" - os-homedir "^1.0.1" - rimraf "^2.6.2" + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" signal-exit "^3.0.2" - which "^1.3.0" + which "^2.0.1" spdx-correct@^3.0.0: version "3.1.0" @@ -10491,6 +15655,29 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -10546,17 +15733,40 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +ssri@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" + integrity sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g== + dependencies: + figgy-pudding "^3.5.1" + minipass "^3.1.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + staged-git-files@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.2.tgz#4326d33886dc9ecfa29a6193bf511ba90a46454b" integrity sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA== +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + static-eval@^2.0.0, static-eval@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" - integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg== + version "2.0.5" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.5.tgz#f0782e66999c4b3651cda99d9ce59c507d188f71" + integrity sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA== dependencies: - escodegen "^1.8.1" + escodegen "^1.11.1" static-extend@^0.1.1: version "0.1.2" @@ -10611,6 +15821,18 @@ static-module@^3.0.2: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +std-env@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" + integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== + dependencies: + ci-info "^1.6.0" + +stickyfill@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stickyfill/-/stickyfill-1.1.1.tgz#39413fee9d025c74a7e59ceecb23784cc0f17f02" + integrity sha1-OUE/7p0CXHSn5ZzuyyN4TMDxfwI= + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -10646,11 +15868,11 @@ stream-http@^2.7.2: xtend "^4.0.0" stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -streamroller@^2.1.0: +streamroller@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.3.tgz#b95c9fad44e2e89005d242141486b3b4962c2d28" integrity sha512-AegmvQsscTRhHVO46PhCDerjIpxi7E+d2GxgUDu+nzw/HuLnUdxHWr6WQ+mVn/4iJgMKKFFdiUwFcFRDvcjCtw== @@ -10669,6 +15891,11 @@ string-argv@^0.0.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736" integrity sha1-2sMECGkMIfPDYwo/86BYd73L1zY= +string-argv@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -10695,7 +15922,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0: +string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== @@ -10705,29 +15932,46 @@ string-width@^4.1.0: strip-ansi "^6.0.0" string.prototype.padend@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" - integrity sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA= + version "3.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz#dc08f57a8010dc5c153550318f67e13adbb72ac3" + integrity sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA== dependencies: - define-properties "^1.1.2" - es-abstract "^1.4.3" - function-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" -string.prototype.trimleft@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" - integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== +string.prototype.trimend@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz#ee497fd29768646d84be2c9b819e292439614373" + integrity sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" -string.prototype.trimright@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" - integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== +string.prototype.trimleft@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" + +string.prototype.trimright@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" + +string.prototype.trimstart@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz#afe596a7ce9de905496919406c9734845f01a2f2" + integrity sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" @@ -10743,7 +15987,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.2.2: +stringify-object@^3.2.2, stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -10752,6 +15996,13 @@ stringify-object@^3.2.2: is-obj "^1.0.1" is-regexp "^1.0.0" +strip-ansi@6.0.0, strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -10773,12 +16024,10 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= strip-bom@^2.0.0: version "2.0.0" @@ -10792,6 +16041,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" @@ -10804,6 +16058,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-hex-prefix@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" @@ -10823,15 +16082,22 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= -strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@2.0.1, strip-json-comments@^2.0.0, strip-json-comments@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== strong-log-transformer@^2.0.0: version "2.1.0" @@ -10842,6 +16108,45 @@ strong-log-transformer@^2.0.0: minimist "^1.2.0" through "^2.3.4" +style-to-object@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + +style-to-object@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.2.3.tgz#afcf42bc03846b1e311880c55632a26ad2780bcb" + integrity sha512-1d/k4EY2N7jVLOqf2j04dTc37TPOv/hHxZmvpg8Pdh8UYydxeu/C1W1U4vD8alzf5V2Gt7rLsmkr4dxAlDm9ng== + dependencies: + inline-style-parser "0.1.1" + +styled-components@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.0.1.tgz#57782a6471031abefb2db5820a1876ae853bc619" + integrity sha512-E0xKTRIjTs4DyvC1MHu/EcCXIj6+ENCP8hP01koyoADF++WdBUOrSGwU1scJRw7/YaYOhDvvoad6VlMG+0j53A== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/traverse" "^7.4.5" + "@emotion/is-prop-valid" "^0.8.3" + "@emotion/stylis" "^0.8.4" + "@emotion/unitless" "^0.7.4" + babel-plugin-styled-components ">= 1" + css-to-react-native "^3.0.0" + hoist-non-react-statics "^3.0.0" + shallowequal "^1.1.0" + supports-color "^5.5.0" + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + superagent@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" @@ -10880,6 +16185,13 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + supports-color@6.1.0, supports-color@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" @@ -10906,18 +16218,61 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + surrial@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/surrial/-/surrial-1.0.0.tgz#3ac560cc7038a8ff446920a4f7c3495e1f03a578" integrity sha512-dkvhz3QvgraMeFWI9V+BinpNCNoaSNxKcxb0umRpkWeFlZ0WSbIfeTm9YtLA6a4kv/Q2pOMQOtMlcv/b5h6qpg== -svg-to-pdfkit@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/svg-to-pdfkit/-/svg-to-pdfkit-0.1.7.tgz#7db6d47e47b3888dce18060751a8de2417f85775" - integrity sha1-fbbUfkeziI3OGAYHUajeJBf4V3U= +svg-to-pdfkit@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/svg-to-pdfkit/-/svg-to-pdfkit-0.1.8.tgz#5921765922044843f0c1a5b25ec1ef8a4a33b8af" + integrity sha512-QItiGZBy5TstGy+q8mjQTMGRlDDOARXLxH+sgVm1n/LYeo0zFcQlcCh8m4zi8QxctrxB9Kue/lStc/RD5iLadQ== dependencies: pdfkit ">=0.8.1" +svgo@^1.0.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +swagger2openapi@^5.3.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-5.4.0.tgz#1e1c8909f7966b1f455bf1b66490093ac1c0029c" + integrity sha512-f5QqfXawiVijhjMtYqWZ55ESHPZFqrPC8L9idhIiuSX8O2qsa1i4MVGtCM3TQF+Smzr/6WfT/7zBuzG3aTgPAA== + dependencies: + better-ajv-errors "^0.6.1" + call-me-maybe "^1.0.1" + node-fetch-h2 "^2.3.0" + node-readfiles "^0.2.0" + oas-kit-common "^1.0.7" + oas-resolver "^2.3.0" + oas-schema-walker "^1.1.3" + oas-validator "^3.4.0" + reftools "^1.1.0" + yaml "^1.8.3" + yargs "^12.0.5" + swarm-js@0.1.39: version "0.1.39" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8" @@ -10956,7 +16311,7 @@ table@^5.0.2: slice-ansi "^2.1.0" string-width "^3.0.0" -tapable@^1.0.0, tapable@^1.1.0: +tapable@^1.0.0, tapable@^1.1.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== @@ -10974,7 +16329,7 @@ tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" -tar@^4, tar@^4.0.2, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: +tar@^4.0.2, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -11020,46 +16375,60 @@ terser-webpack-plugin@1.3.0: webpack-sources "^1.3.0" worker-farm "^1.7.0" -terser-webpack-plugin@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4" - integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg== +terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" + integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== dependencies: cacache "^12.0.2" find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^1.7.0" + serialize-javascript "^2.1.2" source-map "^0.6.1" terser "^4.1.2" webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@^4.0.0, terser@^4.1.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.4.0.tgz#22c46b4817cf4c9565434bfe6ad47336af259ac3" - integrity sha512-oDG16n2WKm27JO8h4y/w3iqBGAOSCtq7k8dRmrn4Wf9NouL0b2WpMHGChFGZq4nFAQy1FsNJrVQHfurXOSTmOA== +terser-webpack-plugin@^2.2.1: + version "2.3.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz#5ad971acce5c517440ba873ea4f09687de2f4a81" + integrity sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== + dependencies: + cacache "^13.0.1" + find-cache-dir "^3.2.0" + jest-worker "^25.1.0" + p-limit "^2.2.2" + schema-utils "^2.6.4" + serialize-javascript "^2.1.2" + source-map "^0.6.1" + terser "^4.4.3" + webpack-sources "^1.4.3" + +terser@^4.0.0, terser@^4.1.2, terser@^4.4.3, terser@^4.6.3: + version "4.6.11" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.11.tgz#12ff99fdd62a26de2a82f508515407eb6ccd8a9f" + integrity sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA== dependencies: commander "^2.20.0" source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^5.1.0, test-exclude@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: - glob "^7.1.3" + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-table@^0.2.0: +text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= @@ -11106,6 +16475,11 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" @@ -11123,11 +16497,31 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== +tiny-invariant@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" + integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== + +tiny-warning@^1.0.0, tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -11145,6 +16539,11 @@ to-buffer@^1.1.1: resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== +to-factory@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-factory/-/to-factory-1.0.0.tgz#8738af8bd97120ad1d4047972ada5563bf9479b1" + integrity sha1-hzivi9lxIK0dQEeXKtpVY7+UebE= + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -11197,13 +16596,13 @@ toposort@^2.0.2: resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: - psl "^1.1.24" - punycode "^1.4.1" + psl "^1.1.28" + punycode "^2.1.1" tr46@^1.0.1: version "1.0.1" @@ -11218,9 +16617,14 @@ traverse@^0.6.6: integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= tree-kill@^1.2.1, tree-kill@~1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a" - integrity sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q== + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + +trim-lines@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.3.tgz#839514be82428fd9e7ec89e35081afe8f6f93115" + integrity sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA== trim-newlines@^1.0.0: version "1.0.0" @@ -11232,20 +16636,39 @@ trim-newlines@^2.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -truffle@5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.3.tgz#ff54abdd3dba7ff0850356c0aae7c390d013e40b" - integrity sha512-XTR1SwWoxNGCBa8zWrv/4J8RNfclL6qhyAp+bfnv4AD3L7GEWziDQvcq5ufSKIPKnoK0a4y1O+EZOW5izyEaKg== +trim-trailing-lines@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" + integrity sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +truffle@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.1.2.tgz#516f28126fdb623bd5eff20c665b2f2bfbaa4eba" + integrity sha512-VfNdtk/dh8zj9onE/q8A7SSrFyqPqz61W+sx5FrUK++BbSE7N09/6z2F/VORuDykPmFaujtYKt8iNoD3Htfeww== dependencies: app-module-path "^2.2.0" - mocha "^4.1.0" + mocha "5.2.0" original-require "1.0.1" - solc "0.5.0" tryer@^1.0.1: version "1.0.1" @@ -11269,16 +16692,27 @@ ts-node-dev@1.0.0-pre.39: ts-node "*" tsconfig "^7.0.0" -ts-node@*, ts-node@8.5.2: - version "8.5.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.5.2.tgz#434f6c893bafe501a30b32ac94ee36809ba2adce" - integrity sha512-W1DK/a6BGoV/D4x/SXXm6TSQx6q3blECUzd5TN+j56YEMX3yPVMpHsICLedUw3DvGF3aTQ8hfdR9AKMaHjIi+A== +ts-node@*: + version "8.8.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.2.tgz#0b39e690bee39ea5111513a9d2bcdc0bc121755f" + integrity sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "3.1.1" + +ts-node@8.6.2: + version "8.6.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" + integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== dependencies: arg "^4.1.0" diff "^4.0.1" make-error "^1.1.1" source-map-support "^0.5.6" - yn "^3.0.0" + yn "3.1.1" ts-simple-ast@12.4.0: version "12.4.0" @@ -11306,16 +16740,16 @@ tsconfig@^7.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== - -tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3, tslib@~1.10.0: +tslib@1.10.0, tslib@~1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + tslint@5.12.1: version "5.12.1" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.1.tgz#8cec9d454cf8a1de9b0a26d7bdbad6de362e52c1" @@ -11370,11 +16804,16 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "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.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.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -11385,7 +16824,7 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -11408,10 +16847,12 @@ type@^2.0.0: resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== -typed-inject@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/typed-inject/-/typed-inject-2.0.0.tgz#78fc8b3e27104fb2945c3242d7eff9bc1e5e6320" - integrity sha512-x4gVaWZzGBZMR15rTMWdijKlMg/rBc7YkjTx9koW7Lp+9UYr+GQZLRMtpQm7MJv4J6r1AVhclwouZ0z+G6tYAw== +typed-inject@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/typed-inject/-/typed-inject-2.1.1.tgz#5e16c5d46961fd77f475295f0170627ac81ffd19" + integrity sha512-TaQrNsYjGTMmgfEwKtjP9+qyZu//H1RJ0RYNvvQ/rcAnpQGZLxHajb+O6TnyFZGfLaK/9319VYaG4PFXGjImug== + dependencies: + typescript "^3.6.3" typed-rest-client@~1.5.0: version "1.5.0" @@ -11433,6 +16874,41 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typedoc-default-themes@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.7.2.tgz#1e9896f920b58e6da0bba9d7e643738d02405a5a" + integrity sha512-fiFKlFO6VTqjcno8w6WpTsbCgXmfPHVjnLfYkmByZE7moaz+E2DSpAT+oHtDHv7E0BM5kAhPrHJELP2J2Y2T9A== + dependencies: + backbone "^1.4.0" + jquery "^3.4.1" + lunr "^2.3.8" + underscore "^1.9.1" + +typedoc-plugin-markdown@2.2.17: + version "2.2.17" + resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-2.2.17.tgz#aaef7420e8268170e62c764f43740e10f842548d" + integrity sha512-eE6cTeqsZIbjur6RG91Lhx1vTwjR49OHwVPRlmsxY3dthS4FNRL8sHxT5Y9pkosBwv1kSmNGQEPHjMYy1Ag6DQ== + dependencies: + fs-extra "^8.1.0" + handlebars "^4.7.3" + +typedoc@0.16.10: + version "0.16.10" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.16.10.tgz#217cd4243c9b4d85f49b94323c178f6d279bfb9c" + integrity sha512-Eo1+K+XTiqSi4lz5cPrV4RncLv6abjCd/jfL5tTueNZGO2p8x2yDIrXkxL9C+SoLjJm2xpMs3CXYmTnilxk1cA== + dependencies: + "@types/minimatch" "3.0.3" + fs-extra "^8.1.0" + handlebars "^4.7.2" + highlight.js "^9.17.1" + lodash "^4.17.15" + marked "^0.8.0" + minimatch "^3.0.0" + progress "^2.0.3" + shelljs "^0.8.3" + typedoc-default-themes "^0.7.2" + typescript "3.7.x" + typescript@2.9.1: version "2.9.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.1.tgz#fdb19d2c67a15d11995fd15640e373e09ab09961" @@ -11443,15 +16919,30 @@ typescript@3.7.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@3.7.x: + version "3.7.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" + integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== + typescript@^2.4.2: version "2.9.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== +typescript@^3.6.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" + integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== + +ua-parser-js@^0.7.18: + version "0.7.21" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + uglify-js@^3.1.4: - version "3.6.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.9.tgz#85d353edb6ddfb62a9d798f36e91792249320611" - integrity sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw== + version "3.8.1" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.8.1.tgz#43bb15ce6f545eaa0a64c49fd29375ea09fa0f93" + integrity sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw== dependencies: commander "~2.20.3" source-map "~0.6.1" @@ -11472,9 +16963,9 @@ umask@^1.1.0: integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= unbzip2-stream@^1.0.9: - version "1.3.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" - integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.0.tgz#097ca7b18b5b71e6c8bc8e514a0f1884a12d6eb1" + integrity sha512-kVx7CDAsdBSWVf404Mw7oI9i09w5/mTT/Ruk+RWa64PLYKvsAucLLFHvQtnvjeADM4ZizxrvG5SHnF4Te4T2Cg== dependencies: buffer "^5.2.1" through "^2.3.8" @@ -11494,6 +16985,44 @@ underscore@1.9.1: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== +underscore@>=1.8.3, underscore@^1.9.1: + version "1.10.2" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" + integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== + +unescape@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unescape/-/unescape-1.0.1.tgz#956e430f61cad8a4d57d82c518f5e6cc5d0dda96" + integrity sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ== + dependencies: + extend-shallow "^2.0.1" + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + unicode-properties@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/unicode-properties/-/unicode-properties-1.3.1.tgz#cc642b6314bde2c691d65dd94cece09ed84f1282" @@ -11502,6 +17031,11 @@ unicode-properties@^1.0.0: base64-js "^1.3.0" unicode-trie "^2.0.0" +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + unicode-trie@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-0.3.1.tgz#d671dddd89101a08bac37b6a5161010602052085" @@ -11526,6 +17060,17 @@ unicode-trie@^2.0.0: pako "^0.2.5" tiny-inflate "^1.0.0" +unified@8.4.2, unified@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" + integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -11536,6 +17081,16 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -11550,10 +17105,94 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.5.tgz#1e903e68467931ebfaea386dae9ea253628acd42" + integrity sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw== + +unist-util-is@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" + integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== + +unist-util-is@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.0.2.tgz#c7d1341188aa9ce5b3cff538958de9895f14a5de" + integrity sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020" + integrity sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A== + dependencies: + unist-util-visit "^1.1.0" + +unist-util-remove@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-1.0.3.tgz#58ec193dfa84b52d5a055ffbc58e5444eb8031a3" + integrity sha512-mB6nCHCQK0pQffUAcCVmKgIWzG/AXs/V8qpS8K72tMPtOSCMSjDeMc5yN+Ye8rB0FhcE+JvW++o1xRNc0R+++g== + dependencies: + unist-util-is "^3.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" + integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== + dependencies: + unist-util-is "^3.0.0" + +unist-util-visit-parents@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz#d4076af3011739c71d2ce99d05de37d545f4351d" + integrity sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.2, unist-util-visit@^2.0.0, unist-util-visit@^2.0.1, unist-util-visit@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.2.tgz#3843782a517de3d2357b4c193b24af2d9366afb7" + integrity sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +unist-util-visit@^1.0.0, unist-util-visit@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" + integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== + dependencies: + unist-util-visit-parents "^2.0.0" + universal-user-agent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.0.tgz#27da2ec87e32769619f68a14996465ea1cb9df16" - integrity sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA== + version "4.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" + integrity sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg== + dependencies: + os-name "^3.1.0" + +universal-user-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-5.0.0.tgz#a3182aa758069bf0e79952570ca757de3579c1d9" + integrity sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q== dependencies: os-name "^3.1.0" @@ -11562,7 +17201,7 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -unix-crypt-td-js@^1.0.0: +unix-crypt-td-js@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== @@ -11572,6 +17211,11 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -11611,11 +17255,24 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url-set-query@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= +url-template@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE= + url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" @@ -11656,6 +17313,24 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -11670,6 +17345,11 @@ util@^0.11.0: dependencies: inherits "2.0.3" +utila@^0.4.0, utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -11685,10 +17365,10 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== +uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache@2.0.3: version "2.0.3" @@ -11710,6 +17390,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + varint@^5.0.0, varint@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.0.tgz#d826b89f7490732fabc0c0ed693ed475dcb29ebf" @@ -11720,6 +17405,11 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +vendors@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -11729,6 +17419,30 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vfile-location@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" + integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.1.0.tgz#d79248957f43225d57ff67a56effc67bef08946e" + integrity sha512-BaTPalregj++64xbGK6uIlsurN3BCRNM/P2Pg8HezlGzKd1O9PrwIac6bd9Pdx2uTb0QHoioZ+rXKolbVXEgJg== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + viz.js@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/viz.js/-/viz.js-1.8.2.tgz#d9cc04cd99f98ec986bf9054db76a6cbcdc5d97a" @@ -11739,11 +17453,25 @@ vlq@^0.2.2: resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== +vlq@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" + integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +wait-file@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/wait-file/-/wait-file-1.0.5.tgz#377f48795f1765046a41bb0671c142ef8e509ae6" + integrity sha512-udLpJY/eOxlrMm3+XD1RLuF2oT9B7J7wiyR5/9xrvQymS6YR6trWvVhzOldHrVbLwyiRmLj9fcvsjzpSXeZHkw== + dependencies: + "@hapi/joi" "^15.1.0" + fs-extra "^8.1.0" + rx "^4.1.0" + wallet-address-validator@0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/wallet-address-validator/-/wallet-address-validator-0.2.4.tgz#717ce316143b679645639986cc0847a107255881" @@ -11752,22 +17480,34 @@ wallet-address-validator@0.2.4: base-x "^3.0.4" jssha "2.3.1" -watchpack@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== +watchpack@^1.5.0, watchpack@^1.6.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2" + integrity sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA== dependencies: - chokidar "^2.0.2" + chokidar "^2.1.8" graceful-fs "^4.1.2" neo-async "^2.5.0" -wcwidth@^1.0.0: +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" +web-namespaces@^1.0.0, web-namespaces@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + web3-bzz@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.1.tgz#c3bd1e8f0c02a13cd6d4e3c3e9e1713f144f6f0d" @@ -11786,6 +17526,15 @@ web3-core-helpers@1.0.0-beta.37: web3-eth-iban "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-core-helpers@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.0.tgz#023947323cebd021e43a67145a5087627ce87fb3" + integrity sha512-KLCCP2FS1cMz23Y9l3ZaEDzaUky+GpsNavl4Hn1xX8lNaKcfgGEF+DgtAY/TfPQYAxLjLrSbIFUDzo9H/W1WAQ== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.0" + web3-utils "1.2.0" + web3-core-helpers@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz#f5f32d71c60a4a3bd14786118e633ce7ca6d5d0d" @@ -11806,6 +17555,17 @@ web3-core-method@1.0.0-beta.37: web3-core-subscriptions "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-core-method@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.0.tgz#9f6a6939d15f53bc74d086f280fbd62461546cd3" + integrity sha512-Iff5rCL+sgHe6zZVZijp818aRixKQf3ZAyQsT6ewER1r9yqXsH89DJtX33Xw8xiaYSwUFcpNs2j+Kluhv/eVAw== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.0" + web3-core-promievent "1.2.0" + web3-core-subscriptions "1.2.0" + web3-utils "1.2.0" + web3-core-method@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.1.tgz#9df1bafa2cd8be9d9937e01c6a47fc768d15d90a" @@ -11825,6 +17585,14 @@ web3-core-promievent@1.0.0-beta.37: any-promise "1.3.0" eventemitter3 "1.1.1" +web3-core-promievent@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.0.tgz#d6454837a307da5b453fe3077743fe25801a07a1" + integrity sha512-9THNYsZka91AX4LZGZvka5hio9+QlOY22hNgCiagmCmYytyKk3cXftL6CWefnNF7XgW8sy/ew5lzWLVsQW61Lw== + dependencies: + any-promise "1.3.0" + eventemitter3 "3.1.2" + web3-core-promievent@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz#003e8a3eb82fb27b6164a6d5b9cad04acf733838" @@ -11844,6 +17612,17 @@ web3-core-requestmanager@1.0.0-beta.37: web3-providers-ipc "1.0.0-beta.37" web3-providers-ws "1.0.0-beta.37" +web3-core-requestmanager@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.0.tgz#a7f9995495340037e7ac72792c1885c35c1e7616" + integrity sha512-hPe1jyESodXAiE7qJglu7ySo4GINCn5CgG+9G1ATLQbriZsir83QMSeKQekv/hckKFIf4SvZJRPEBhtAle+Dhw== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.0" + web3-providers-http "1.2.0" + web3-providers-ipc "1.2.0" + web3-providers-ws "1.2.0" + web3-core-requestmanager@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz#fa2e2206c3d738db38db7c8fe9c107006f5c6e3d" @@ -11864,6 +17643,15 @@ web3-core-subscriptions@1.0.0-beta.37: underscore "1.8.3" web3-core-helpers "1.0.0-beta.37" +web3-core-subscriptions@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.0.tgz#d359b9b5fb6f6a700f1b383be11de7925cb7549f" + integrity sha512-DHipGH8It5E4HxxvymhkudcYhBVgGx6MwGWobIVKFgp6JRxtuvAbqwrMbuD/+78J6yXOa4y9zVXBk12dm2NXGg== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.0" + web3-core-subscriptions@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz#8c2368a839d4eec1c01a4b5650bbeb82d0e4a099" @@ -11883,6 +17671,16 @@ web3-core@1.0.0-beta.37: web3-core-requestmanager "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-core@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.0.tgz#6f3c59f84b2af9ab0ee7617d3c5208a814d3953c" + integrity sha512-Vy+fargzx94COdihE79zIM5lb/XAl/LJlgGdmz2a6QhgGZrSL8K6DKKNS+OuORAcLJN2PWNMc4IdfknkOw1PhQ== + dependencies: + web3-core-helpers "1.2.0" + web3-core-method "1.2.0" + web3-core-requestmanager "1.2.0" + web3-utils "1.2.0" + web3-core@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.1.tgz#7278b58fb6495065e73a77efbbce781a7fddf1a9" @@ -11902,6 +17700,15 @@ web3-eth-abi@1.0.0-beta.37: underscore "1.8.3" web3-utils "1.0.0-beta.37" +web3-eth-abi@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.0.tgz#26b22261756ffbb3363bc37c1a6f5143bebb6469" + integrity sha512-FDuPq/tFeMg/D/f7cNSmvVYkMYb1z379gUUzSL8ZFtZrdHPkezq+lq/TmWmbCOMLYNXlhGJBzjGdLXRS4Upprg== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.0" + web3-eth-abi@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz#9b915b1c9ebf82f70cca631147035d5419064689" @@ -11927,6 +17734,22 @@ web3-eth-accounts@1.0.0-beta.37: web3-core-method "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-eth-accounts@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.0.tgz#bb26d5446017700a13b75fc69a2b1226fe44f6bb" + integrity sha512-d/fUAL3F6HqstvEiBnZ1RwZ77/DytgF9d6A3mWVvPOUk2Pqi77PM0adRvsKvIWUaQ/k6OoCk/oXtbzaO7CyGpg== + dependencies: + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + scrypt.js "^0.3.0" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.0" + web3-core-helpers "1.2.0" + web3-core-method "1.2.0" + web3-utils "1.2.0" + web3-eth-accounts@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz#2741a8ef337a7219d57959ac8bd118b9d68d63cf" @@ -11958,6 +17781,20 @@ web3-eth-contract@1.0.0-beta.37: web3-eth-abi "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-eth-contract@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.0.tgz#8d1c235c6624b5df428969ea2e9c26337095f6f0" + integrity sha512-hfjozNbfsoMeR3QklfkwU0Mqcw6YRD4y1Cb1ghGWNhFy2+/sbvKcQNPPJDKFTde22PRzGQBWyh/nb422Sux4bQ== + dependencies: + underscore "1.9.1" + web3-core "1.2.0" + web3-core-helpers "1.2.0" + web3-core-method "1.2.0" + web3-core-promievent "1.2.0" + web3-core-subscriptions "1.2.0" + web3-eth-abi "1.2.0" + web3-utils "1.2.0" + web3-eth-contract@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz#3542424f3d341386fd9ff65e78060b85ac0ea8c4" @@ -11986,6 +17823,20 @@ web3-eth-ens@1.0.0-beta.37: web3-eth-contract "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-eth-ens@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.0.tgz#af66308542f4acfa09ccd3ce370e3ad2de20ec30" + integrity sha512-kE6uHMLwH9dv+MZSKT7BcKXcQ6CcLP5m5mM44s2zg2e9Rl20F3J6R3Ik6sLc/w2ywdCwTe/Z22yEstHXQwu5ig== + dependencies: + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.0" + web3-core-helpers "1.2.0" + web3-core-promievent "1.2.0" + web3-eth-abi "1.2.0" + web3-eth-contract "1.2.0" + web3-utils "1.2.0" + web3-eth-ens@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz#a0e52eee68c42a8b9865ceb04e5fb022c2d971d5" @@ -12008,6 +17859,14 @@ web3-eth-iban@1.0.0-beta.37: bn.js "4.11.6" web3-utils "1.0.0-beta.37" +web3-eth-iban@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.0.tgz#1bece9cebf817dca82fa03230203351f4f263866" + integrity sha512-6DzTx/cvIgEvxadhJjLGpsuDUARA4RKskNOuwWYUsUODcPb50rsfMmRkHhGtLss/sNXVE5gNjbT9rX3TDqy2tg== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.0" + web3-eth-iban@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz#2c3801718946bea24e9296993a975c80b5acf880" @@ -12027,6 +17886,17 @@ web3-eth-personal@1.0.0-beta.37: web3-net "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-eth-personal@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.0.tgz#7194f519c870d720eee1349d867408004f0f78af" + integrity sha512-8QdcaT6dbdiMC8zEqvDuij8XeI34r2GGdQYGvYBP2UgCm15EZBHgewxO30A+O+j2oIW1/Hu60zP5upnhCuA1Dw== + dependencies: + web3-core "1.2.0" + web3-core-helpers "1.2.0" + web3-core-method "1.2.0" + web3-net "1.2.0" + web3-utils "1.2.0" + web3-eth-personal@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz#244e9911b7b482dc17c02f23a061a627c6e47faf" @@ -12057,6 +17927,25 @@ web3-eth@1.0.0-beta.37: web3-net "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-eth@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.0.tgz#ac8d3409356538d2fe1cb6151036b724eace76f6" + integrity sha512-GP1+hHS/IVW1tAOIDS44PxCpvSl9PBU/KAB40WgP27UMvSy43LjHxGlP6hQQOdIfmBLBTvGvn2n+Z5kW2gzAzg== + dependencies: + underscore "1.9.1" + web3-core "1.2.0" + web3-core-helpers "1.2.0" + web3-core-method "1.2.0" + web3-core-subscriptions "1.2.0" + web3-eth-abi "1.2.0" + web3-eth-accounts "1.2.0" + web3-eth-contract "1.2.0" + web3-eth-ens "1.2.0" + web3-eth-iban "1.2.0" + web3-eth-personal "1.2.0" + web3-net "1.2.0" + web3-utils "1.2.0" + web3-eth@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.1.tgz#b9989e2557c73a9e8ffdc107c6dafbe72c79c1b0" @@ -12085,6 +17974,15 @@ web3-net@1.0.0-beta.37: web3-core-method "1.0.0-beta.37" web3-utils "1.0.0-beta.37" +web3-net@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.0.tgz#9e99c4326a28712451dc4d45f3acf26c1d4b3219" + integrity sha512-7iD8C6vvx8APXPMmlpPLGWjn4bsXHzd3BTdFzKjkoYjiiVFJdVAbY3j1BwN/6tVQu8Ay7sDpV2EdTNub7GKbyw== + dependencies: + web3-core "1.2.0" + web3-core-method "1.2.0" + web3-utils "1.2.0" + web3-net@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.1.tgz#edd249503315dd5ab4fa00220f6509d95bb7ab10" @@ -12102,6 +18000,14 @@ web3-providers-http@1.0.0-beta.37: web3-core-helpers "1.0.0-beta.37" xhr2-cookies "1.1.0" +web3-providers-http@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.0.tgz#c6ebf9b6a23564439fa3c4a431cd6b405cc1ec0f" + integrity sha512-UrUn6JSz7NVCZ+0nZZtC4cmbl5JIi57w1flL1jN8jgkfdWDdErNvTkSwCt/QYdTQscMaUtWXDDOSAsVO6YC64g== + dependencies: + web3-core-helpers "1.2.0" + xhr2-cookies "1.1.0" + web3-providers-http@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.1.tgz#c93ea003a42e7b894556f7e19dd3540f947f5013" @@ -12119,6 +18025,15 @@ web3-providers-ipc@1.0.0-beta.37: underscore "1.8.3" web3-core-helpers "1.0.0-beta.37" +web3-providers-ipc@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.0.tgz#98b8b8c9e77935dabfcf6d16e66c783f2429eac8" + integrity sha512-T2OSbiqu7+dahbGG5YFEQM5+FXdLVvaTCKmHXaQpw8IuL5hw7HELtyFOtHVudgDRyw0tJKxIfAiX/v2F1IL1fQ== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.0" + web3-providers-ipc@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz#017bfc687a8fc5398df2241eb98f135e3edd672c" @@ -12137,6 +18052,15 @@ web3-providers-ws@1.0.0-beta.37: web3-core-helpers "1.0.0-beta.37" websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" +web3-providers-ws@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.0.tgz#c45929f0d1e1743301372e6e604aab63e83f66e3" + integrity sha512-rnwGcCe6cev5A6eG5UBCQqPmkJVZMCrK+HN1AvUCco0OHD/0asGc9LuLbtkQIyznA6Lzetq/OOcaTOM4KeT11g== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.0" + websocket "github:frozeman/WebSocket-Node#browserifyCompatible" + web3-providers-ws@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz#2d941eaf3d5a8caa3214eff8dc16d96252b842cb" @@ -12169,6 +18093,19 @@ web3-utils@1.0.0-beta.37: underscore "1.8.3" utf8 "2.1.1" +web3-utils@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.0.tgz#1f11b05d173b757d3f5ba32cb90b375a487d3bf0" + integrity sha512-tI1low8ICoaWU2c53cikH0rsksKuIskI2nycH5E5sEXxxl9/BOD3CeDDBFbxgNPQ+bpDevbR7gXNEDB7Ud4G9g== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randomhex "0.1.5" + underscore "1.9.1" + utf8 "3.0.0" + web3-utils@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.1.tgz#21466e38291551de0ab34558de21512ac4274534" @@ -12200,10 +18137,10 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-bundle-analyzer@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.3.2.tgz#3da733a900f515914e729fcebcd4c40dde71fc6f" - integrity sha512-7qvJLPKB4rRWZGjVp5U1KEjwutbDHSKboAl0IfafnrdXMrgC0tOtZbQD6Rw0u4cmpgRN4O02Fc0t8eAT+FgGzA== +webpack-bundle-analyzer@3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz#39b3a8f829ca044682bc6f9e011c95deb554aefd" + integrity sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g== dependencies: acorn "^6.0.7" acorn-walk "^6.1.1" @@ -12214,15 +18151,34 @@ webpack-bundle-analyzer@3.3.2: express "^4.16.3" filesize "^3.6.1" gzip-size "^5.0.0" - lodash "^4.17.10" + lodash "^4.17.15" + mkdirp "^0.5.1" + opener "^1.5.1" + ws "^6.0.0" + +webpack-bundle-analyzer@^3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.1.tgz#bdb637c2304424f2fbff9a950c7be42a839ae73b" + integrity sha512-Nfd8HDwfSx1xBwC+P8QMGvHAOITxNBSvu/J/mCJvOwv+G4VWkU7zir9SSenTtyCi0LnVtmsc7G5SZo1uV+bxRw== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + bfj "^6.1.1" + chalk "^2.4.1" + commander "^2.18.0" + ejs "^2.6.1" + express "^4.16.3" + filesize "^3.6.1" + gzip-size "^5.0.0" + lodash "^4.17.15" mkdirp "^0.5.1" opener "^1.5.1" ws "^6.0.0" -webpack-cli@3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.6.tgz#2c8c399a2642133f8d736a359007a052e060032c" - integrity sha512-0vEa83M7kJtxK/jUhlpZ27WHIOndz5mghWL2O53kiDoA9DIxSKnfqB92LoqEn77cT4f3H2cZm1BMEat/6AZz3A== +webpack-cli@3.3.10: + version "3.3.10" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13" + integrity sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg== dependencies: chalk "2.4.2" cross-spawn "6.0.5" @@ -12236,6 +18192,56 @@ webpack-cli@3.3.6: v8-compile-cache "2.0.3" yargs "13.2.4" +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.9.0: + version "3.10.3" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz#f35945036813e57ef582c2420ef7b470e14d3af0" + integrity sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.2.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.6" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.25" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.19" + sockjs-client "1.4.0" + spdy "^4.0.1" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "12.0.5" + webpack-log@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" @@ -12246,7 +18252,22 @@ webpack-log@^1.2.0: loglevelnext "^1.0.1" uuid "^3.1.0" -webpack-sources@^1.3.0, webpack-sources@^1.4.0: +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -12283,6 +18304,49 @@ webpack@4.38.0: watchpack "^1.5.0" webpack-sources "^1.3.0" +webpack@^4.41.2: + version "4.42.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.42.1.tgz#ae707baf091f5ca3ef9c38b884287cfe8f1983ef" + integrity sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + +webpackbar@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-4.0.0.tgz#ee7a87f16077505b5720551af413c8ecd5b1f780" + integrity sha512-k1qRoSL/3BVuINzngj09nIwreD8wxV4grcuhHTD8VJgUbGcy8lQSPqv+bM00B7F+PffwIsQ8ISd4mIwRbr23eQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + consola "^2.10.0" + figures "^3.0.0" + pretty-time "^1.1.0" + std-env "^2.2.1" + text-table "^0.2.0" + wrap-ansi "^6.0.0" + websocket-driver@>=0.5.1: version "0.7.3" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" @@ -12293,16 +18357,17 @@ websocket-driver@>=0.5.1: websocket-extensions ">=0.1.1" websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" - integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== websocket@^1.0.28: - version "1.0.30" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.30.tgz#91d3bd00c3d43e916f0cf962f8f8c451bb0b2373" - integrity sha512-aO6klgaTdSMkhfl5VVJzD5fm+Srhh5jLYbS15+OiI1sN6h/RU/XW6WN9J1uVIpUKNmsTvT3Hs35XAFjn9NMfOw== + version "1.0.31" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.31.tgz#e5d0f16c3340ed87670e489ecae6144c79358730" + integrity sha512-VAouplvGKPiKFDTeCCO65vYHsyay8DqoBSlzIO3fayrfOgU94lQN5a1uWVnFrMLceTJw/+fQXR5PGbUVRaHshQ== dependencies: debug "^2.2.0" + es5-ext "^0.10.50" nan "^2.14.0" typedarray-to-buffer "^3.1.5" yaeti "^0.0.6" @@ -12316,6 +18381,16 @@ websocket@^1.0.28: typedarray-to-buffer "^3.1.2" yaeti "^0.0.6" +"websocket@github:frozeman/WebSocket-Node#browserifyCompatible": + version "1.0.26" + uid "6c72925e3f8aaaea8dc8450f97627e85263999f2" + resolved "https://codeload.github.com/frozeman/WebSocket-Node/tar.gz/6c72925e3f8aaaea8dc8450f97627e85263999f2" + dependencies: + debug "^2.2.0" + nan "^2.3.3" + typedarray-to-buffer "^3.1.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/905deb4812572b344f5801f8c9ce8bb02799d82e" @@ -12326,6 +18401,11 @@ websocket@^1.0.28: typedarray-to-buffer "^3.1.5" yaeti "^0.0.6" +whatwg-fetch@>=0.10.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== + whatwg-url@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" @@ -12349,14 +18429,21 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@^1.1.1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1.3.1, which@^1.1.1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" -wide-align@^1.1.0: +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3, wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== @@ -12364,9 +18451,9 @@ wide-align@^1.1.0: string-width "^1.0.2 || 2" windows-release@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" - integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.0.tgz#dce167e9f8be733f21c849ebd4d03fe66b29b9f0" + integrity sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ== dependencies: execa "^1.0.0" @@ -12380,11 +18467,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= - worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -12392,6 +18474,13 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -12417,6 +18506,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.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== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -12431,6 +18529,16 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + write-json-file@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f" @@ -12479,7 +18587,7 @@ ws@^3.0.0: safe-buffer "~5.1.0" ultron "~1.1.0" -ws@^6.0.0: +ws@^6.0.0, ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== @@ -12523,12 +18631,24 @@ xhr@^2.0.4, xhr@^2.3.3: parse-headers "^2.0.0" xtend "^4.0.0" +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + +xmlbuilder@^13.0.0: + version "13.0.2" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7" + integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ== + xmlhttprequest@1.8.0: version "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.2, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -12558,6 +18678,26 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" + integrity sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw== + dependencies: + "@babel/runtime" "^7.8.7" + +yargs-parser@13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -12573,10 +18713,18 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^13.0.0, yargs-parser@^13.1.0, yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== +yargs-parser@^13.1.0, yargs-parser@^13.1.1, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^18.1.1: + version "18.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1" + integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -12588,30 +18736,14 @@ yargs-parser@^8.1.0: dependencies: camelcase "^4.1.0" -yargs-parser@^9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" - integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= - dependencies: - camelcase "^4.1.0" - -yargs@11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" - integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A== +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" yargs@12.0.5, yargs@^12.0.1, yargs@^12.0.5: version "12.0.5" @@ -12648,6 +18780,22 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" +yargs@13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + yargs@14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.0.0.tgz#ba4cacc802b3c0b3e36a9e791723763d57a85066" @@ -12683,39 +18831,38 @@ yargs@^10.0.3: y18n "^3.2.1" yargs-parser "^8.1.0" -yargs@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" - integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== +yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^3.1.0" + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" require-directory "^2.1.1" - require-main-filename "^1.0.1" + require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^2.0.0" + string-width "^3.0.0" which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" + y18n "^4.0.0" + yargs-parser "^13.1.2" -yargs@^13.2.2: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== +yargs@^15.0.2, yargs@^15.3.1: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== dependencies: - cliui "^5.0.0" - find-up "^3.0.0" + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" get-caller-file "^2.0.1" require-directory "^2.1.1" require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^3.0.0" + string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^18.1.1" yauzl@^2.4.2: version "2.10.0" @@ -12725,7 +18872,7 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -yn@^3.0.0: +yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== @@ -12741,3 +18888,13 @@ yup@^0.26.10: property-expr "^1.5.0" synchronous-promise "^2.0.5" toposort "^2.0.2" + +zepto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/zepto/-/zepto-1.2.0.tgz#e127bd9e66fd846be5eab48c1394882f7c0e4f98" + integrity sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g= + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==