From 9844c489516cd7598ee78e2770d7d90606f402af Mon Sep 17 00:00:00 2001 From: rkv Date: Wed, 14 Sep 2022 11:38:02 +0000 Subject: [PATCH] Merge branch 'develop' into test/playground-migration --- --- /dev/null +++ b/.github/workflows/canary.yml @@ -0,0 +1,12 @@ +on: + workflow_call: + +jobs: + + market-e2e-test: + name: market e2e tests + uses: ./.github/workflows/market-test_v2.yml + secrets: inherit + + + --- /dev/null +++ b/.github/workflows/ci-develop.yml @@ -0,0 +1,34 @@ +name: develop + +on: + pull_request: + branches: [ 'develop' ] + types: [ opened, reopened, synchronize, ready_for_review ] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + + yarn-test-dev: + uses: ./.github/workflows/dev-build-tests_v2.yml + + unit-test: + uses: ./.github/workflows/unit-test_v2.yml + + canary: + if: ${{ contains( github.event.pull_request.labels.*.name, 'canary') }} + uses: ./.github/workflows/canary.yml + secrets: inherit # pass all secrets + + xcm: + if: ${{ contains( github.event.pull_request.labels.*.name, 'xcm') }} + uses: ./.github/workflows/xcm.yml + secrets: inherit # pass all secrets + + codestyle: + uses: ./.github/workflows/codestyle_v2.yml + + yarn_eslint: + uses: ./.github/workflows/test_codestyle_v2.yml --- /dev/null +++ b/.github/workflows/ci-master.yml @@ -0,0 +1,32 @@ +name: master + +on: + pull_request: + branches: [ 'master' ] + types: [ opened, reopened, synchronize, ready_for_review ] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + + unit-test: + uses: ./.github/workflows/unit-test_v2.yml + + node-only-update: + uses: ./.github/workflows/node-only-update_v2.yml + + forkless: + uses: ./.github/workflows/forkless.yml + + canary: + uses: ./.github/workflows/canary.yml + secrets: inherit # pass all secrets + + xcm: + uses: ./.github/workflows/xcm.yml + secrets: inherit # pass all secrets + + codestyle: + uses: ./.github/workflows/codestyle_v2.yml \ No newline at end of file --- a/.github/workflows/codestyle.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: cargo fmt - -on: - pull_request: - branches: - - develop - types: - - opened - - reopened - - synchronize - - ready_for_review - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -jobs: - rustfmt: - runs-on: self-hosted-ci - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - uses: actions/checkout@v3 - - name: Install latest nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - default: true - target: wasm32-unknown-unknown - components: rustfmt, clippy - - name: Run cargo fmt - run: cargo fmt -- --check # In that mode it returns only exit code. - - name: Cargo fmt state - if: success() - run: echo "Nothing to do. Command 'cargo fmt -- --check' returned exit code 0." - - - clippy: - if: ${{ false }} - runs-on: self-hosted-ci - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - uses: actions/checkout@v3 - - name: Install substrate dependencies - run: sudo apt-get install libssl-dev pkg-config libclang-dev clang - - name: Install latest nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - default: true - target: wasm32-unknown-unknown - components: rustfmt, clippy - - name: Run cargo check - run: cargo clippy -- -Dwarnings --- /dev/null +++ b/.github/workflows/codestyle_v2.yml @@ -0,0 +1,43 @@ +name: cargo fmt + +on: + workflow_call: + +jobs: + rustfmt: + runs-on: self-hosted-ci + + steps: + - uses: actions/checkout@v3 + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + target: wasm32-unknown-unknown + components: rustfmt, clippy + - name: Run cargo fmt + run: cargo fmt -- --check # In that mode it returns only exit code. + - name: Cargo fmt state + if: success() + run: echo "Nothing to do. Command 'cargo fmt -- --check' returned exit code 0." + + + clippy: + if: ${{ false }} + runs-on: self-hosted-ci + + steps: + + - uses: actions/checkout@v3 + - name: Install substrate dependencies + run: sudo apt-get install libssl-dev pkg-config libclang-dev clang + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + target: wasm32-unknown-unknown + components: rustfmt, clippy + - name: Run cargo check + run: cargo clippy -- -Dwarnings --- a/.github/workflows/dev-build-tests.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: yarn test dev - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - pull_request: - branches: - - develop - types: - - opened - - reopened - - synchronize #commit(s) pushed to the pull request - - ready_for_review - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - dev_build_int_tests: - # The type of runner that the job will run on - runs-on: [self-hosted-ci,medium] - timeout-minutes: 1380 - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: - - network: "opal" - features: "opal-runtime" - - network: "quartz" - features: "quartz-runtime" - - network: "unique" - features: "unique-runtime" - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.tmp-dev.j2 - output_file: .docker/docker-compose.${{ matrix.network }}.yml - variables: | - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - FEATURE=${{ matrix.features }} - - - - name: Show build configuration - run: cat .docker/docker-compose.${{ matrix.network }}.yml - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --remove-orphans - - - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Run tests - run: | - cd tests - yarn install - yarn add mochawesome - echo "Ready to start tests" - node scripts/readyness.js - NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} - env: - RPC_URL: http://127.0.0.1:9933/ - - - name: Test Report - uses: phoenix-actions/test-reporting@v8 - id: test-report - if: success() || failure() # run this step even if previous step failed - with: - name: int test results - ${{ matrix.network }} # Name of the check run which will be created - path: tests/mochawesome-report/test-*.json # Path to test results - reporter: mochawesome-json - fail-on-error: 'false' - - - name: Read output variables - run: | - echo "url is ${{ steps.test-report.outputs.runHtmlUrl }}" - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --- /dev/null +++ b/.github/workflows/dev-build-tests_v2.yml @@ -0,0 +1,97 @@ +name: yarn test dev + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + workflow_call: + + +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + dev_build_int_tests: + # The type of runner that the job will run on + runs-on: [self-hosted-ci,medium] + timeout-minutes: 1380 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: + - network: "opal" + features: "opal-runtime" + - network: "quartz" + features: "quartz-runtime" + - network: "unique" + features: "unique-runtime" + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-dev.j2 + output_file: .docker/docker-compose.${{ matrix.network }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + FEATURE=${{ matrix.features }} + + + - name: Show build configuration + run: cat .docker/docker-compose.${{ matrix.network }}.yml + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --remove-orphans + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Run tests + working-directory: tests + run: | + yarn install + yarn add mochawesome + echo "Ready to start tests" + node scripts/readyness.js + yarn polkadot-types + NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} + env: + RPC_URL: http://127.0.0.1:9933/ + + - name: Test Report + uses: phoenix-actions/test-reporting@v8 + id: test-report + if: success() || failure() # run this step even if previous step failed + with: + name: int test results - ${{ matrix.network }} # Name of the check run which will be created + path: tests/mochawesome-report/test-*.json # Path to test results + reporter: mochawesome-json + fail-on-error: 'false' + + - name: Read output variables + run: | + echo "url is ${{ steps.test-report.outputs.runHtmlUrl }}" + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --- /dev/null +++ b/.github/workflows/execution-matrix.yml @@ -0,0 +1,42 @@ +name: Reusable workflow + +on: + workflow_call: + # Map the workflow outputs to job outputs + outputs: + matrix: + description: "The first output string" + value: ${{ jobs.create-matrix.outputs.matrix_output }} + +jobs: + + create-marix: + + name: Prepare execution matrix + + runs-on: self-hosted-ci + outputs: + matrix_output: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: CertainLach/create-matrix-action@v3 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.OPAL_REPLICA_FROM }}} + network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.QUARTZ_REPLICA_FROM }}} + network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}}, replica_from_address {${{ env.UNIQUE_REPLICA_FROM }}} + --- a/.github/workflows/forkless-update-data.yml +++ /dev/null @@ -1,204 +0,0 @@ -name: upgrade replica - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - pull_request: - branches: - - master - types: - - opened - - reopened - - synchronize #commit(s) pushed to the pull request - - ready_for_review - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - prepare-execution-marix: - - name: Prepare execution matrix - - runs-on: self-hosted-ci - outputs: - matrix: ${{ steps.create_matrix.outputs.matrix }} - - steps: - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Create Execution matrix - uses: CertainLach/create-matrix-action@v3 - id: create_matrix - with: - matrix: | - network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.OPAL_REPLICA_FROM }}} - network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.QUARTZ_REPLICA_FROM }}} - network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}}, replica_from_address {${{ env.UNIQUE_REPLICA_FROM }}} - - - - forkless-update-data: - needs: prepare-execution-marix - # The type of runner that the job will run on - runs-on: [self-hosted-ci,large] - timeout-minutes: 1380 - - - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} - - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.tmp-forkless-data.j2 - output_file: .docker/docker-compose.${{ matrix.network }}.yml - variables: | - REPO_URL=${{ github.server_url }}/${{ github.repository }}.git - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} - POLKADOT_MAINNET_BRANCH=${{ env.POLKADOT_MAINNET_BRANCH }} - MAINNET_TAG=${{ matrix.mainnet_tag }} - MAINNET_BRANCH=${{ matrix.mainnet_branch }} - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - BRANCH=${{ github.head_ref }} - REPLICA_FROM=${{ matrix.replica_from_address }} - - - name: Show build configuration - run: cat .docker/docker-compose.${{ matrix.network }}.yml - - - name: Generate launch-config-forkless-data.json - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/forkless-config/launch-config-forkless-data.j2 - output_file: .docker/launch-config-forkless-data.json - variables: | - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - - - name: Show launch-config-forkless configuration - run: cat .docker/launch-config-forkless-data.json - - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --force-recreate --timeout 300 - - - name: Check if docker logs consist logs related to Runtime Upgrade testing. - if: success() - run: | - counter=160 - function check_container_status { - docker inspect -f {{.State.Running}} node-parachain - } - function do_docker_logs { - docker logs --details node-parachain 2>&1 - } - function is_started { - if [ "$(check_container_status)" == "true" ]; then - echo "Container: node-parachain RUNNING"; - echo "Check Docker logs" - DOCKER_LOGS=$(do_docker_logs) - if [[ ${DOCKER_LOGS} = *"🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸"* ]];then - echo "🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸" - return 0 - elif [[ ${DOCKER_LOGS} = *"🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧"* ]];then - echo "🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧" - return 1 - else - echo "Message not found in logs output, repeating..." - return 1 - fi - else - echo "Container node-parachain not RUNNING" - echo "Halting all future checks" - exit 1 - fi - exit 0 - } - while ! is_started; do - echo "Waiting for special message in log files " - sleep 30s - counter=$(( $counter - 1 )) - echo "Counter: $counter" - if [ "$counter" -gt "0" ]; then - continue - else - break - fi - done - echo "Halting script" - exit 0 - shell: bash - - - name: Collect Docker Logs - if: success() || failure() - uses: jwalton/gh-docker-logs@v2.2.0 - with: - dest: './forkless-parachain-upgrade-data-logs.${{ matrix.features }}' - images: 'node-parachain' - - - name: Show Docker logs - if: success() || failure() - run: cat './forkless-parachain-upgrade-data-logs.${{ matrix.features }}/node-parachain.log' - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --- /dev/null +++ b/.github/workflows/forkless-update-data_v2.yml @@ -0,0 +1,165 @@ +# Controls when the action will run. +on: + workflow_call: + + +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + execution-marix: + + name: execution matrix + + runs-on: self-hosted-ci + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: CertainLach/create-matrix-action@v3 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.OPAL_REPLICA_FROM }}} + network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.QUARTZ_REPLICA_FROM }}} + network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}}, replica_from_address {${{ env.UNIQUE_REPLICA_FROM }}} + + forkless-update-data: + needs: execution-marix + # The type of runner that the job will run on + runs-on: [self-hosted-ci,large] + timeout-minutes: 1380 + + name: ${{ matrix.network }} + strategy: + matrix: + include: ${{fromJson(needs.execution-marix.outputs.matrix)}} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + + steps: + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-forkless-data.j2 + output_file: .docker/docker-compose.${{ matrix.network }}.yml + variables: | + REPO_URL=${{ github.server_url }}/${{ github.repository }}.git + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + POLKADOT_MAINNET_BRANCH=${{ env.POLKADOT_MAINNET_BRANCH }} + MAINNET_TAG=${{ matrix.mainnet_tag }} + MAINNET_BRANCH=${{ matrix.mainnet_branch }} + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + BRANCH=${{ github.head_ref }} + REPLICA_FROM=${{ matrix.replica_from_address }} + + - name: Show build configuration + run: cat .docker/docker-compose.${{ matrix.network }}.yml + + - name: Generate launch-config-forkless-data.json + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/forkless-config/launch-config-forkless-data.j2 + output_file: .docker/launch-config-forkless-data.json + variables: | + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + + - name: Show launch-config-forkless configuration + run: cat .docker/launch-config-forkless-data.json + + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --force-recreate --timeout 300 + + - name: Check if docker logs consist logs related to Runtime Upgrade testing. + if: success() + run: | + counter=160 + function check_container_status { + docker inspect -f {{.State.Running}} node-parachain + } + function do_docker_logs { + docker logs --details node-parachain 2>&1 + } + function is_started { + if [ "$(check_container_status)" == "true" ]; then + echo "Container: node-parachain RUNNING"; + echo "Check Docker logs" + DOCKER_LOGS=$(do_docker_logs) + if [[ ${DOCKER_LOGS} = *"🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸"* ]];then + echo "🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸" + return 0 + elif [[ ${DOCKER_LOGS} = *"🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧"* ]];then + echo "🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧" + return 1 + else + echo "Message not found in logs output, repeating..." + return 1 + fi + else + echo "Container node-parachain not RUNNING" + echo "Halting all future checks" + exit 1 + fi + exit 0 + } + while ! is_started; do + echo "Waiting for special message in log files " + sleep 30s + counter=$(( $counter - 1 )) + echo "Counter: $counter" + if [ "$counter" -gt "0" ]; then + continue + else + break + fi + done + echo "Halting script" + exit 0 + shell: bash + + - name: Collect Docker Logs + if: success() || failure() + uses: jwalton/gh-docker-logs@v2.2.0 + with: + dest: './forkless-parachain-upgrade-data-logs.${{ matrix.features }}' + images: 'node-parachain' + + - name: Show Docker logs + if: success() || failure() + run: cat './forkless-parachain-upgrade-data-logs.${{ matrix.features }}/node-parachain.log' + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --volumes --- a/.github/workflows/forkless-update-nodata.yml +++ /dev/null @@ -1,204 +0,0 @@ -name: upgrade nodata - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - pull_request: - branches: - - master - types: - - opened - - reopened - - synchronize #commit(s) pushed to the pull request - - ready_for_review - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - prepare-execution-marix: - - name: Prepare execution matrix - - runs-on: self-hosted-ci - outputs: - matrix: ${{ steps.create_matrix.outputs.matrix }} - - steps: - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Create Execution matrix - uses: fabiocaccamo/create-matrix-action@v2 - id: create_matrix - with: - matrix: | - network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} - network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} - network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}} - - - - forkless-update-nodata: - needs: prepare-execution-marix - # The type of runner that the job will run on - runs-on: [self-hosted-ci,large] - - - - timeout-minutes: 1380 - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} - - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.tmp-forkless-nodata.j2 - output_file: .docker/docker-compose.${{ matrix.network }}.yml - variables: | - REPO_URL=${{ github.server_url }}/${{ github.repository }}.git - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} - POLKADOT_MAINNET_BRANCH=${{ env.POLKADOT_MAINNET_BRANCH }} - MAINNET_TAG=${{ matrix.mainnet_tag }} - MAINNET_BRANCH=${{ matrix.mainnet_branch }} - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - BRANCH=${{ github.head_ref }} - - - name: Show build configuration - run: cat .docker/docker-compose.${{ matrix.network }}.yml - - - name: Generate launch-config-forkless-nodata.json - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/forkless-config/launch-config-forkless-nodata.j2 - output_file: .docker/launch-config-forkless-nodata.json - variables: | - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - - - name: Show launch-config-forkless configuration - run: cat .docker/launch-config-forkless-nodata.json - - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --force-recreate --timeout 300 - - - name: Check if docker logs consist logs related to Runtime Upgrade testing. - if: success() - run: | - counter=160 - function check_container_status { - docker inspect -f {{.State.Running}} node-parachain - } - function do_docker_logs { - docker logs --details node-parachain 2>&1 - } - function is_started { - if [ "$(check_container_status)" == "true" ]; then - echo "Container: node-parachain RUNNING"; - echo "Check Docker logs" - DOCKER_LOGS=$(do_docker_logs) - if [[ ${DOCKER_LOGS} = *"🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸"* ]];then - echo "🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸" - return 0 - elif [[ ${DOCKER_LOGS} = *"🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧"* ]];then - echo "🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧" - return 1 - else - echo "Message not found in logs output, repeating..." - return 1 - fi - else - echo "Container node-parachain not RUNNING" - echo "Halting all future checks" - exit 1 - fi - exit 0 - } - while ! is_started; do - echo "Waiting for special message in log files " - sleep 30s - counter=$(( $counter - 1 )) - echo "Counter: $counter" - if [ "$counter" -gt "0" ]; then - continue - else - break - fi - done - echo "Halting script" - exit 0 - shell: bash - - - name: Collect Docker Logs - if: success() || failure() - uses: jwalton/gh-docker-logs@v2.2.0 - with: - dest: './forkless-parachain-upgrade-nodata-logs.${{ matrix.features }}' - images: 'node-parachain' - - - name: Show docker logs - if: success() || failure() - run: cat './forkless-parachain-upgrade-nodata-logs.${{ matrix.features }}/node-parachain.log' - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --- /dev/null +++ b/.github/workflows/forkless-update-nodata_v2.yml @@ -0,0 +1,167 @@ + + +# Controls when the action will run. +on: + workflow_call: + + +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + execution-marix: + + name: Prepare execution matrix + + runs-on: self-hosted-ci + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: fabiocaccamo/create-matrix-action@v2 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} + network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} + network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}} + + + forkless-update-nodata: + needs: execution-marix + # The type of runner that the job will run on + runs-on: [self-hosted-ci,large] + + timeout-minutes: 1380 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: ${{fromJson(needs.execution-marix.outputs.matrix)}} + + steps: + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-forkless-nodata.j2 + output_file: .docker/docker-compose.${{ matrix.network }}.yml + variables: | + REPO_URL=${{ github.server_url }}/${{ github.repository }}.git + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + POLKADOT_MAINNET_BRANCH=${{ env.POLKADOT_MAINNET_BRANCH }} + MAINNET_TAG=${{ matrix.mainnet_tag }} + MAINNET_BRANCH=${{ matrix.mainnet_branch }} + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + BRANCH=${{ github.head_ref }} + + - name: Show build configuration + run: cat .docker/docker-compose.${{ matrix.network }}.yml + + - name: Generate launch-config-forkless-nodata.json + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/forkless-config/launch-config-forkless-nodata.j2 + output_file: .docker/launch-config-forkless-nodata.json + variables: | + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + + - name: Show launch-config-forkless configuration + run: cat .docker/launch-config-forkless-nodata.json + + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --force-recreate --timeout 300 + + - name: Check if docker logs consist logs related to Runtime Upgrade testing. + if: success() + run: | + counter=160 + function check_container_status { + docker inspect -f {{.State.Running}} node-parachain + } + function do_docker_logs { + docker logs --details node-parachain 2>&1 + } + function is_started { + if [ "$(check_container_status)" == "true" ]; then + echo "Container: node-parachain RUNNING"; + echo "Check Docker logs" + DOCKER_LOGS=$(do_docker_logs) + if [[ ${DOCKER_LOGS} = *"🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸"* ]];then + echo "🛸 PARACHAINS' RUNTIME UPGRADE TESTING COMPLETE 🛸" + return 0 + elif [[ ${DOCKER_LOGS} = *"🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧"* ]];then + echo "🚧 PARACHAINS' RUNTIME UPGRADE TESTING FAILED 🚧" + return 1 + else + echo "Message not found in logs output, repeating..." + return 1 + fi + else + echo "Container node-parachain not RUNNING" + echo "Halting all future checks" + exit 1 + fi + exit 0 + } + while ! is_started; do + echo "Waiting for special message in log files " + sleep 30s + counter=$(( $counter - 1 )) + echo "Counter: $counter" + if [ "$counter" -gt "0" ]; then + continue + else + break + fi + done + echo "Halting script" + exit 0 + shell: bash + + - name: Collect Docker Logs + if: success() || failure() + uses: jwalton/gh-docker-logs@v2.2.0 + with: + dest: './forkless-parachain-upgrade-nodata-logs.${{ matrix.features }}' + images: 'node-parachain' + + - name: Show docker logs + if: success() || failure() + run: cat './forkless-parachain-upgrade-nodata-logs.${{ matrix.features }}/node-parachain.log' + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --- /dev/null +++ b/.github/workflows/forkless.yml @@ -0,0 +1,18 @@ +name: Nesting Forkless + +on: + workflow_call: + +jobs: + + forkless-update-data: + name: with data + uses: ./.github/workflows/forkless-update-data_v2.yml + + forkless-update-no-data: + name: no data + uses: ./.github/workflows/forkless-update-nodata_v2.yml + + try-runtime: + name: try-runtime + uses: ./.github/workflows/try-runtime_v2.yml --- /dev/null +++ b/.github/workflows/generate-execution-matrix.yml @@ -0,0 +1,45 @@ +name: Prepare execution matrix + +on: + workflow_call: + # Map the workflow outputs to job outputs + outputs: + matrix_values: + description: "Matix output" + matrix: ${{ jobs.prepare-execution-matrix.outputs.matrix }} + + +#concurrency: +# group: ${{ github.workflow }}-${{ github.head_ref }} +# cancel-in-progress: true + + +jobs: + prepare-execution-matrix: + name: Generate output + runs-on: self-hosted-ci + # Map the job outputs to step outputs + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: CertainLach/create-matrix-action@v3 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.OPAL_REPLICA_FROM }}} + network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}}, replica_from_address {${{ env.QUARTZ_REPLICA_FROM }}} + network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}}, replica_from_address {${{ env.UNIQUE_REPLICA_FROM }}} --- /dev/null +++ b/.github/workflows/market-test_v2.yml @@ -0,0 +1,192 @@ +name: market api tests + +# Controls when the action will run. +on: + workflow_call: + + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + market_test: + # The type of runner that the job will run on + runs-on: [self-hosted-ci,large] + timeout-minutes: 1380 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: + - network: "opal" + features: "opal-runtime" +# - network: "quartz" +# features: "quartz-runtime" +# - network: "unique" +# features: "unique-runtime" + + steps: + + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Checkout master repo + uses: actions/checkout@master + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Checkout Market e2e tests + uses: actions/checkout@v3 + with: + repository: 'UniqueNetwork/market-e2e-tests' + ssh-key: ${{ secrets.GH_PAT }} + path: 'qa-tests' + ref: 'ci_test_v2' + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Copy qa-tests/.env.example to qa-tests/.env + working-directory: qa-tests + run: cp .env.docker .env + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: qa-tests/.docker/docker-compose.tmp-market.j2 + output_file: qa-tests/.docker/docker-compose.${{ matrix.network }}.yml + variables: | + REPO_URL=${{ github.server_url }}/${{ github.repository }}.git + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + FEATURE=${{ matrix.features }} + BRANCH=${{ github.head_ref }} + + + - name: Show build configuration + working-directory: qa-tests + run: cat .docker/docker-compose.${{ matrix.network }}.yml + + - name: Start node-parachain + working-directory: qa-tests + run: docker-compose -f ".docker/docker-compose.market.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build --remove-orphans --force-recreate node-parachain + + - uses: actions/setup-node@v3 + with: + node-version: 16.17 + + - name: Setup TypeScript + working-directory: qa-tests + run: | + npm install -g ts-node + npm install + + - name: Copy qa-tests/.env.docker to qa-tests/.env + working-directory: qa-tests + run: | + rm -rf .env + cp .env.docker .env + + - name: Wait for chain up and running + working-directory: tests + run: | + yarn install + node scripts/readyness.js + echo "Ready to start tests" + + - name: Show content of .env file and Generate accounts + working-directory: qa-tests + run: | + cat .env + ts-node ./src/scripts/create-market-accounts.ts + + - name: Copy qa-tests/.env to qa-tests/.env.docker + working-directory: qa-tests + run: | + rm -rf .env.docker + cp .env .env.docker + + - name: Get chain logs + if: always() # run this step always + run: | + docker exec node-parachain cat /polkadot-launch/9944.log + docker exec node-parachain cat /polkadot-launch/9945.log + docker exec node-parachain cat /polkadot-launch/alice.log + docker exec node-parachain cat /polkadot-launch/eve.log + docker exec node-parachain cat /polkadot-launch/dave.log + docker exec node-parachain cat /polkadot-launch/charlie.log + + - name: Deploy contracts + run: | + cd qa-tests + ts-node ./src/scripts/deploy-contract.ts + + - name: Timeout for debug + if: failure() + run: sleep 300s + + - name: Import test data + working-directory: qa-tests + run: ts-node ./src/scripts/create-test-collections.ts + + - name: Show content of qa-test .env + working-directory: qa-tests + run: cat .env + + - name: Read qa -test .env file Before market start + uses: xom9ikk/dotenv@v1.0.2 + with: + path: qa-tests/ + + + - name: local-market:start + run: docker-compose -f "qa-tests/.docker/docker-compose.market.yml" -f "qa-tests/.docker/docker-compose.${{ matrix.network }}.yml" up -d --build + + - name: Wait for market readyness + working-directory: qa-tests + run: src/scripts/wait-market-ready.sh + shell: bash + + - name: Install dependecies + working-directory: qa-tests + run: | + npm ci + npm install -D @playwright/test + npx playwright install-deps + npx playwright install + + - name: Show content of qa-test .env + working-directory: qa-tests + run: cat .env + + - name: Test API interface + working-directory: qa-tests + run: | + npx playwright test --workers=8 --quiet .*.api.test.ts --reporter=github --config playwright.config.ts + + - name: Timeout for debug + if: failure() + run: sleep 300s + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f "qa-tests/.docker/docker-compose.market.yml" -f "qa-tests/.docker/docker-compose.${{ matrix.network }}.yml" down --volumes +# run: docker-compose -f "qa-tests/.docker/docker-compose.market.yml" -f "qa-tests/.docker/docker-compose.${{ matrix.network }}.yml" down + + - name: Remove builder cache + if: always() # run this step always + run: | + docker builder prune -f + docker system prune -f + + - name: Clean Workspace + if: always() + uses: AutoModality/action-clean@v1.1.0 + + + + --- /dev/null +++ b/.github/workflows/node-only-update_v2.yml @@ -0,0 +1,271 @@ +name: nodes-only update + +# Controls when the action will run. +on: + workflow_call: +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + execution-marix: + + name: execution matrix + + runs-on: self-hosted-ci + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: fabiocaccamo/create-matrix-action@v2 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} + network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} + network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}} + + + + forkless-update-nodata: + needs: execution-marix + # The type of runner that the job will run on + runs-on: [self-hosted-ci,large] + + + + timeout-minutes: 1380 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-node.j2 + output_file: .docker/docker-compose.node.${{ matrix.network }}.yml + variables: | + REPO_URL=${{ github.server_url }}/${{ github.repository }}.git + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + POLKADOT_MAINNET_BRANCH=${{ env.POLKADOT_MAINNET_BRANCH }} + MAINNET_TAG=${{ matrix.mainnet_tag }} + MAINNET_BRANCH=${{ matrix.mainnet_branch }} + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + BRANCH=${{ github.head_ref }} + + - name: Show build configuration + run: cat .docker/docker-compose.node.${{ matrix.network }}.yml + + - name: Generate launch-config-forkless-nodata.json + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/forkless-config/launch-config-forkless-nodata.j2 + output_file: .docker/launch-config-forkless-nodata.json + variables: | + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + + - name: Show launch-config-forkless configuration + run: cat .docker/launch-config-forkless-nodata.json + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.node.${{ matrix.network }}.yml" up -d --build --remove-orphans --force-recreate --timeout 300 + + # 🚀 POLKADOT LAUNCH COMPLETE 🚀 + - name: Check if docker logs consist messages related to testing of Node Parachain Upgrade. + if: success() + run: | + counter=160 + function check_container_status { + docker inspect -f {{.State.Running}} node-parachain + } + function do_docker_logs { + docker logs --details node-parachain 2>&1 + } + function is_started { + if [ "$(check_container_status)" == "true" ]; then + echo "Container: node-parachain RUNNING"; + echo "Check Docker logs" + DOCKER_LOGS=$(do_docker_logs) + if [[ ${DOCKER_LOGS} = *"POLKADOT LAUNCH COMPLETE"* ]];then + echo "🚀 POLKADOT LAUNCH COMPLETE 🚀" + return 0 + else + echo "Message not found in logs output, repeating..." + return 1 + fi + else + echo "Container node-parachain NOT RUNNING" + echo "Halting all future checks" + exit 1 + fi + echo "something goes wrong" + exit 1 + } + while ! is_started; do + echo "Waiting for special message in log files " + sleep 30s + counter=$(( $counter - 1 )) + echo "Counter: $counter" + if [ "$counter" -gt "0" ]; then + continue + else + break + fi + done + echo "Halting script" + exit 0 + shell: bash + + - name: Run tests before Node Parachain upgrade + working-directory: tests + run: | + yarn install + yarn add mochawesome + node scripts/readyness.js + echo "Ready to start tests" + yarn polkadot-types + NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} + env: + RPC_URL: http://127.0.0.1:9933/ + + - name: Test Report Before Node upgrade + uses: phoenix-actions/test-reporting@v8 + id: test-report-before + if: success() || failure() # run this step even if previous step failed + with: + name: Tests before node upgrade ${{ matrix.network }} # Name of the check run which will be created + path: tests/mochawesome-report/test-*.json # Path to test results + reporter: mochawesome-json + fail-on-error: 'false' + + - name: Send SIGUSR1 to polkadotlaunch process + if: success() || failure() + run: | + #Get PID of polkadot-launch + PID=$(docker exec node-parachain pidof 'polkadot-launch') + echo "Polkadot-launch PID: $PID" + #Send SIGUSR1 signal to $PID + docker exec node-parachain kill -SIGUSR1 ${PID} + + # 🌗 All parachain collators restarted with the new binaries. + - name: Check if docker logs consist messages related to testing of Node Parachain Upgrade. + if: success() + run: | + counter=160 + function check_container_status { + docker inspect -f {{.State.Running}} node-parachain + } + function do_docker_logs { + docker logs --details node-parachain 2>&1 + } + function is_started { + if [ "$(check_container_status)" == "true" ]; then + echo "Container: node-parachain RUNNING"; + echo "Check Docker logs" + DOCKER_LOGS=$(do_docker_logs) + if [[ ${DOCKER_LOGS} = *"All parachain collators restarted with the new binaries."* ]];then + echo "🌗 All parachain collators restarted with the new binaries." + return 0 + else + echo "Message not found in logs output, repeating..." + return 1 + fi + else + echo "Container node-parachain NOT RUNNING" + echo "Halting all future checks" + exit 1 + fi + echo "something goes wrong" + exit 1 + } + while ! is_started; do + echo "Waiting for special message in log files " + sleep 30s + counter=$(( $counter - 1 )) + echo "Counter: $counter" + if [ "$counter" -gt "0" ]; then + continue + else + break + fi + done + echo "Halting script" + exit 0 + shell: bash + + - name: Run tests after Node Parachain upgrade + working-directory: tests + run: | + yarn install + yarn add mochawesome + node scripts/readyness.js + echo "Ready to start tests" + yarn polkadot-types + NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} + env: + RPC_URL: http://127.0.0.1:9933/ + + - name: Test Report After Node upgrade + uses: phoenix-actions/test-reporting@v8 + id: test-report-after + if: success() || failure() # run this step even if previous step failed + with: + name: Tests after node upgrade ${{ matrix.network }} # Name of the check run which will be created + path: tests/mochawesome-report/test-*.json # Path to test results + reporter: mochawesome-json + fail-on-error: 'false' + + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.node.${{ matrix.network }}.yml" down --volumes + + - name: Remove builder cache + if: always() # run this step always + run: | + docker builder prune -f + docker system prune -f + + - name: Clean Workspace + if: always() + uses: AutoModality/action-clean@v1.1.0 --- a/.github/workflows/nodes-only-update.yml +++ /dev/null @@ -1,301 +0,0 @@ -name: nodes-only update - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - pull_request: - branches: - - master - types: - - opened - - reopened - - synchronize #commit(s) pushed to the pull request - - ready_for_review - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - prepare-execution-marix: - - name: Prepare execution matrix - - runs-on: self-hosted-ci - outputs: - matrix: ${{ steps.create_matrix.outputs.matrix }} - - steps: - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Create Execution matrix - uses: fabiocaccamo/create-matrix-action@v2 - id: create_matrix - with: - matrix: | - network {opal}, runtime {opal}, features {opal-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} - network {quartz}, runtime {quartz}, features {quartz-runtime}, mainnet_branch {${{ env.QUARTZ_MAINNET_TAG }}} - network {unique}, runtime {unique}, features {unique-runtime}, mainnet_branch {${{ env.UNIQUE_MAINNET_TAG }}} - - - - forkless-update-nodata: - needs: prepare-execution-marix - # The type of runner that the job will run on - runs-on: [self-hosted-ci,large] - - - - timeout-minutes: 1380 - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} - - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.tmp-node.j2 - output_file: .docker/docker-compose.node.${{ matrix.network }}.yml - variables: | - REPO_URL=${{ github.server_url }}/${{ github.repository }}.git - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} - POLKADOT_MAINNET_BRANCH=${{ env.POLKADOT_MAINNET_BRANCH }} - MAINNET_TAG=${{ matrix.mainnet_tag }} - MAINNET_BRANCH=${{ matrix.mainnet_branch }} - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - BRANCH=${{ github.head_ref }} - - - name: Show build configuration - run: cat .docker/docker-compose.node.${{ matrix.network }}.yml - - - name: Generate launch-config-forkless-nodata.json - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/forkless-config/launch-config-forkless-nodata.j2 - output_file: .docker/launch-config-forkless-nodata.json - variables: | - FEATURE=${{ matrix.features }} - RUNTIME=${{ matrix.runtime }} - - - name: Show launch-config-forkless configuration - run: cat .docker/launch-config-forkless-nodata.json - - - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.node.${{ matrix.network }}.yml" up -d --build --remove-orphans --force-recreate --timeout 300 - - # 🚀 POLKADOT LAUNCH COMPLETE 🚀 - - name: Check if docker logs consist messages related to testing of Node Parachain Upgrade. - if: success() - run: | - counter=160 - function check_container_status { - docker inspect -f {{.State.Running}} node-parachain - } - function do_docker_logs { - docker logs --details node-parachain 2>&1 - } - function is_started { - if [ "$(check_container_status)" == "true" ]; then - echo "Container: node-parachain RUNNING"; - echo "Check Docker logs" - DOCKER_LOGS=$(do_docker_logs) - if [[ ${DOCKER_LOGS} = *"POLKADOT LAUNCH COMPLETE"* ]];then - echo "🚀 POLKADOT LAUNCH COMPLETE 🚀" - return 0 - else - echo "Message not found in logs output, repeating..." - return 1 - fi - else - echo "Container node-parachain NOT RUNNING" - echo "Halting all future checks" - exit 1 - fi - echo "something goes wrong" - exit 1 - } - while ! is_started; do - echo "Waiting for special message in log files " - sleep 30s - counter=$(( $counter - 1 )) - echo "Counter: $counter" - if [ "$counter" -gt "0" ]; then - continue - else - break - fi - done - echo "Halting script" - exit 0 - shell: bash - - - name: Run tests before Node Parachain upgrade - working-directory: tests - run: | - yarn install - yarn add mochawesome - echo "Ready to start tests" - NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} - env: - RPC_URL: http://127.0.0.1:9933/ - - - name: Test Report Before Node upgrade - uses: phoenix-actions/test-reporting@v8 - id: test-report-before - if: success() || failure() # run this step even if previous step failed - with: - name: Tests before node upgrade ${{ matrix.network }} # Name of the check run which will be created - path: tests/mochawesome-report/test-*.json # Path to test results - reporter: mochawesome-json - fail-on-error: 'false' - - - name: Send SIGUSR1 to polkadotlaunch process - if: success() || failure() - run: | - #Get PID of polkadot-launch - PID=$(docker exec node-parachain pidof 'polkadot-launch') - echo "Polkadot-launch PID: $PID" - #Send SIGUSR1 signal to $PID - docker exec node-parachain kill -SIGUSR1 ${PID} - - # 🌗 All parachain collators restarted with the new binaries. - - name: Check if docker logs consist messages related to testing of Node Parachain Upgrade. - if: success() - run: | - counter=160 - function check_container_status { - docker inspect -f {{.State.Running}} node-parachain - } - function do_docker_logs { - docker logs --details node-parachain 2>&1 - } - function is_started { - if [ "$(check_container_status)" == "true" ]; then - echo "Container: node-parachain RUNNING"; - echo "Check Docker logs" - DOCKER_LOGS=$(do_docker_logs) - if [[ ${DOCKER_LOGS} = *"All parachain collators restarted with the new binaries."* ]];then - echo "🌗 All parachain collators restarted with the new binaries." - return 0 - else - echo "Message not found in logs output, repeating..." - return 1 - fi - else - echo "Container node-parachain NOT RUNNING" - echo "Halting all future checks" - exit 1 - fi - echo "something goes wrong" - exit 1 - } - while ! is_started; do - echo "Waiting for special message in log files " - sleep 30s - counter=$(( $counter - 1 )) - echo "Counter: $counter" - if [ "$counter" -gt "0" ]; then - continue - else - break - fi - done - echo "Halting script" - exit 0 - shell: bash - - - name: Run tests after Node Parachain upgrade - working-directory: tests - run: | - yarn install - yarn add mochawesome - echo "Ready to start tests" - NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} - env: - RPC_URL: http://127.0.0.1:9933/ - - - name: Test Report After Node upgrade - uses: phoenix-actions/test-reporting@v8 - id: test-report-after - if: success() || failure() # run this step even if previous step failed - with: - name: Tests after node upgrade ${{ matrix.network }} # Name of the check run which will be created - path: tests/mochawesome-report/test-*.json # Path to test results - reporter: mochawesome-json - fail-on-error: 'false' - - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose-forkless.yml" -f ".docker/docker-compose.node.${{ matrix.network }}.yml" down --volumes - - - name: Remove builder cache - if: always() # run this step always - run: | - docker builder prune -f - docker system prune -f - - - name: Clean Workspace - if: always() - uses: AutoModality/action-clean@v1.1.0 --- /dev/null +++ b/.github/workflows/test_codestyle_v2.yml @@ -0,0 +1,20 @@ +name: yarn eslint + +on: + workflow_call: + +jobs: + code_style: + runs-on: [ self-hosted-ci ] + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Install modules + run: cd tests && yarn + - name: Run ESLint + run: cd tests && yarn eslint --ext .ts,.js src/ --- a/.github/workflows/tests_codestyle.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: yarn eslint - -on: - pull_request: - branches: - - develop - types: - - opened - - reopened - - synchronize - - ready_for_review - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -jobs: - code_style: - runs-on: self-hosted-ci - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Install modules - run: cd tests && yarn - - name: Run ESLint - run: cd tests && yarn eslint --ext .ts,.js src/ --- a/.github/workflows/try-runtime.yml +++ /dev/null @@ -1,108 +0,0 @@ -name: try-runtime - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - pull_request: - branches: - - master - types: - - opened - - reopened - - synchronize #commit(s) pushed to the pull request - - ready_for_review - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -#Define Workflow variables -env: - REPO_URL: ${{ github.server_url }}/${{ github.repository }} - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - try-runtime: - # The type of runner that the job will run on - runs-on: self-hosted-ci - - name: ${{ matrix.network }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - strategy: - matrix: - include: - - network: opal - features: try-runtime,opal-runtime - replica_from_address: wss://eu-ws-opal.unique.network:443 - - network: quartz - features: try-runtime,quartz-runtime - replica_from_address: wss://eu-ws-quartz.unique.network:443 - - network: unique - features: try-runtime,unique-runtime - replica_from_address: wss://eu-ws.unique.network:443 - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.try-runtime.j2 - output_file: .docker/docker-compose.try-runtime.${{ matrix.network }}.yml - variables: | - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - FEATURE=${{ matrix.features }} - REPLICA_FROM=${{ matrix.replica_from_address }} - - - name: Show build configuration - run: cat .docker/docker-compose.try-runtime.${{ matrix.network }}.yml - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose-try-runtime.yml" -f ".docker/docker-compose.try-runtime.${{ matrix.network }}.yml" up --build --force-recreate --timeout 300 --remove-orphans --exit-code-from try-runtime - - - name: Collect Docker Logs - if: success() || failure() - uses: jwalton/gh-docker-logs@v2.2.0 - with: - dest: './try-runtime-logs.${{ matrix.network }}' - images: 'try-runtime' - - - name: Show docker logs - run: cat './try-runtime-logs.${{ matrix.network }}/try-runtime.log' - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose-try-runtime.yml" -f ".docker/docker-compose.try-runtime.${{ matrix.network }}.yml" down --- /dev/null +++ b/.github/workflows/try-runtime_v2.yml @@ -0,0 +1,69 @@ +on: + workflow_call: + + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + try-runtime: + # The type of runner that the job will run on + runs-on: self-hosted-ci + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: + - network: opal + features: opal-runtime + replica_from_address: wss://eu-ws-opal.unique.network:443 + - network: quartz + features: quartz-runtime + replica_from_address: wss://eu-ws-quartz.unique.network:443 + - network: unique + features: unique-runtime + replica_from_address: wss://eu-ws.unique.network:443 + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.try-runtime.j2 + output_file: .docker/docker-compose.try-runtime.${{ matrix.network }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + FEATURE=${{ matrix.features }} + REPLICA_FROM=${{ matrix.replica_from_address }} + + - name: Show build configuration + run: cat .docker/docker-compose.try-runtime.${{ matrix.network }}.yml + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-try-runtime.yml" -f ".docker/docker-compose.try-runtime.${{ matrix.network }}.yml" up --build --force-recreate --timeout 300 --remove-orphans --exit-code-from try-runtime + + - name: Collect Docker Logs + if: success() || failure() + uses: jwalton/gh-docker-logs@v2.2.0 + with: + dest: './try-runtime-logs.${{ matrix.network }}' + images: 'try-runtime' + + - name: Show docker logs + run: cat './try-runtime-logs.${{ matrix.network }}/try-runtime.log' + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-try-runtime.yml" -f ".docker/docker-compose.try-runtime.${{ matrix.network }}.yml" down --- a/.github/workflows/unit-test.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: unit tests - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - pull_request: - branches: - - develop - - master - types: - - opened - - reopened - - synchronize #commit(s) pushed to the pull request - - ready_for_review - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - unit_tests: - # The type of runner that the job will run on - runs-on: [self-hosted-ci,medium] - timeout-minutes: 1380 - - name: ${{ github.base_ref }} - - continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. - - - steps: - - name: Skip if pull request is in Draft - # `if: github.event.pull_request.draft == true` should be kept here, at - # the step level, rather than at the job level. The latter is not - # recommended because when the PR is moved from "Draft" to "Ready to - # review" the workflow will immediately be passing (since it was skipped), - # even though it hasn't actually ran, since it takes a few seconds for - # the workflow to start. This is also disclosed in: - # https://github.community/t/dont-run-actions-on-draft-pull-requests/16817/17 - # That scenario would open an opportunity for the check to be bypassed: - # 1. Get your PR approved - # 2. Move it to Draft - # 3. Push whatever commits you want - # 4. Move it to "Ready for review"; now the workflow is passing (it was - # skipped) and "Check reviews" is also passing (it won't be updated - # until the workflow is finished) - if: github.event.pull_request.draft == true - run: exit 1 - - - name: Clean Workspace - uses: AutoModality/action-clean@v1.1.0 - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} #Checking out head commit - - - name: Read .env file - uses: xom9ikk/dotenv@v1.0.2 - - - name: Generate ENV related extend file for docker-compose - uses: cuchi/jinja2-action@v1.2.0 - with: - template: .docker/docker-compose.tmp-unit.j2 - output_file: .docker/docker-compose.unit.yml - variables: | - RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} - FEATURE=${{ matrix.features }} - - - - name: Show build configuration - run: cat .docker/docker-compose.unit.yml - - - name: Build the stack - run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.unit.yml" up --build --force-recreate --timeout 300 --remove-orphans --exit-code-from node-dev - - - name: Stop running containers - if: always() # run this step always - run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.unit.yml" down --- /dev/null +++ b/.github/workflows/unit-test_v2.yml @@ -0,0 +1,51 @@ +name: unit tests + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + workflow_call: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + unit_tests: + # The type of runner that the job will run on + runs-on: [self-hosted-ci,medium] + timeout-minutes: 1380 + + name: ${{ github.base_ref }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-unit.j2 + output_file: .docker/docker-compose.unit.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + + + - name: Show build configuration + run: cat .docker/docker-compose.unit.yml + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.unit.yml" up --build --force-recreate --timeout 300 --remove-orphans --exit-code-from node-dev + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-dev.yaml" -f ".docker/docker-compose.unit.yml" down --- /dev/null +++ b/.github/workflows/xcm-testnet-build.yml @@ -0,0 +1,151 @@ +name: xcm-testnet-build + +# Controls when the action will run. +on: + workflow_call: + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + prepare-execution-marix: + + name: Prepare execution matrix + + runs-on: [XL] + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: fabiocaccamo/create-matrix-action@v2 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.WESTMINT_BUILD_BRANCH }}} + network {quartz}, runtime {quartz}, features {quartz-runtime}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}} + network {unique}, runtime {unique}, features {unique-runtime}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINT_BUILD_BRANCH }}} + + xcm-build: + + needs: prepare-execution-marix + # The type of runner that the job will run on + runs-on: [XL] + + timeout-minutes: 600 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} + + steps: + - name: Skip if pull request is in Draft + if: github.event.pull_request.draft == true + run: exit 1 + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend Dockerfile file + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-xcm.j2 + output_file: .docker/Dockerfile-xcm.${{ matrix.network }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + NETWORK=${{ matrix.network }} + POLKADOT_BUILD_BRANCH=${{ env.POLKADOT_BUILD_BRANCH }} + POLKADOT_LAUNCH_BRANCH=${{ env.POLKADOT_LAUNCH_BRANCH }} + FEATURE=${{ matrix.features }} + RUNTIME=${{ matrix.runtime }} + BRANCH=${{ github.head_ref }} + ACALA_BUILD_BRANCH=${{ matrix.acala_version }} + MOONBEAM_BUILD_BRANCH=${{ matrix.moonbeam_version }} + CUMULUS_BUILD_BRANCH=${{ matrix.cumulus_version }} + + - name: Show build Dockerfile + run: cat .docker/Dockerfile-xcm.${{ matrix.network }}.yml + + - name: Show launch-config-xcm-${{ matrix.network }} configuration + run: cat .docker/xcm-config/launch-config-xcm-${{ matrix.network }}.json + + - name: Run find-and-replace to remove slashes from branch name + uses: mad9000/actions-find-and-replace-string@2 + id: branchname + with: + source: ${{ github.head_ref }} + find: '/' + replace: '-' + + - name: Log in to Docker Hub + uses: docker/login-action@v2.0.0 + with: + username: ${{ secrets.CORE_DOCKERHUB_USERNAME }} + password: ${{ secrets.CORE_DOCKERHUB_TOKEN }} + + - name: Pull acala docker image + run: docker pull uniquenetwork/builder-acala:${{ matrix.acala_version }} + + - name: Pull moonbeam docker image + run: docker pull uniquenetwork/builder-moonbeam:${{ matrix.moonbeam_version }} + + - name: Pull cumulus docker image + run: docker pull uniquenetwork/builder-cumulus:${{ matrix.cumulus_version }} + + - name: Pull polkadot docker image + run: docker pull uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} + + - name: Pull chainql docker image + run: docker pull uniquenetwork/builder-chainql:latest + + - name: Build the stack + run: cd .docker/ && docker build --no-cache --file ./Dockerfile-xcm.${{ matrix.network }}.yml --tag uniquenetwork/xcm-${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} --tag uniquenetwork/xcm-${{ matrix.network }}-testnet-local:latest . + + - name: Push docker image version + run: docker push uniquenetwork/xcm-${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} + + - name: Push docker image latest + run: docker push uniquenetwork/xcm-${{ matrix.network }}-testnet-local:latest + + - name: Clean Workspace + if: always() + uses: AutoModality/action-clean@v1.1.0 + + - name: Remove builder cache + if: always() # run this step always + run: | + docker builder prune -f + docker system prune -f + + + --- /dev/null +++ b/.github/workflows/xcm-tests_v2.yml @@ -0,0 +1,175 @@ +name: xcm-tests + +# Controls when the action will run. +on: + # Allows you to run this workflow manually from the Actions tab + workflow_call: + +#Define Workflow variables +env: + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + prepare-execution-marix: + + name: Prepare execution matrix + +# needs: [xcm-testnet-build] + + runs-on: XL + outputs: + matrix: ${{ steps.create_matrix.outputs.matrix }} + + steps: + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Create Execution matrix + uses: fabiocaccamo/create-matrix-action@v2 + id: create_matrix + with: + matrix: | + network {opal}, runtime {opal}, features {opal-runtime}, runtest {testXcmOpal} + network {quartz}, runtime {quartz}, features {quartz-runtime}, runtest {testXcmQuartz} + network {unique}, runtime {unique}, features {unique-runtime}, runtest {testXcmUnique} + + xcm-tests: + needs: prepare-execution-marix + # The type of runner that the job will run on + runs-on: [XL] + + timeout-minutes: 600 + + name: ${{ matrix.network }} + + continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false. + + strategy: + matrix: + include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}} + + + steps: + - name: Skip if pull request is in Draft + if: github.event.pull_request.draft == true + run: exit 1 + + - name: Clean Workspace + uses: AutoModality/action-clean@v1.1.0 + + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} #Checking out head commit + + - name: Read .env file + uses: xom9ikk/dotenv@v1.0.2 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-xcm-tests.j2 + output_file: .docker/docker-compose.xcm-tests.${{ matrix.network }}.yml + variables: | + NETWORK=${{ matrix.network }} + + - name: Show build configuration + run: cat .docker/docker-compose.xcm-tests.${{ matrix.network }}.yml + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose.xcm-tests.${{ matrix.network }}.yml" up -d --remove-orphans --force-recreate --timeout 300 + + # 🚀 POLKADOT LAUNCH COMPLETE 🚀 + - name: Check if docker logs consist messages related to testing of xcm tests + if: success() + run: | + counter=160 + function check_container_status { + docker inspect -f {{.State.Running}} xcm-${{ matrix.network }}-testnet-local + } + function do_docker_logs { + docker logs --details xcm-${{ matrix.network }}-testnet-local 2>&1 + } + function is_started { + if [ "$(check_container_status)" == "true" ]; then + echo "Container: xcm-${{ matrix.network }}-testnet-local RUNNING"; + echo "Check Docker logs" + DOCKER_LOGS=$(do_docker_logs) + if [[ ${DOCKER_LOGS} = *"POLKADOT LAUNCH COMPLETE"* ]];then + echo "🚀 POLKADOT LAUNCH COMPLETE 🚀" + return 0 + else + echo "Message not found in logs output, repeating..." + return 1 + fi + else + echo "Container xcm-${{ matrix.network }}-testnet-local NOT RUNNING" + echo "Halting all future checks" + exit 1 + fi + echo "something goes wrong" + exit 1 + } + while ! is_started; do + echo "Waiting for special message in log files " + sleep 30s + counter=$(( $counter - 1 )) + echo "Counter: $counter" + if [ "$counter" -gt "0" ]; then + continue + else + break + fi + done + echo "Halting script" + exit 0 + shell: bash + + - name: Run XCM tests + working-directory: tests + run: | + yarn install + yarn add mochawesome + node scripts/readyness.js + echo "Ready to start tests" + NOW=$(date +%s) && yarn ${{ matrix.runtest }} --reporter mochawesome --reporter-options reportFilename=test-${NOW} + + - name: XCM Test Report + uses: phoenix-actions/test-reporting@v8 + id: test-report + if: success() || failure() # run this step even if previous step failed + with: + name: XCM Tests ${{ matrix.network }} # Name of the check run which will be created + path: tests/mochawesome-report/test-*.json # Path to test results + reporter: mochawesome-json + fail-on-error: 'false' + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose.xcm-tests.${{ matrix.network }}.yml" down + + - name: Clean Workspace + if: always() + uses: AutoModality/action-clean@v1.1.0 + + - name: Remove builder cache + if: always() # run this step always + run: | + docker system prune -a -f + --- /dev/null +++ b/.github/workflows/xcm.yml @@ -0,0 +1,19 @@ +name: Nesting XCM + +on: + workflow_call: + + +jobs: + + xcm-testnet-build: + name: testnet build + uses: ./.github/workflows/xcm-testnet-build.yml + secrets: inherit + + xcm-tests: + name: tests + needs: [ xcm-testnet-build ] + uses: ./.github/workflows/xcm-tests_v2.yml + + --- a/.maintain/scripts/generate_sol.sh +++ b/.maintain/scripts/generate_sol.sh @@ -1,11 +1,14 @@ #!/bin/sh set -eu +PRETTIER_CONFIG="$(pwd)""/.prettierrc" + tmp=$(mktemp) cargo test --package $PACKAGE -- $NAME --exact --nocapture --ignored | tee $tmp raw=$(mktemp --suffix .sol) sed -n '/=== SNIP START ===/, /=== SNIP END ===/{ /=== SNIP START ===/! { /=== SNIP END ===/! p } }' $tmp > $raw + formatted=$(mktemp) -prettier --use-tabs $raw > $formatted +prettier --config $PRETTIER_CONFIG $raw > $formatted mv $formatted $OUTPUT --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +!**/*.sol --- /dev/null +++ b/.prettierrc @@ -0,0 +1,16 @@ +{ + "useTabs": true, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "all", + "overrides": [ + { + "files": "*.sol", + "options": { + "singleQuote": false, + "printWidth": 120, + "explicitTypes": "always" + } + } + ] +} \ No newline at end of file --- a/Cargo.lock +++ b/Cargo.lock @@ -2324,7 +2324,7 @@ [[package]] name = "fc-consensus" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "async-trait", "fc-db", @@ -2343,7 +2343,7 @@ [[package]] name = "fc-db" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "fp-storage", "kvdb-rocksdb", @@ -2359,7 +2359,7 @@ [[package]] name = "fc-mapping-sync" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "fc-db", "fp-consensus", @@ -2376,7 +2376,7 @@ [[package]] name = "fc-rpc" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -2416,7 +2416,7 @@ [[package]] name = "fc-rpc-core" version = "1.1.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -2557,7 +2557,7 @@ [[package]] name = "fp-consensus" version = "2.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "parity-scale-codec 3.1.5", @@ -2569,7 +2569,7 @@ [[package]] name = "fp-evm" version = "3.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "evm", "frame-support", @@ -2583,7 +2583,7 @@ [[package]] name = "fp-evm-mapping" version = "0.1.0" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "frame-support", "sp-core", @@ -2592,7 +2592,7 @@ [[package]] name = "fp-rpc" version = "3.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -2609,7 +2609,7 @@ [[package]] name = "fp-self-contained" version = "1.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "frame-support", @@ -2625,7 +2625,7 @@ [[package]] name = "fp-storage" version = "2.0.0" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "parity-scale-codec 3.1.5", ] @@ -5445,7 +5445,7 @@ [[package]] name = "pallet-base-fee" version = "1.0.0" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "fp-evm", "frame-support", @@ -5660,7 +5660,7 @@ [[package]] name = "pallet-ethereum" version = "4.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "ethereum", "ethereum-types", @@ -5689,7 +5689,7 @@ [[package]] name = "pallet-evm" version = "6.0.0-dev" -source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27#69945f20e8dc06ab9e0dd636848a8db6a408c71c" +source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit#0ae36821312f4a26e7f96225f1b2cb16216cddf5" dependencies = [ "evm", "fp-evm", @@ -5733,7 +5733,7 @@ [[package]] name = "pallet-evm-contract-helpers" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ethereum", "evm-coder", @@ -5744,6 +5744,7 @@ "pallet-common", "pallet-evm", "pallet-evm-coder-substrate", + "pallet-evm-transaction-payment", "parity-scale-codec 3.1.5", "scale-info", "sp-core", @@ -12316,7 +12317,7 @@ [[package]] name = "unique-rpc" -version = "0.1.1" +version = "0.1.2" dependencies = [ "app-promotion-rpc", "fc-db", --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -19,4 +19,4 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-rpc = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -577,8 +577,8 @@ BlockNumber: Decode + Member + AtLeast32BitUnsigned, AccountId: Decode, C: 'static + ProvideRuntimeApi + HeaderBackend, + CrossAccountId: pallet_evm::account::CrossAccountId, C::Api: AppPromotionRuntimeApi, - CrossAccountId: pallet_evm::account::CrossAccountId, { pass_method!(total_staked(staker: Option) -> String => |v| v.to_string(), app_promotion_api); pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> => --- /dev/null +++ b/doc/sponsoring-flow.drawio.svg @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + +
+
+
+ EVM calls +
+
+
+
+ + EVM calls + +
+
+ + + + +
+
+
+
+ Extrinsics +
+
+
+
+
+ + Extrinsics + +
+
+ + + + + + + + +
+
+
+
+ User +
+
+
+
+
+ + User + +
+
+ + + + + + +
+
+
+
+ Substrate +
+
+
+
+
+ + Substrate + +
+
+ + + + + + +
+
+
+
+ Ethereum +
+
+
+
+
+ + Ethereum + +
+
+ + + + + + +
+
+
+ pallet_evm:: +
+ TransactionValidityHack +
+
+
+
+ + pallet_evm::... + +
+
+ + + + + + +
+
+
+

+ + pallet_charge_evm_transaction:: +
+ Config::EvmSponsorshipHandler +
+
+

+
+
+
+
+ + pallet_charge_evm_tr... + +
+
+ + + + + + +
+
+
+ pallet_charge_evm_transaction:: +
+ BridgeSponsorshipHandler +
+
+
+
+ + pallet_charge_evm_transaction::... + +
+
+ + + + + + +
+
+
+ pallet_sponsoring:: +
+ ChargeTransactionPayment +
+
+
+
+
+ + pallet_sponsoring::... + +
+
+ + + + + +
+
+
+ tx.others +
+
+
+
+ + tx.others + +
+
+ + + + + +
+
+
+
+ tx.evm.call +
+
+
+
+
+ + tx.evm.call + +
+
+ + + + +
+
+
+
+ pallet_sponsoring:: +
+ Config::SponsorshipHandler +
+
+
+
+
+ + pallet_sponsoring::... + +
+
+ + + + +
+
+
+ SponsorshipHandler<CrossAccountId, (H160, Vec<u8>), CallContext> +
+
+
+
+ + SponsorshipHandler<CrossAccountId, (H160, Vec<u8>), CallContext> + +
+
+ + + + +
+
+
+ SponsorshipHandler<AccountId, Call, ()> +
+
+
+
+ + SponsorshipHandler<AccountId, Call, ()> + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
--- /dev/null +++ b/doc/sponsoring.md @@ -0,0 +1,13 @@ +# Sponsoring + +![Sponsoring flow](./sponsoring-flow.drawio.svg) + +## Implementation + +If you need to add sponsoring for pallet call, you should implement `SponsorshipHandler`, see `UniqueSponsorshipHandler` for example. + +If you need to add sponsoring for EVM contract call, you should implement `SponsorshipHandler), CallContext>`, see `UniqueEthSponsorshipHandler` for example. + +## EVM bridging + +In case if Ethereum call is being called using substrate `evm.call` extrinsic, `BridgeSponsorshipHandler` is used to convert between two different `SponsorshipHandler` types --- a/node/cli/CHANGELOG.md +++ b/node/cli/CHANGELOG.md @@ -1,4 +1,10 @@ + +## [v0.9.27] 2022-09-08 + +### Added +- Support RPC for `AppPromotion` pallet. + ## [v0.9.27] 2022-08-16 ### Other changes --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -309,13 +309,13 @@ jsonrpsee = { version = "0.14.0", features = ["server", "macros"] } tokio = { version = "1.19.2", features = ["time"] } -fc-rpc-core = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-consensus = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-mapping-sync = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-db = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fc-rpc-core = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-consensus = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-mapping-sync = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-db = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } unique-rpc = { default-features = false, path = "../rpc" } app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} --- a/node/rpc/CHANGELOG.md +++ b/node/rpc/CHANGELOG.md @@ -1,4 +1,9 @@ +## [v0.1.2] 2022-09-08 + +### Added +- Support RPC for `AppPromotion` pallet. + ## [v0.1.1] 2022-08-16 ### Other changes --- a/node/rpc/Cargo.toml +++ b/node/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "unique-rpc" -version = "0.1.1" +version = "0.1.2" authors = ['Unique Network '] license = 'GPLv3' edition = "2021" @@ -40,13 +40,13 @@ substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } tokio = { version = "1.19.2", features = ["macros", "sync"] } -pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-storage = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-rpc-core = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-db = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-storage = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-rpc-core = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-db = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { default-features = false, path = "../../pallets/common" } up-common = { path = "../../primitives/common" } --- a/pallets/app-promotion/Cargo.toml +++ b/pallets/app-promotion/Cargo.toml @@ -52,7 +52,7 @@ pallet-balances ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } pallet-timestamp ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } pallet-randomness-collective-flip ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm ={ default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm ={ default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } sp-std ={ default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } --- a/pallets/app-promotion/src/benchmarking.rs +++ b/pallets/app-promotion/src/benchmarking.rs @@ -112,6 +112,7 @@ let share = Perbill::from_rational(1u32, 20); let _ = ::Currency::make_free_balance_be(&caller, Perbill::from_rational(1u32, 2) * BalanceOf::::max_value()); (0..10).map(|_| { + // used to change block number >::finalize(); PromototionPallet::::stake(RawOrigin::Signed(caller.clone()).into(), share * ::Currency::total_balance(&caller)) }).collect::, _>>()?; --- a/pallets/app-promotion/src/lib.rs +++ b/pallets/app-promotion/src/lib.rs @@ -14,13 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -//! # App promotion +//! # App Promotion pallet +//! +//! The pallet implements the mechanics of staking and sponsoring collections/contracts. //! -//! The app promotion pallet is designed to ... . +//! - [`Config`] +//! - [`Pallet`] +//! - [`Error`] +//! - [`Event`] +//! +//! ## Overview +//! The App Promotion pallet allows fund holders to stake at a certain daily rate of return. +//! The mechanics implemented in the pallet allow it to act as a sponsor for collections / contracts, +//! the list of which is set by the pallet administrator. +//! //! //! ## Interface +//! The pallet provides interfaces for funds, collection/contract operations (see [types] module). + //! //! ### Dispatchable Functions +//! - [`set_admin_address`][`Pallet::set_admin_address`] - sets an address as the the admin. +//! - [`stake`][`Pallet::stake`] - stakes the amount of native tokens. +//! - [`unstake`][`Pallet::unstake`] - unstakes all stakes. +//! - [`sponsor_collection`][`Pallet::sponsor_collection`] - sets the pallet to be the sponsor for the collection. +//! - [`stop_sponsoring_collection`][`Pallet::stop_sponsoring_collection`] - removes the pallet as the sponsor for the collection. +//! - [`sponsor_contract`][`Pallet::sponsor_contract`] - sets the pallet to be the sponsor for the contract. +//! - [`stop_sponsoring_contract`][`Pallet::stop_sponsoring_contract`] - removes the pallet as the sponsor for the contract. +//! - [`payout_stakers`][`Pallet::payout_stakers`] - recalculates interest for the specified number of stakers. //! // #![recursion_limit = "1024"] @@ -95,10 +116,10 @@ /// Type for interacting with conrtacts type ContractHandler: ContractHandler; - /// ID for treasury + /// `AccountId` for treasury type TreasuryAccountId: Get; - /// The app's pallet id, used for deriving its sovereign account ID. + /// The app's pallet id, used for deriving its sovereign account address. #[pallet::constant] type PalletId: Get; @@ -138,7 +159,7 @@ /// Staking recalculation was performed /// /// # Arguments - /// * AccountId: ID of the staker. + /// * AccountId: account of the staker. /// * Balance : recalculation base /// * Balance : total income StakingRecalculation( @@ -153,21 +174,21 @@ /// Staking was performed /// /// # Arguments - /// * AccountId: ID of the staker + /// * AccountId: account of the staker /// * Balance : staking amount Stake(T::AccountId, BalanceOf), /// Unstaking was performed /// /// # Arguments - /// * AccountId: ID of the staker + /// * AccountId: account of the staker /// * Balance : unstaking amount Unstake(T::AccountId, BalanceOf), /// The admin was set /// /// # Arguments - /// * AccountId: ID of the admin + /// * AccountId: account address of the admin SetAdmin(T::AccountId), } @@ -187,13 +208,20 @@ IncorrectLockedBalanceOperation, } + /// Stores the total staked amount. #[pallet::storage] pub type TotalStaked = StorageValue, QueryKind = ValueQuery>; + /// Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`. #[pallet::storage] pub type Admin = StorageValue; - /// Amount of tokens staked by account in the blocknumber. + /// Stores the amount of tokens staked by account in the blocknumber. + /// + /// * **Key1** - Staker account. + /// * **Key2** - Relay block number when the stake was made. + /// * **(Balance, BlockNumber)** - Balance of the stake. + /// The number of the relay block in which we must perform the interest recalculation #[pallet::storage] pub type Staked = StorageNMap< Key = ( @@ -203,11 +231,19 @@ Value = (BalanceOf, T::BlockNumber), QueryKind = ValueQuery, >; - /// Amount of stakes for an Account + + /// Stores amount of stakes for an `Account`. + /// + /// * **Key** - Staker account. + /// * **Value** - Amount of stakes. #[pallet::storage] pub type StakesPerAccount = StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>; + /// Stores amount of stakes for an `Account`. + /// + /// * **Key** - Staker account. + /// * **Value** - Amount of stakes. #[pallet::storage] pub type PendingUnstake = StorageMap< _, @@ -252,6 +288,15 @@ T::BlockNumber: From + Into, <::Currency as Currency>::Balance: Sum + From, { + /// Sets an address as the the admin. + /// + /// # Permissions + /// + /// * Sudo + /// + /// # Arguments + /// + /// * `admin`: account of the new admin. #[pallet::weight(T::WeightInfo::set_admin_address())] pub fn set_admin_address(origin: OriginFor, admin: T::CrossAccountId) -> DispatchResult { ensure_root(origin)?; @@ -263,6 +308,13 @@ Ok(()) } + /// Stakes the amount of native tokens. + /// Sets `amount` to the locked state. + /// The maximum number of stakes for a staker is 10. + /// + /// # Arguments + /// + /// * `amount`: in native tokens. #[pallet::weight(T::WeightInfo::stake())] pub fn stake(staker: OriginFor, amount: BalanceOf) -> DispatchResult { let staker_id = ensure_signed(staker)?; @@ -280,6 +332,7 @@ let balance = <::Currency as Currency>::free_balance(&staker_id); + // checks that we can lock `amount` on the `staker` account. <::Currency as Currency>::ensure_can_withdraw( &staker_id, amount, @@ -293,6 +346,8 @@ let block_number = T::RelayBlockNumberProvider::current_block_number(); + // Calculation of the number of recalculation periods, + // after how much the first interest calculation should be performed for the stake let recalculate_after_interval: T::BlockNumber = if block_number % T::RecalculationInterval::get() == 0u32.into() { 1u32.into() @@ -300,6 +355,8 @@ 2u32.into() }; + // Сalculation of the number of the relay block + // in which it is necessary to accrue remuneration for the stake. let recalc_block = (block_number / T::RecalculationInterval::get() + recalculate_after_interval) * T::RecalculationInterval::get(); @@ -327,12 +384,20 @@ Ok(()) } + /// Unstakes all stakes. + /// Moves the sum of all stakes to the `reserved` state. + /// After the end of `PendingInterval` this sum becomes completely + /// free for further use. #[pallet::weight(T::WeightInfo::unstake())] pub fn unstake(staker: OriginFor) -> DispatchResultWithPostInfo { let staker_id = ensure_signed(staker)?; + + // calculate block number where the sum would be free let block = >::block_number() + T::PendingInterval::get(); + let mut pendings = >::get(block); + // checks that we can do unreserve stakes in the block ensure!(!pendings.is_full(), Error::::PendingForBlockOverflow); let mut total_stakes = 0u64; @@ -371,6 +436,15 @@ Ok(None.into()) } + /// Sets the pallet to be the sponsor for the collection. + /// + /// # Permissions + /// + /// * Pallet admin + /// + /// # Arguments + /// + /// * `collection_id`: ID of the collection that will be sponsored by `pallet_id` #[pallet::weight(T::WeightInfo::sponsor_collection())] pub fn sponsor_collection( admin: OriginFor, @@ -384,6 +458,18 @@ T::CollectionHandler::set_sponsor(Self::account_id(), collection_id) } + + /// Removes the pallet as the sponsor for the collection. + /// Returns [`NoPermission`][`Error::NoPermission`] + /// if the pallet wasn't the sponsor. + /// + /// # Permissions + /// + /// * Pallet admin + /// + /// # Arguments + /// + /// * `collection_id`: ID of the collection that is sponsored by `pallet_id` #[pallet::weight(T::WeightInfo::stop_sponsoring_collection())] pub fn stop_sponsoring_collection( admin: OriginFor, @@ -404,6 +490,15 @@ T::CollectionHandler::remove_collection_sponsor(collection_id) } + /// Sets the pallet to be the sponsor for the contract. + /// + /// # Permissions + /// + /// * Pallet admin + /// + /// # Arguments + /// + /// * `contract_id`: the contract address that will be sponsored by `pallet_id` #[pallet::weight(T::WeightInfo::sponsor_contract())] pub fn sponsor_contract(admin: OriginFor, contract_id: H160) -> DispatchResult { let admin_id = ensure_signed(admin)?; @@ -419,6 +514,17 @@ ) } + /// Removes the pallet as the sponsor for the contract. + /// Returns [`NoPermission`][`Error::NoPermission`] + /// if the pallet wasn't the sponsor. + /// + /// # Permissions + /// + /// * Pallet admin + /// + /// # Arguments + /// + /// * `contract_id`: the contract address that is sponsored by `pallet_id` #[pallet::weight(T::WeightInfo::stop_sponsoring_contract())] pub fn stop_sponsoring_contract(admin: OriginFor, contract_id: H160) -> DispatchResult { let admin_id = ensure_signed(admin)?; @@ -437,6 +543,18 @@ T::ContractHandler::remove_contract_sponsor(contract_id) } + /// Recalculates interest for the specified number of stakers. + /// If all stakers are not recalculated, the next call of the extrinsic + /// will continue the recalculation, from those stakers for whom this + /// was not perform in last call. + /// + /// # Permissions + /// + /// * Pallet admin + /// + /// # Arguments + /// + /// * `stakers_number`: the number of stakers for which recalculation will be performed #[pallet::weight(T::WeightInfo::payout_stakers(stakers_number.unwrap_or(20) as u32))] pub fn payout_stakers(admin: OriginFor, stakers_number: Option) -> DispatchResult { let admin_id = ensure_signed(admin)?; @@ -446,63 +564,19 @@ Error::::NoPermission ); + // calculate the number of the current recalculation block, + // this is necessary in order to understand which stakers we should calculate interest let current_recalc_block = Self::get_current_recalc_block(T::RelayBlockNumberProvider::current_block_number()); + + // calculate the number of the next recalculation block, + // this value is set for the stakers to whom the recalculation will be performed let next_recalc_block = current_recalc_block + T::RecalculationInterval::get(); let mut storage_iterator = Self::get_next_calculated_key() .map_or(Staked::::iter(), |key| Staked::::iter_from(key)); NextCalculatedRecord::::set(None); - - // { - // let mut stakers_number = stakers_number.unwrap_or(20); - // let mut last_id = admin_id; - // let mut income_acc = BalanceOf::::default(); - // let mut amount_acc = BalanceOf::::default(); - - // while let Some(( - // (current_id, staked_block), - // (amount, next_recalc_block_for_stake), - // )) = storage_iterator.next() - // { - // if last_id != current_id { - // if income_acc != BalanceOf::::default() { - // >::transfer( - // &T::TreasuryAccountId::get(), - // &last_id, - // income_acc, - // ExistenceRequirement::KeepAlive, - // ) - // .and_then(|_| Self::add_lock_balance(&last_id, income_acc))?; - - // Self::deposit_event(Event::StakingRecalculation( - // last_id, amount, income_acc, - // )); - // } - - // if stakers_number == 0 { - // NextCalculatedRecord::::set(Some((current_id, staked_block))); - // break; - // } - // stakers_number -= 1; - // income_acc = BalanceOf::::default(); - // last_id = current_id; - // }; - // if current_recalc_block >= next_recalc_block_for_stake { - // Self::recalculate_and_insert_stake( - // &last_id, - // staked_block, - // next_recalc_block, - // amount, - // ((current_recalc_block - next_recalc_block_for_stake) - // / T::RecalculationInterval::get()) - // .into() + 1, - // &mut income_acc, - // ); - // } - // } - // } { let mut stakers_number = stakers_number.unwrap_or(20); @@ -510,6 +584,8 @@ let income_acc = RefCell::new(BalanceOf::::default()); let amount_acc = RefCell::new(BalanceOf::::default()); + // this closure is used to perform some of the actions if we break the loop because we reached the number of stakers for recalculation, + // but there were unrecalculated records in the storage. let flush_stake = || -> DispatchResult { if let Some(last_id) = &*last_id.borrow() { if !income_acc.borrow().is_zero() { @@ -578,10 +654,18 @@ } impl Pallet { + /// The account address of the app promotion pot. + /// + /// This actually does computation. If you need to keep using it, then make sure you cache the + /// value and only call this once. pub fn account_id() -> T::AccountId { T::PalletId::get().into_account_truncating() } + /// Unlocks the balance that was locked by the pallet. + /// + /// - `staker`: staker account. + /// - `amount`: amount of unlocked funds. fn unlock_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { let locked_balance = Self::get_locked_balance(staker) .map(|l| l.amount) @@ -598,6 +682,10 @@ Ok(()) } + /// Adds the balance to locked by the pallet. + /// + /// - `staker`: staker account. + /// - `amount`: amount of added locked funds. fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { Self::get_locked_balance(staker) .map_or(>::default(), |l| l.amount) @@ -606,6 +694,10 @@ .ok_or(ArithmeticError::Overflow.into()) } + /// Sets the new state of a balance locked by the pallet. + /// + /// - `staker`: staker account. + /// - `amount`: amount of locked funds. fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf) { if amount.is_zero() { >::remove_lock(LOCK_IDENTIFIER, &staker); @@ -619,6 +711,9 @@ } } + /// Returns the balance locked by the pallet for the staker. + /// + /// - `staker`: staker account. pub fn get_locked_balance( staker: impl EncodeLike, ) -> Option>> { @@ -627,6 +722,9 @@ .find(|l| l.id == LOCK_IDENTIFIER) } + /// Returns the total staked balance for the staker. + /// + /// - `staker`: staker account. pub fn total_staked_by_id(staker: impl EncodeLike) -> Option> { let staked = Staked::::iter_prefix((staker,)) .into_iter() @@ -640,6 +738,10 @@ } } + /// Returns all relay block numbers when stake was made, + /// the amount of the stake. + /// + /// - `staker`: staker account. pub fn total_staked_by_id_per_block( staker: impl EncodeLike, ) -> Option)>> { @@ -655,6 +757,9 @@ } } + /// Returns the total staked balance for the staker. + /// If `staker` is `None`, returns the total amount staked. + /// - `staker`: staker account. pub fn cross_id_total_staked(staker: Option) -> Option> { staker.map_or(Some(>::get()), |s| { Self::total_staked_by_id(s.as_sub()) @@ -667,6 +772,10 @@ // .unwrap_or_default() // } + /// Returns all relay block numbers when stake was made, + /// the amount of the stake. + /// + /// - `staker`: staker account. pub fn cross_id_total_staked_per_block( staker: T::CrossAccountId, ) -> Vec<(T::BlockNumber, BalanceOf)> { @@ -703,10 +812,6 @@ fn get_current_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber { (current_relay_block / T::RecalculationInterval::get()) * T::RecalculationInterval::get() } - - // fn get_next_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber { - // Self::get_current_recalc_block(current_relay_block) + T::RecalculationInterval::get() - // } fn get_next_calculated_key() -> Option> { Self::get_next_calculated_record().map(|key| Staked::::hashed_key_for(key)) @@ -717,6 +822,11 @@ where <::Currency as Currency>::Balance: Sum, { + /// Returns the amount reserved by the pending. + /// If `staker` is `None`, returns the total pending. + /// + /// -`staker`: staker account. + /// /// Since user funds are not transferred anywhere by staking, overflow protection is provided /// at the level of the associated type `Balance` of `Currency` trait. In order to overflow, /// the staker must have more funds on his account than the maximum set for `Balance` type. @@ -740,6 +850,10 @@ ) } + /// Returns all parachain block numbers when unreserve is expected, + /// the amount of the unreserved funds. + /// + /// - `staker`: staker account. pub fn cross_id_pending_unstake_per_block( staker: T::CrossAccountId, ) -> Vec<(T::BlockNumber, BalanceOf)> { --- a/pallets/app-promotion/src/types.rs +++ b/pallets/app-promotion/src/types.rs @@ -9,7 +9,10 @@ use sp_std::borrow::ToOwned; use pallet_evm_contract_helpers::{Pallet as EvmHelpersPallet, Config as EvmHelpersConfig}; +/// This trait was defined because `LockableCurrency` +/// has no way to know the state of the lock for an account. pub trait ExtendedLockableCurrency: LockableCurrency { + /// Returns lock balance for an account. Allows to determine the cause of the lock. fn locks(who: KArg) -> WeakBoundedVec, Self::MaxLocks> where KArg: EncodeLike; @@ -25,18 +28,28 @@ Self::locks(who) } } - +/// Trait for interacting with collections. pub trait CollectionHandler { type CollectionId; type AccountId; + /// Sets sponsor for a collection. + /// + /// - `sponsor_id`: the account of the sponsor-to-be. + /// - `collection_id`: ID of the modified collection. fn set_sponsor( sponsor_id: Self::AccountId, collection_id: Self::CollectionId, ) -> DispatchResult; + /// Removes sponsor for a collection. + /// + /// - `collection_id`: ID of the modified collection. fn remove_collection_sponsor(collection_id: Self::CollectionId) -> DispatchResult; + /// Retuns the current sponsor for a collection if one is set. + /// + /// - `collection_id`: ID of the collection. fn sponsor(collection_id: Self::CollectionId) -> Result, DispatchError>; } @@ -66,18 +79,28 @@ .map(|acc| acc.to_owned())) } } - +/// Trait for interacting with contracts. pub trait ContractHandler { type ContractId; type AccountId; + /// Sets sponsor for a contract. + /// + /// - `sponsor_id`: the account of the sponsor-to-be. + /// - `contract_address`: the address of the modified contract. fn set_sponsor( sponsor_id: Self::AccountId, contract_address: Self::ContractId, ) -> DispatchResult; + /// Removes sponsor for a contract. + /// + /// - `contract_address`: the address of the modified contract. fn remove_contract_sponsor(contract_address: Self::ContractId) -> DispatchResult; + /// Retuns the current sponsor for a contract if one is set. + /// + /// - `contract_address`: the contract address. fn sponsor( contract_address: Self::ContractId, ) -> Result, DispatchError>; --- a/pallets/common/Cargo.toml +++ b/pallets/common/Cargo.toml @@ -17,12 +17,12 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } evm-coder = { default-features = false, path = '../../crates/evm-coder' } ethereum = { version = "0.12.0", default-features = false } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } serde = { version = "1.0.130", default-features = false } scale-info = { version = "2.0.1", default-features = false, features = [ --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -20,6 +20,7 @@ solidity_interface, solidity, ToLog, types::*, execution::{Result, Error}, + weight, }; pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId}; use pallet_evm_coder_substrate::dispatch_to_evm; @@ -31,8 +32,12 @@ use alloc::format; use crate::{ - Pallet, CollectionHandle, Config, CollectionProperties, - eth::{convert_cross_account_to_uint256, convert_uint256_to_cross_account}, + Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf, + eth::{ + convert_cross_account_to_uint256, convert_uint256_to_cross_account, + convert_cross_account_to_tuple, + }, + weights::WeightInfo, }; /// Events for ethereum collection helper. @@ -69,6 +74,7 @@ /// /// @param key Property key. /// @param value Propery value. + #[weight(>::set_collection_properties(1))] fn set_collection_property( &mut self, caller: caller, @@ -88,7 +94,10 @@ /// Delete collection property. /// /// @param key Property key. + #[weight(>::delete_collection_properties(1))] fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> { + self.consume_store_reads_and_writes(1, 1)?; + let caller = T::CrossAccountId::from_eth(caller); let key = >::from(key) .try_into() @@ -120,6 +129,8 @@ /// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract. fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let sponsor = T::CrossAccountId::from_eth(sponsor); @@ -138,6 +149,8 @@ caller: caller, sponsor: uint256, ) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let sponsor = convert_uint256_to_cross_account::(sponsor); @@ -146,7 +159,7 @@ save(self) } - // /// Whether there is a pending sponsor. + /// Whether there is a pending sponsor. fn has_collection_pending_sponsor(&self) -> Result { Ok(matches!( self.collection.sponsorship, @@ -158,6 +171,8 @@ /// /// @dev After setting the sponsor for the collection, it must be confirmed with this function. fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result { + self.consume_store_writes(1)?; + let caller = T::CrossAccountId::from_eth(caller); if !self .confirm_sponsorship(caller.as_sub()) @@ -170,6 +185,7 @@ /// Remove collection sponsor. fn remove_collection_sponsor(&mut self, caller: caller) -> Result { + self.consume_store_reads_and_writes(1, 1)?; check_is_owner_or_admin(caller, self)?; self.remove_sponsor().map_err(dispatch_to_evm::)?; save(self) @@ -206,6 +222,8 @@ /// @param value Value of the limit. #[solidity(rename_selector = "setCollectionLimit")] fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let mut limits = self.limits.clone(); @@ -249,6 +267,8 @@ /// @param value Value of the limit. #[solidity(rename_selector = "setCollectionLimit")] fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let mut limits = self.limits.clone(); @@ -275,7 +295,7 @@ } /// Get contract address. - fn contract_address(&self, _caller: caller) -> Result
{ + fn contract_address(&self) -> Result
{ Ok(crate::eth::collection_id_to_address(self.id)) } @@ -286,6 +306,8 @@ caller: caller, new_admin: uint256, ) -> Result { + self.consume_store_writes(2)?; + let caller = T::CrossAccountId::from_eth(caller); let new_admin = convert_uint256_to_cross_account::(new_admin); >::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::)?; @@ -299,6 +321,8 @@ caller: caller, admin: uint256, ) -> Result { + self.consume_store_writes(2)?; + let caller = T::CrossAccountId::from_eth(caller); let admin = convert_uint256_to_cross_account::(admin); >::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::)?; @@ -308,6 +332,8 @@ /// Add collection admin. /// @param newAdmin Address of the added administrator. fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result { + self.consume_store_writes(2)?; + let caller = T::CrossAccountId::from_eth(caller); let new_admin = T::CrossAccountId::from_eth(new_admin); >::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::)?; @@ -318,6 +344,8 @@ /// /// @param admin Address of the removed administrator. fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result { + self.consume_store_writes(2)?; + let caller = T::CrossAccountId::from_eth(caller); let admin = T::CrossAccountId::from_eth(admin); >::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::)?; @@ -329,6 +357,8 @@ /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled' #[solidity(rename_selector = "setCollectionNesting")] fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let mut permissions = self.collection.permissions.clone(); @@ -358,6 +388,8 @@ enable: bool, collections: Vec
, ) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + if collections.is_empty() { return Err("no addresses provided".into()); } @@ -401,6 +433,8 @@ /// 0 for Normal /// 1 for AllowList fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let permissions = CollectionPermissions { access: Some(match mode { @@ -420,30 +454,78 @@ save(self) } + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + fn allowed(&self, user: address) -> Result { + Ok(Pallet::::allowed( + self.id, + T::CrossAccountId::from_eth(user), + )) + } + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result { + self.consume_store_writes(1)?; + let caller = T::CrossAccountId::from_eth(caller); let user = T::CrossAccountId::from_eth(user); >::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::)?; Ok(()) } + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + fn add_to_collection_allow_list_substrate( + &mut self, + caller: caller, + user: uint256, + ) -> Result { + self.consume_store_writes(1)?; + + let caller = T::CrossAccountId::from_eth(caller); + let user = convert_uint256_to_cross_account::(user); + Pallet::::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::)?; + Ok(()) + } + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result { + self.consume_store_writes(1)?; + let caller = T::CrossAccountId::from_eth(caller); let user = T::CrossAccountId::from_eth(user); >::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::)?; Ok(()) } + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + fn remove_from_collection_allow_list_substrate( + &mut self, + caller: caller, + user: uint256, + ) -> Result { + self.consume_store_writes(1)?; + + let caller = T::CrossAccountId::from_eth(caller); + let user = convert_uint256_to_cross_account::(user); + Pallet::::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::)?; + Ok(()) + } + /// Switch permission for minting. /// /// @param mode Enable if "true". fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result { + self.consume_store_reads_and_writes(1, 1)?; + check_is_owner_or_admin(caller, self)?; let permissions = CollectionPermissions { mint_mode: Some(mode), @@ -481,7 +563,7 @@ /// Returns collection type /// /// @return `Fungible` or `NFT` or `ReFungible` - fn unique_collection_type(&mut self) -> Result { + fn unique_collection_type(&self) -> Result { let mode = match self.collection.mode { CollectionMode::Fungible(_) => "Fungible", CollectionMode::NFT => "NFT", @@ -490,11 +572,23 @@ Ok(mode.into()) } + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + fn collection_owner(&self) -> Result<(address, uint256)> { + Ok(convert_cross_account_to_tuple::( + &T::CrossAccountId::from_sub(self.owner.clone()), + )) + } + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner /// @param newOwner new owner account fn set_owner(&mut self, caller: caller, new_owner: address) -> Result { + self.consume_store_writes(1)?; + let caller = T::CrossAccountId::from_eth(caller); let new_owner = T::CrossAccountId::from_eth(new_owner); self.set_owner_internal(caller, new_owner) @@ -506,13 +600,25 @@ /// @dev Owner can be changed only by current owner /// @param newOwner new owner substrate account fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result { + self.consume_store_writes(1)?; + let caller = T::CrossAccountId::from_eth(caller); let new_owner = convert_uint256_to_cross_account::(new_owner); self.set_owner_internal(caller, new_owner) .map_err(dispatch_to_evm::) } + + // TODO: need implement AbiWriter for &Vec + // fn collection_admins(&self) -> Result> { + // let result = pallet_common::IsAdmin::::iter_prefix((self.id,)) + // .map(|(admin, _)| pallet_common::eth::convert_cross_account_to_tuple::(&admin)) + // .collect(); + // Ok(result) + // } } +/// ### Note +/// Do not forget to add: `self.consume_store_reads(1)?;` fn check_is_owner_or_admin( caller: caller, collection: &CollectionHandle, @@ -524,9 +630,9 @@ Ok(caller) } +/// ### Note +/// Do not forget to add: `self.consume_store_writes(1)?;` fn save(collection: &CollectionHandle) -> Result { - // TODO possibly delete for the lack of transaction - collection.consume_store_writes(1)?; collection .check_is_internal() .map_err(dispatch_to_evm::)?; --- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -16,7 +16,7 @@ //! The module contains a number of functions for converting and checking ethereum identifiers. -use evm_coder::types::uint256; +use evm_coder::types::{uint256, address}; pub use pallet_evm::account::{Config, CrossAccountId}; use sp_core::H160; use up_data_structs::CollectionId; @@ -69,3 +69,19 @@ let account_id = T::AccountId::from(new_admin_arr); T::CrossAccountId::from_sub(account_id) } + +/// Convert `CrossAccountId` to `(address, uint256)`. +pub fn convert_cross_account_to_tuple( + cross_account_id: &T::CrossAccountId, +) -> (address, uint256) +where + T::AccountId: AsRef<[u8; 32]>, +{ + if cross_account_id.is_canonical_substrate() { + let sub = convert_cross_account_to_uint256::(cross_account_id); + (Default::default(), sub) + } else { + let eth = *cross_account_id.as_eth(); + (eth, Default::default()) + } +} --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -112,7 +112,6 @@ RmrkBoundedTheme, RmrkNftChild, CollectionPermissions, - SchemaVersion, }; pub use pallet::*; @@ -202,6 +201,21 @@ )) } + /// Consume gas for reading and writing. + pub fn consume_store_reads_and_writes( + &self, + reads: u64, + writes: u64, + ) -> evm_coder::execution::Result<()> { + let weight = ::DbWeight::get(); + let reads = weight.read.saturating_mul(reads); + let writes = weight.read.saturating_mul(writes); + self.recorder + .consume_gas(T::GasWeightMapping::weight_to_gas( + reads.saturating_add(writes), + )) + } + /// Save collection to storage. pub fn save(&self) -> DispatchResult { >::insert(self.id, &self.collection); @@ -310,6 +324,8 @@ } /// Changes collection owner to another account + /// #### Store read/writes + /// 1 writes fn set_owner_internal( &mut self, caller: T::CrossAccountId, @@ -1292,6 +1308,8 @@ } /// Toggle `user` participation in the `collection`'s allow list. + /// #### Store read/writes + /// 1 writes pub fn toggle_allowlist( collection: &CollectionHandle, sender: &T::CrossAccountId, @@ -1312,6 +1330,8 @@ } /// Toggle `user` participation in the `collection`'s admin list. + /// #### Store read/writes + /// 2 writes pub fn toggle_admin( collection: &CollectionHandle, sender: &T::CrossAccountId, --- a/pallets/configuration/Cargo.toml +++ b/pallets/configuration/Cargo.toml @@ -16,7 +16,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-arithmetic = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } smallvec = "1.6.1" [features] --- a/pallets/evm-coder-substrate/Cargo.toml +++ b/pallets/evm-coder-substrate/Cargo.toml @@ -12,8 +12,8 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } ethereum = { version = "0.12.0", default-features = false } evm-coder = { default-features = false, path = "../../crates/evm-coder" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } --- a/pallets/evm-contract-helpers/CHANGELOG.md +++ b/pallets/evm-contract-helpers/CHANGELOG.md @@ -2,29 +2,35 @@ All notable changes to this project will be documented in this file. +## [v0.3.0] 2022-09-05 + +### Added + +- Methods `force_set_sponsor` , `force_remove_sponsor` to be able to administer sponsorships with other pallets. Added to implement `AppPromotion` pallet logic. + ## [v0.2.0] - 2022-08-19 ### Added - - Set arbitrary evm address as contract sponsor. - - Ability to remove current sponsor. +- Set arbitrary evm address as contract sponsor. +- Ability to remove current sponsor. ### Removed - - Remove methods - + sponsoring_enabled - + toggle_sponsoring - ### Changed +- Remove methods + - sponsoring_enabled + - toggle_sponsoring - - Change `toggle_sponsoring` to `self_sponsored_enable`. +### Changed +- Change `toggle_sponsoring` to `self_sponsored_enable`. ## [v0.1.2] 2022-08-16 ### Other changes -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a +- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 +- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b \ No newline at end of file +- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b --- a/pallets/evm-contract-helpers/Cargo.toml +++ b/pallets/evm-contract-helpers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-evm-contract-helpers" -version = "0.2.0" +version = "0.3.0" license = "GPLv3" edition = "2021" @@ -19,14 +19,15 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } # Unique -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } # Locals evm-coder = { default-features = false, path = '../../crates/evm-coder' } pallet-common = { default-features = false, path = '../../pallets/common' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } +pallet-evm-transaction-payment = { default-features = false, path = '../../pallets/evm-transaction-payment' } up-data-structs = { default-features = false, path = '../../primitives/data-structs', features = [ 'serde1', ] } --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -20,16 +20,17 @@ use evm_coder::{ abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog, }; -use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm}; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle, account::CrossAccountId, }; -use sp_core::H160; +use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm}; +use pallet_evm_transaction_payment::CallContext; +use sp_core::{H160, U256}; use up_data_structs::SponsorshipState; use crate::{ - AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT, - Sponsoring, + AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit, + SponsoringRateLimit, SponsoringModeT, Sponsoring, }; use frame_support::traits::Get; use up_sponsorship::SponsorshipHandler; @@ -171,14 +172,9 @@ fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> { let sponsor = Pallet::::get_sponsor(contract_address).ok_or("Contract has no sponsor")?; - let result: (address, uint256) = if sponsor.is_canonical_substrate() { - let sponsor = pallet_common::eth::convert_cross_account_to_uint256::(&sponsor); - (Default::default(), sponsor) - } else { - let sponsor = *sponsor.as_eth(); - (sponsor, Default::default()) - }; - Ok(result) + Ok(pallet_common::eth::convert_cross_account_to_tuple::( + &sponsor, + )) } /// Check tat contract has confirmed sponsor. @@ -222,9 +218,11 @@ } /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring mode of + /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result { + self.recorder().consume_sload()?; + Ok(>::get(contract_address) .try_into() .map_err(|_| "rate limit > u32::MAX")?) @@ -250,6 +248,37 @@ Ok(()) } + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + fn set_sponsoring_fee_limit( + &mut self, + caller: caller, + contract_address: address, + fee_limit: uint256, + ) -> Result { + self.recorder().consume_sload()?; + self.recorder().consume_sstore()?; + + >::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::)?; + >::set_sponsoring_fee_limit(contract_address, fee_limit.into()) + .map_err(dispatch_to_evm::)?; + Ok(()) + } + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + fn get_sponsoring_fee_limit(&self, contract_address: address) -> Result { + self.recorder().consume_sload()?; + + Ok(get_sponsoring_fee_limit::(contract_address)) + } + /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included /// @param contractAddress Contract to check allowlist of @@ -363,23 +392,26 @@ /// Bridge to pallet-sponsoring pub struct HelpersContractSponsoring(PhantomData<*const T>); -impl SponsorshipHandler)> +impl SponsorshipHandler for HelpersContractSponsoring { - fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec)) -> Option { - let (contract_address, _) = call; - let mode = >::sponsoring_mode(*contract_address); + fn get_sponsor( + who: &T::CrossAccountId, + call_context: &CallContext, + ) -> Option { + let contract_address = call_context.contract_address; + let mode = >::sponsoring_mode(contract_address); if mode == SponsoringModeT::Disabled { return None; } - let sponsor = match >::get_sponsor(*contract_address) { + let sponsor = match >::get_sponsor(contract_address) { Some(sponsor) => sponsor, None => return None, }; if mode == SponsoringModeT::Allowlisted - && !>::allowed(*contract_address, *who.as_eth()) + && !>::allowed(contract_address, *who.as_eth()) { return None; } @@ -394,11 +426,24 @@ } } + let sponsored_fee_limit = get_sponsoring_fee_limit::(contract_address); + + if call_context.max_fee > sponsored_fee_limit { + return None; + } + >::insert(contract_address, who.as_eth(), block_number); Some(sponsor) } } +fn get_sponsoring_fee_limit(contract_address: address) -> uint256 { + >::get(contract_address) + .get(&0xffffffff) + .cloned() + .unwrap_or(U256::MAX) +} + generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true); generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false); --- a/pallets/evm-contract-helpers/src/lib.rs +++ b/pallets/evm-contract-helpers/src/lib.rs @@ -22,15 +22,19 @@ pub use pallet::*; pub use eth::*; use scale_info::TypeInfo; +use frame_support::storage::bounded_btree_map::BoundedBTreeMap; pub mod eth; +/// Maximum number of methods per contract that could have fee limit +pub const MAX_FEE_LIMITED_METHODS: u32 = 5; + #[frame_support::pallet] pub mod pallet { pub use super::*; use crate::eth::ContractHelpersEvents; use frame_support::pallet_prelude::*; use pallet_evm_coder_substrate::DispatchResult; - use sp_core::H160; + use sp_core::{H160, U256}; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use up_data_structs::SponsorshipState; use evm_coder::ToLog; @@ -57,6 +61,9 @@ /// No pending sponsor for contract. NoPendingSponsor, + + /// Number of methods that sponsored limit is defined for exceeds maximum. + TooManyMethodsHaveSponsoredLimit, } #[pallet::pallet] @@ -118,6 +125,14 @@ /// * **Key2** - sponsored user address. /// * **Value** - last sponsored block number. #[pallet::storage] + pub(super) type SponsoringFeeLimit = StorageMap< + Hasher = Twox128, + Key = H160, + Value = BoundedBTreeMap>, + QueryKind = ValueQuery, + >; + + #[pallet::storage] pub(super) type SponsorBasket = StorageDoubleMap< Hasher1 = Twox128, Key1 = H160, @@ -216,9 +231,10 @@ Ok(()) } - /// TO-DO - /// + /// Force set `sponsor` for `contract`. /// + /// Differs from `set_sponsor` in that confirmation + /// from the sponsor is not required. pub fn force_set_sponsor( contract_address: H160, sponsor: &T::CrossAccountId, @@ -269,9 +285,10 @@ Self::force_remove_sponsor(contract_address) } - /// TO-DO - /// + /// Force remove `sponsor` for `contract`. /// + /// Differs from `remove_sponsor` in that + /// it doesn't require consent from the `owner` of the contract. pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult { Sponsoring::::remove(contract_address); @@ -353,6 +370,16 @@ >::insert(contract, rate_limit); } + /// Set maximum for gas limit of transaction + pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult { + >::try_mutate(contract, |limits_map| { + limits_map + .try_insert(0xffffffff, fee_limit) + .map_err(|_| >::TooManyMethodsHaveSponsoredLimit) + })?; + Ok(()) + } + /// Is user added to allowlist, or he is owner of specified contract pub fn allowed(contract: H160, user: H160) -> bool { >::get(&contract, &user) || >::get(&contract) == user --- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol +++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol @@ -32,7 +32,7 @@ } /// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0xd77fab70 +/// @dev the ERC-165 identifier for this interface is 0x172cb4fb contract ContractHelpers is Dummy, ERC165, ContractHelpersEvents { /// Get user, which deployed specified contract /// @dev May return zero address in case if contract is deployed @@ -171,7 +171,7 @@ } /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring mode of + /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions /// @dev EVM selector for this function is: 0x610cfabd, /// or in textual repr: getSponsoringRateLimit(address) @@ -203,6 +203,40 @@ dummy = 0; } + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x03aed665, + /// or in textual repr: setSponsoringFeeLimit(address,uint256) + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) + public + { + require(false, stub_error); + contractAddress; + feeLimit; + dummy = 0; + } + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + /// @dev EVM selector for this function is: 0xc3fdc9ee, + /// or in textual repr: getSponsoringFeeLimit(address) + function getSponsoringFeeLimit(address contractAddress) + public + view + returns (uint256) + { + require(false, stub_error); + contractAddress; + dummy; + return 0; + } + /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included /// @param contractAddress Contract to check allowlist of --- a/pallets/evm-migration/Cargo.toml +++ b/pallets/evm-migration/Cargo.toml @@ -15,8 +15,8 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [dependencies.codec] default-features = false --- a/pallets/evm-transaction-payment/Cargo.toml +++ b/pallets/evm-transaction-payment/Cargo.toml @@ -14,11 +14,11 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [dependencies.codec] default-features = false --- a/pallets/evm-transaction-payment/src/lib.rs +++ b/pallets/evm-transaction-payment/src/lib.rs @@ -22,8 +22,8 @@ use fp_evm::WithdrawReason; use frame_support::traits::IsSubType; pub use pallet::*; -use pallet_evm::{EnsureAddressOrigin, account::CrossAccountId}; -use sp_core::H160; +use pallet_evm::{account::CrossAccountId, EnsureAddressOrigin}; +use sp_core::{H160, U256}; use sp_runtime::{TransactionOutcome, DispatchError}; use up_sponsorship::SponsorshipHandler; @@ -33,10 +33,20 @@ use sp_std::vec::Vec; + /// Contains call data + pub struct CallContext { + /// Contract address + pub contract_address: H160, + /// Transaction data + pub input: Vec, + /// Max fee for transaction - gasLimit * gasPrice + pub max_fee: U256, + } + #[pallet::config] pub trait Config: frame_system::Config + pallet_evm::account::Config { /// Loosly-coupled handlers for evm call sponsoring - type EvmSponsorshipHandler: SponsorshipHandler)>; + type EvmSponsorshipHandler: SponsorshipHandler; } #[pallet::pallet] @@ -47,11 +57,20 @@ /// Implements [`fp_evm::TransactionValidityHack`], which provides sponsor address to pallet-evm pub struct TransactionValidityHack(PhantomData<*const T>); impl fp_evm::TransactionValidityHack for TransactionValidityHack { - fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option { + fn who_pays_fee( + origin: H160, + max_fee: U256, + reason: &WithdrawReason, + ) -> Option { match reason { WithdrawReason::Call { target, input } => { let origin_sub = T::CrossAccountId::from_eth(origin); - T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &(*target, input.clone())) + let call_context = CallContext { + contract_address: *target, + input: input.clone(), + max_fee, + }; + T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &call_context) } _ => None, } @@ -71,6 +90,8 @@ source, target, input, + gas_limit, + max_fee_per_gas, .. } => { let _ = T::CallOrigin::ensure_address_origin( @@ -79,11 +100,17 @@ ) .ok()?; let who = T::CrossAccountId::from_sub(who.clone()); + let max_fee = max_fee_per_gas.saturating_mul((*gas_limit).into()); + let call_context = CallContext { + contract_address: *target, + input: input.clone(), + max_fee, + }; // Effects from EvmSponsorshipHandler are applied by pallet_evm::runner // TODO: Should we implement simulation mode (test, but do not apply effects) in `up-sponsorship`? let sponsor = frame_support::storage::with_transaction(|| { TransactionOutcome::Rollback(Ok::<_, DispatchError>( - T::EvmSponsorshipHandler::get_sponsor(&who, &(*target, input.clone())), + T::EvmSponsorshipHandler::get_sponsor(&who, &call_context), )) }) // FIXME: it may fail with DispatchError in case of depth limit --- a/pallets/fungible/Cargo.toml +++ b/pallets/fungible/Cargo.toml @@ -23,7 +23,7 @@ pallet-evm-coder-substrate = { default-features = false, path = '../evm-coder-substrate' } ethereum = { version = "0.12.0", default-features = false } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } scale-info = { version = "2.0.1", default-features = false, features = [ "derive", ] } --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -22,7 +22,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xe54be640 +/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -255,6 +255,18 @@ dummy = 0; } + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0xd63a8e11, + /// or in textual repr: allowed(address) + function allowed(address user) public view returns (bool) { + require(false, stub_error); + user; + dummy; + return false; + } + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. @@ -266,6 +278,17 @@ dummy = 0; } + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xd06ad267, + /// or in textual repr: addToCollectionAllowListSubstrate(uint256) + function addToCollectionAllowListSubstrate(uint256 user) public { + require(false, stub_error); + user; + dummy = 0; + } + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. @@ -277,6 +300,17 @@ dummy = 0; } + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xa31913ed, + /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256) + function removeFromCollectionAllowListSubstrate(uint256 user) public { + require(false, stub_error); + user; + dummy = 0; + } + /// Switch permission for minting. /// /// @param mode Enable if "true". @@ -325,6 +359,18 @@ return ""; } + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() public view returns (Tuple6 memory) { + require(false, stub_error); + dummy; + return Tuple6(0x0000000000000000000000000000000000000000, 0); + } + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner --- a/pallets/nonfungible/Cargo.toml +++ b/pallets/nonfungible/Cargo.toml @@ -16,7 +16,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { default-features = false, path = '../common' } pallet-structure = { default-features = false, path = '../structure' } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -99,7 +99,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xe54be640 +/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -332,6 +332,18 @@ dummy = 0; } + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0xd63a8e11, + /// or in textual repr: allowed(address) + function allowed(address user) public view returns (bool) { + require(false, stub_error); + user; + dummy; + return false; + } + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. @@ -343,6 +355,17 @@ dummy = 0; } + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xd06ad267, + /// or in textual repr: addToCollectionAllowListSubstrate(uint256) + function addToCollectionAllowListSubstrate(uint256 user) public { + require(false, stub_error); + user; + dummy = 0; + } + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. @@ -354,6 +377,17 @@ dummy = 0; } + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xa31913ed, + /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256) + function removeFromCollectionAllowListSubstrate(uint256 user) public { + require(false, stub_error); + user; + dummy = 0; + } + /// Switch permission for minting. /// /// @param mode Enable if "true". @@ -402,6 +436,18 @@ return ""; } + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() public view returns (Tuple17 memory) { + require(false, stub_error); + dummy; + return Tuple17(0x0000000000000000000000000000000000000000, 0); + } + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner --- a/pallets/proxy-rmrk-core/Cargo.toml +++ b/pallets/proxy-rmrk-core/Cargo.toml @@ -20,7 +20,7 @@ pallet-nonfungible = { default-features = false, path = "../../pallets/nonfungible" } pallet-structure = { default-features = false, path = "../../pallets/structure" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } rmrk-traits = { default-features = false, path = "../../primitives/rmrk-traits" } scale-info = { version = "2.0.1", default-features = false, features = [ --- a/pallets/proxy-rmrk-equip/Cargo.toml +++ b/pallets/proxy-rmrk-equip/Cargo.toml @@ -19,7 +19,7 @@ pallet-common = { default-features = false, path = '../common' } pallet-nonfungible = { default-features = false, path = "../../pallets/nonfungible" } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } rmrk-traits = { default-features = false, path = "../../primitives/rmrk-traits" } scale-info = { version = "2.0.1", default-features = false, features = [ --- a/pallets/refungible/Cargo.toml +++ b/pallets/refungible/Cargo.toml @@ -16,7 +16,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } pallet-common = { default-features = false, path = '../common' } --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -99,7 +99,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xe54be640 +/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 contract Collection is Dummy, ERC165 { /// Set collection property. /// @@ -332,6 +332,18 @@ dummy = 0; } + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0xd63a8e11, + /// or in textual repr: allowed(address) + function allowed(address user) public view returns (bool) { + require(false, stub_error); + user; + dummy; + return false; + } + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. @@ -343,6 +355,17 @@ dummy = 0; } + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xd06ad267, + /// or in textual repr: addToCollectionAllowListSubstrate(uint256) + function addToCollectionAllowListSubstrate(uint256 user) public { + require(false, stub_error); + user; + dummy = 0; + } + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. @@ -354,6 +377,17 @@ dummy = 0; } + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xa31913ed, + /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256) + function removeFromCollectionAllowListSubstrate(uint256 user) public { + require(false, stub_error); + user; + dummy = 0; + } + /// Switch permission for minting. /// /// @param mode Enable if "true". @@ -402,6 +436,18 @@ return ""; } + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() public view returns (Tuple17 memory) { + require(false, stub_error); + dummy; + return Tuple17(0x0000000000000000000000000000000000000000, 0); + } + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner --- a/pallets/structure/Cargo.toml +++ b/pallets/structure/Cargo.toml @@ -16,7 +16,7 @@ "derive", ] } up-data-structs = { path = "../../primitives/data-structs", default-features = false } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [features] default = ["std"] --- a/pallets/unique/CHANGELOG.md +++ b/pallets/unique/CHANGELOG.md @@ -4,7 +4,12 @@ -## [v0.1.4] 2022-09-5 +## [v0.2.0] 2022-09-13 + +### Changes +- Change **collectionHelper** method `createRefungibleCollection` to `createRFTCollection`, + +## [v0.1.4] 2022-09-05 ### Added --- a/pallets/unique/Cargo.toml +++ b/pallets/unique/Cargo.toml @@ -9,7 +9,7 @@ license = 'GPLv3' name = 'pallet-unique' repository = 'https://github.com/UniqueNetwork/unique-chain' -version = "0.1.4" +version = "0.2.0" [package.metadata.docs.rs] targets = ['x86_64-unknown-linux-gnu'] @@ -98,7 +98,7 @@ scale-info = { version = "2.0.1", default-features = false, features = [ "derive", ] } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { default-features = false, path = "../common" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' } --- a/pallets/unique/src/eth/mod.rs +++ b/pallets/unique/src/eth/mod.rs @@ -21,25 +21,23 @@ use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*}; use frame_support::traits::Get; use pallet_common::{ - CollectionById, CollectionHandle, + CollectionById, dispatch::CollectionDispatch, erc::{ CollectionHelpersEvents, static_property::{key, value as property_value}, }, - Pallet as PalletCommon, }; use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult}; -use pallet_evm_coder_substrate::dispatch_to_evm; use up_data_structs::{ CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData, - CollectionMode, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, + CollectionMode, PropertyValue, }; use crate::{Config, SelfWeightOf, weights::WeightInfo}; -use sp_std::{vec, vec::Vec}; +use sp_std::vec::Vec; use alloc::format; /// See [`CollectionHelpersCall`] @@ -245,6 +243,7 @@ } #[weight(>::create_collection())] + #[solidity(rename_selector = "createRFTCollection")] fn create_refungible_collection( &mut self, caller: caller, --- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol +++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol @@ -10,11 +10,7 @@ } contract ERC165 is Dummy { - function supportsInterface(bytes4 interfaceID) - external - view - returns (bool) - { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { require(false, stub_error); interfaceID; return true; @@ -23,14 +19,11 @@ /// @dev inlined interface contract CollectionHelpersEvents { - event CollectionCreated( - address indexed owner, - address indexed collectionId - ); + event CollectionCreated(address indexed owner, address indexed collectionId); } /// @title Contract, which allows users to operate with collections -/// @dev the ERC-165 identifier for this interface is 0x675f3074 +/// @dev the ERC-165 identifier for this interface is 0x88ee8ef1 contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { /// Create an NFT collection /// @param name Name of the collection @@ -69,9 +62,9 @@ return 0x0000000000000000000000000000000000000000; } - /// @dev EVM selector for this function is: 0x44a68ad5, - /// or in textual repr: createRefungibleCollection(string,string,string) - function createRefungibleCollection( + /// @dev EVM selector for this function is: 0xab173450, + /// or in textual repr: createRFTCollection(string,string,string) + function createRFTCollection( string memory name, string memory description, string memory tokenPrefix @@ -106,11 +99,7 @@ /// @return bool Does the collection exist? /// @dev EVM selector for this function is: 0xc3de1494, /// or in textual repr: isCollectionExist(address) - function isCollectionExist(address collectionAddress) - public - view - returns (bool) - { + function isCollectionExist(address collectionAddress) public view returns (bool) { require(false, stub_error); collectionAddress; dummy; --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -1105,6 +1105,15 @@ } impl Pallet { + /// Force set `sponsor` for `collection`. + /// + /// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation + /// from the `sponsor` is not required. + /// + /// # Arguments + /// + /// * `sponsor`: ID of the account of the sponsor-to-be. + /// * `collection_id`: ID of the modified collection. pub fn force_set_sponsor(sponsor: T::AccountId, collection_id: CollectionId) -> DispatchResult { let mut target_collection = >::try_get(collection_id)?; target_collection.check_is_internal()?; @@ -1125,6 +1134,14 @@ target_collection.save() } + /// Force remove `sponsor` for `collection`. + /// + /// Differs from `remove_sponsor` in that + /// it doesn't require consent from the `owner` of the collection. + /// + /// # Arguments + /// + /// * `collection_id`: ID of the modified collection. pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult { let mut target_collection = >::try_get(collection_id)?; target_collection.check_is_internal()?; --- a/primitives/app_promotion_rpc/Cargo.toml +++ b/primitives/app_promotion_rpc/Cargo.toml @@ -14,7 +14,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [features] default = ["std"] --- a/primitives/common/CHANGELOG.md +++ b/primitives/common/CHANGELOG.md @@ -1,4 +1,9 @@ +## [v0.9.27] 2022-09-08 + +### Added +- Relay block constants. In particular, it is necessary to add the `AppPromotion` pallet at runtime. + ## [v0.9.25] 2022-08-16 ### Other changes --- a/primitives/common/Cargo.toml +++ b/primitives/common/Cargo.toml @@ -48,9 +48,9 @@ [dependencies.fp-rpc] default-features = false git = "https://github.com/uniquenetwork/frontier" -branch = "unique-polkadot-v0.9.27" +branch = "unique-polkadot-v0.9.27-fee-limit" [dependencies.pallet-evm] default-features = false git = "https://github.com/uniquenetwork/frontier" -branch = "unique-polkadot-v0.9.27" +branch = "unique-polkadot-v0.9.27-fee-limit" --- a/primitives/data-structs/Cargo.toml +++ b/primitives/data-structs/Cargo.toml @@ -25,7 +25,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } derivative = { version = "2.2.0", features = ["use_core"] } struct-versioning = { path = "../../crates/struct-versioning" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } rmrk-traits = { default-features = false, path = "../rmrk-traits" } [features] --- a/primitives/rpc/Cargo.toml +++ b/primitives/rpc/Cargo.toml @@ -14,7 +14,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } [features] default = ["std"] --- a/runtime/common/config/sponsoring.rs +++ b/runtime/common/config/sponsoring.rs @@ -14,15 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -use frame_support::parameter_types; use crate::{ runtime_common::{sponsoring::UniqueSponsorshipHandler}, Runtime, }; -use up_common::{types::BlockNumber, constants::*}; +use frame_support::parameter_types; +use sp_core::U256; +use up_common::{constants::*, types::BlockNumber}; parameter_types! { pub const DefaultSponsoringRateLimit: BlockNumber = 1 * DAYS; + pub const DefaultSponsoringFeeLimit: U256 = U256::MAX; } type SponsorshipHandler = ( --- a/runtime/common/ethereum/sponsoring.rs +++ b/runtime/common/ethereum/sponsoring.rs @@ -16,27 +16,30 @@ //! Implements EVM sponsoring logic via TransactionValidityHack +use core::{convert::TryInto, marker::PhantomData}; use evm_coder::{Call, abi::AbiReader}; use pallet_common::{CollectionHandle, eth::map_eth_to_id}; -use sp_core::H160; +use pallet_evm::account::CrossAccountId; +use pallet_evm_transaction_payment::CallContext; +use pallet_nonfungible::{ + Config as NonfungibleConfig, + erc::{ + UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call, + TokenPropertiesCall, + }, +}; +use pallet_fungible::{ + Config as FungibleConfig, + erc::{UniqueFungibleCall, ERC20Call}, +}; +use pallet_refungible::Config as RefungibleConfig; +use pallet_unique::Config as UniqueConfig; use sp_std::prelude::*; +use up_data_structs::{CollectionMode, CreateItemData, CreateNftData, TokenId}; use up_sponsorship::SponsorshipHandler; -use core::marker::PhantomData; -use core::convert::TryInto; -use pallet_evm::account::CrossAccountId; -use up_data_structs::{TokenId, CreateItemData, CreateNftData, CollectionMode}; -use pallet_unique::Config as UniqueConfig; use crate::{Runtime, runtime_common::sponsoring::*}; -use pallet_nonfungible::erc::{ - UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call, TokenPropertiesCall, -}; -use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call}; -use pallet_fungible::Config as FungibleConfig; -use pallet_nonfungible::Config as NonfungibleConfig; -use pallet_refungible::Config as RefungibleConfig; - pub type EvmSponsorshipHandler = ( UniqueEthSponsorshipHandler, pallet_evm_contract_helpers::HelpersContractSponsoring, @@ -44,13 +47,16 @@ pub struct UniqueEthSponsorshipHandler(PhantomData<*const T>); impl - SponsorshipHandler)> for UniqueEthSponsorshipHandler + SponsorshipHandler for UniqueEthSponsorshipHandler { - fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec)) -> Option { - let collection_id = map_eth_to_id(&call.0)?; + fn get_sponsor( + who: &T::CrossAccountId, + call_context: &CallContext, + ) -> Option { + let collection_id = map_eth_to_id(&call_context.contract_address)?; let collection = >::new(collection_id)?; let sponsor = collection.sponsorship.sponsor()?.clone(); - let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?; + let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?; Some(T::CrossAccountId::from_sub(match &collection.mode { CollectionMode::NFT => { let call = >::parse(method_id, &mut reader).ok()??; --- a/runtime/opal/CHANGELOG.md +++ b/runtime/opal/CHANGELOG.md @@ -3,16 +3,23 @@ All notable changes to this project will be documented in this file. + +## [v0.9.27] 2022-09-08 + +### Added + +- `AppPromotion` pallet to runtime. + ## [v0.9.27] 2022-08-16 ### Bugfixes -- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08 +- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08 ### Other changes -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a +- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 +- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b +- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -417,7 +417,7 @@ up-rpc = { path = "../../primitives/rpc", default-features = false } app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} rmrk-rpc = { path = "../../primitives/rmrk-rpc", default-features = false } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-inflation = { path = '../../pallets/inflation', default-features = false } pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } @@ -436,13 +436,13 @@ pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } ################################################################################ # Build Dependencies --- a/runtime/quartz/CHANGELOG.md +++ b/runtime/quartz/CHANGELOG.md @@ -3,17 +3,23 @@ All notable changes to this project will be documented in this file. + +## [v0.9.27] 2022-09-08 + +### Added + +- `AppPromotion` pallet to runtime. + ## [v0.9.27] 2022-08-16 ### Bugfixes -- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08 +- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08 ### Other changes -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a +- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 +- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b - +- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -418,7 +418,7 @@ pallet-unique = { path = '../../pallets/unique', default-features = false } up-rpc = { path = "../../primitives/rpc", default-features = false } app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false} -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-inflation = { path = '../../pallets/inflation', default-features = false } pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false } up-data-structs = { path = '../../primitives/data-structs', default-features = false } @@ -437,13 +437,13 @@ pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } ################################################################################ # Build Dependencies --- a/runtime/tests/Cargo.toml +++ b/runtime/tests/Cargo.toml @@ -16,7 +16,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } @@ -25,8 +25,8 @@ pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } pallet-common = { path = '../../pallets/common' } pallet-structure = { path = '../../pallets/structure' } @@ -43,4 +43,4 @@ scale-info = "*" evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } --- a/runtime/unique/CHANGELOG.md +++ b/runtime/unique/CHANGELOG.md @@ -3,16 +3,23 @@ All notable changes to this project will be documented in this file. + +## [v0.9.27] 2022-09-08 + +### Added + +- `AppPromotion` pallet to runtime. + ## [v0.9.27] 2022-08-16 ### Bugfixes -- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08 +- Add missing config keys 74f532ac28dce15c15e7d576c074a58eba658c08 ### Other changes -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a +- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 +- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b +- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -430,14 +430,14 @@ pallet-evm-contract-helpers = { path = '../../pallets/evm-contract-helpers', default-features = false } pallet-evm-transaction-payment = { path = '../../pallets/evm-transaction-payment', default-features = false } pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } -pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } -fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" } +pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +pallet-base-fee = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } +fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27-fee-limit" } evm-coder = { default-features = false, path = '../../crates/evm-coder' } -up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = 'polkadot-v0.9.27' } +up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.27" } ################################################################################ # Build Dependencies --- a/tests/package.json +++ b/tests/package.json @@ -73,6 +73,7 @@ "testRemoveFromContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/removeFromContractAllowList.test.ts", "testSetContractSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setContractSponsoringRateLimit.test.ts", "testSetOffchainSchema": "mocha --timeout 9999999 -r ts-node/register ./**/setOffchainSchema.test.ts", + "testNextSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/nextSponsoring.test.ts", "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts", "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts", "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts", @@ -99,7 +100,7 @@ "dependencies": { "@polkadot/api": "9.2.2", "@polkadot/api-contract": "9.2.2", - "@polkadot/util-crypto": "10.1.1", + "@polkadot/util-crypto": "10.1.7", "bignumber.js": "^9.0.2", "chai-as-promised": "^7.1.1", "chai-like": "^1.1.1", --- a/tests/scripts/readyness.js +++ b/tests/scripts/readyness.js @@ -6,9 +6,9 @@ await api.isReadyOrError; const head = (await api.rpc.chain.getHeader()).number.toNumber(); + await api.disconnect(); if(head < 1) throw Error('No block #1'); - await api.disconnect(); } const sleep = time => { --- a/tests/src/addCollectionAdmin.test.ts +++ b/tests/src/addCollectionAdmin.test.ts @@ -15,121 +15,111 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {usingPlaygrounds} from './util/playgrounds'; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; -chai.use(chaiAsPromised); -const expect = chai.expect; +describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { + let donor: IKeyringPair; -let donor: IKeyringPair; - -before(async () => { - await usingPlaygrounds(async (_, privateKeyWrapper) => { - donor = privateKeyWrapper('//Alice'); + before(async () => { + await usingPlaygrounds(async (_, privateKeyWrapper) => { + donor = privateKeyWrapper('//Alice'); + }); }); -}); -describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { - it('Add collection admin.', async () => { - await usingPlaygrounds(async (helper) => { - const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + itSub('Add collection admin.', async ({helper}) => { + const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - const collection = await helper.collection.getData(collectionId); - expect(collection!.normalizedOwner!).to.be.equal(alice.address); + const collection = await helper.collection.getData(collectionId); + expect(collection!.normalizedOwner!).to.be.equal(helper.address.normalizeSubstrate(alice.address)); - await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); + await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address}); - const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - }); + const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); }); }); describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => { - it("Not owner can't add collection admin.", async () => { - await usingPlaygrounds(async (helper) => { - const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + let donor: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (_, privateKeyWrapper) => { + donor = privateKeyWrapper('//Alice'); + }); + }); - const collection = await helper.collection.getData(collectionId); - expect(collection?.normalizedOwner).to.be.equal(alice.address); + itSub("Not owner can't add collection admin.", async ({helper}) => { + const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - const changeAdminTxBob = async () => helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address}); - const changeAdminTxCharlie = async () => helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address}); - await expect(changeAdminTxCharlie()).to.be.rejected; - await expect(changeAdminTxBob()).to.be.rejected; + const collection = await helper.collection.getData(collectionId); + expect(collection?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address)); + + const changeAdminTxBob = async () => helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address}); + const changeAdminTxCharlie = async () => helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address}); + await expect(changeAdminTxCharlie()).to.be.rejectedWith(/common\.NoPermission/); + await expect(changeAdminTxBob()).to.be.rejectedWith(/common\.NoPermission/); - const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); - expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address}); - expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: bob.address}); - }); + const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId); + expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address}); + expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: bob.address}); }); - it("Admin can't add collection admin.", async () => { - await usingPlaygrounds(async (helper) => { - const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + itSub("Admin can't add collection admin.", async ({helper}) => { + const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.addAdmin(alice, {Substrate: bob.address}); - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - const changeAdminTxCharlie = async () => collection.addAdmin(bob, {Substrate: charlie.address}); - await expect(changeAdminTxCharlie()).to.be.rejected; + const changeAdminTxCharlie = async () => collection.addAdmin(bob, {Substrate: charlie.address}); + await expect(changeAdminTxCharlie()).to.be.rejectedWith(/common\.NoPermission/); - const adminListAfterAddNewAdmin = await collection.getAdmins(); - expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address}); - expect(adminListAfterAddNewAdmin).to.be.not.deep.contains({Substrate: charlie.address}); - }); + const adminListAfterAddNewAdmin = await collection.getAdmins(); + expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address}); + expect(adminListAfterAddNewAdmin).to.be.not.deep.contains({Substrate: charlie.address}); }); - it("Can't add collection admin of not existing collection.", async () => { - await usingPlaygrounds(async (helper) => { - const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - // tslint:disable-next-line: no-bitwise - const collectionId = (1 << 32) - 1; + itSub("Can't add collection admin of not existing collection.", async ({helper}) => { + const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collectionId = (1 << 32) - 1; - const addAdminTx = async () => helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - await expect(addAdminTx()).to.be.rejected; + const addAdminTx = async () => helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + await expect(addAdminTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - }); + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); }); - it("Can't add an admin to a destroyed collection.", async () => { - await usingPlaygrounds(async (helper) => { - const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + itSub("Can't add an admin to a destroyed collection.", async ({helper}) => { + const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - await collection.burn(alice); - const addAdminTx = async () => collection.addAdmin(alice, {Substrate: bob.address}); - await expect(addAdminTx()).to.be.rejected; + await collection.burn(alice); + const addAdminTx = async () => collection.addAdmin(alice, {Substrate: bob.address}); + await expect(addAdminTx()).to.be.rejectedWith(/common\.CollectionNotFound/); - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - }); + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); }); - it('Add an admin to a collection that has reached the maximum number of admins limit', async () => { - await usingPlaygrounds(async (helper) => { - const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor); - const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); + itSub('Add an admin to a collection that has reached the maximum number of admins limit', async ({helper}) => { + const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor); + const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'}); - const chainAdminLimit = (helper.api!.consts.common.collectionAdminsLimit as any).toNumber(); - expect(chainAdminLimit).to.be.equal(5); + const chainAdminLimit = (helper.api!.consts.common.collectionAdminsLimit as any).toNumber(); + expect(chainAdminLimit).to.be.equal(5); - for (let i = 0; i < chainAdminLimit; i++) { - await collection.addAdmin(alice, {Substrate: accounts[i].address}); - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address}); - } + for (let i = 0; i < chainAdminLimit; i++) { + await collection.addAdmin(alice, {Substrate: accounts[i].address}); + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address}); + } - const addExtraAdminTx = async () => collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address}); - await expect(addExtraAdminTx()).to.be.rejected; - }); + const addExtraAdminTx = async () => collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address}); + await expect(addExtraAdminTx()).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/); }); }); --- a/tests/src/approve.test.ts +++ b/tests/src/approve.test.ts @@ -15,26 +15,8 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import { - approveExpectFail, - approveExpectSuccess, - createCollectionExpectSuccess, - createItemExpectSuccess, -} from './util/helpers'; -import {usingPlaygrounds} from './util/playgrounds'; - -let donor: IKeyringPair; - -before(async () => { - await usingPlaygrounds(async (_, privateKey) => { - donor = privateKey('//Alice'); - }); -}); +import {expect, itSub, Pallets, usingPlaygrounds} from './util/playgrounds'; -chai.use(chaiAsPromised); -const expect = chai.expect; describe('Integration Test approve(spender, collection_id, item_id, amount):', () => { let alice: IKeyringPair; @@ -42,89 +24,76 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('[nft] Execute the extrinsic and check approvedList', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - }); + itSub('[nft] Execute the extrinsic and check approvedList', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; }); - it('[fungible] Execute the extrinsic and check approvedList', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amount).to.be.equal(BigInt(1)); - }); + itSub('[fungible] Execute the extrinsic and check approvedList', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, {Substrate: alice.address}); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amount).to.be.equal(BigInt(1)); }); - it('[refungible] Execute the extrinsic and check approvedList', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amount).to.be.equal(BigInt(1)); - }); + itSub.ifWithPallets('[refungible] Execute the extrinsic and check approvedList', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amount).to.be.equal(BigInt(1)); }); - it('[nft] Remove approval by using 0 amount', async () => { - await usingPlaygrounds(async (helper) => { - const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const collectionId = collection.collectionId; - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - await helper.signTransaction(alice, helper.api?.tx.unique.approve({Substrate: bob.address}, collectionId, tokenId, 0)); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; - }); + itSub('[nft] Remove approval by using 0 amount', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const collectionId = collection.collectionId; + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; + await helper.signTransaction(alice, helper.api?.tx.unique.approve({Substrate: bob.address}, collectionId, tokenId, 0)); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; }); - it('[fungible] Remove approval by using 0 amount', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountBefore).to.be.equal(BigInt(1)); + itSub('[fungible] Remove approval by using 0 amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, {Substrate: alice.address}); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountBefore).to.be.equal(BigInt(1)); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountAfter).to.be.equal(BigInt(0)); - }); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountAfter).to.be.equal(BigInt(0)); }); - it('[refungible] Remove approval by using 0 amount', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountBefore).to.be.equal(BigInt(1)); + itSub.ifWithPallets('[refungible] Remove approval by using 0 amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountBefore).to.be.equal(BigInt(1)); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountAfter).to.be.equal(BigInt(0)); - }); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountAfter).to.be.equal(BigInt(0)); }); - it('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - const approveTokenTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(approveTokenTx()).to.be.rejected; - }); + itSub('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + const approveTokenTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + await expect(approveTokenTx()).to.be.rejected; }); }); @@ -134,39 +103,34 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('NFT', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - await helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.true; - }); + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + await helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.true; }); - it('Fungible up to an approved amount', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, bob.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, {Substrate: bob.address}); - expect(amount).to.be.equal(BigInt(1)); - }); + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, bob.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, {Substrate: bob.address}); + expect(amount).to.be.equal(BigInt(1)); }); - it('ReFungible up to an approved amount', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: bob.address, pieces: 100n}); - await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); - const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, {Substrate: bob.address}); - expect(amount).to.be.equal(BigInt(100n)); - }); + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: bob.address, pieces: 100n}); + await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); + const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, {Substrate: bob.address}); + expect(amount).to.be.equal(BigInt(100n)); }); }); @@ -176,45 +140,40 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('NFT', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - await helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(alice.address); - }); + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + await helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner.Substrate).to.be.equal(alice.address); }); - it('Fungible up to an approved amount', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, bob.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); - const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - expect(after - before).to.be.equal(BigInt(1)); - }); + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, bob.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); + const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + expect(after - before).to.be.equal(BigInt(1)); }); - it('ReFungible up to an approved amount', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: bob.address, pieces: 100n}); - await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); - const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - expect(after - before).to.be.equal(BigInt(1)); - }); + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: bob.address, pieces: 100n}); + await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); + const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + expect(after - before).to.be.equal(BigInt(1)); }); }); @@ -224,52 +183,47 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('NFT', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - await helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - const owner = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner.Substrate).to.be.equal(alice.address); - const transferTokenFromTx = async () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - await expect(transferTokenFromTx()).to.be.rejected; - }); + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + await helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + const owner = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner.Substrate).to.be.equal(alice.address); + const transferTokenFromTx = async () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + await expect(transferTokenFromTx()).to.be.rejected; }); - it('Fungible up to an approved amount', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, bob.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); - const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - expect(after - before).to.be.equal(BigInt(1)); + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, bob.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); + const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + expect(after - before).to.be.equal(BigInt(1)); - const transferTokenFromTx = async () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); - await expect(transferTokenFromTx()).to.be.rejected; - }); + const transferTokenFromTx = async () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n); + await expect(transferTokenFromTx()).to.be.rejected; }); - it('ReFungible up to an approved amount', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: bob.address, pieces: 100n}); - await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); - const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n); - const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - expect(after - before).to.be.equal(BigInt(100)); - const transferTokenFromTx = async () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n); - await expect(transferTokenFromTx()).to.be.rejected; - }); + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: bob.address, pieces: 100n}); + await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}, 100n); + const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n); + const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + expect(after - before).to.be.equal(BigInt(100)); + const transferTokenFromTx = async () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n); + await expect(transferTokenFromTx()).to.be.rejected; }); }); @@ -280,28 +234,27 @@ let dave: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); }); }); - it('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n); + itSub('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n); - const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); - await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}, 2n); - const charlieAfter = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); - expect(charlieAfter - charlieBefore).to.be.equal(BigInt(2)); + const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); + await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address}, 2n); + const charlieAfter = await helper.ft.getBalance(collectionId, {Substrate: charlie.address}); + expect(charlieAfter - charlieBefore).to.be.equal(BigInt(2)); - const daveBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: alice.address}, {Substrate: dave.address}, 8n); - const daveAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - expect(daveAfter - daveBefore).to.be.equal(BigInt(8)); - }); + const daveBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: alice.address}, {Substrate: dave.address}, 8n); + const daveAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + expect(daveAfter - daveBefore).to.be.equal(BigInt(8)); }); }); @@ -311,57 +264,52 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('NFT', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; - await helper.signTransaction(alice, helper.api?.tx.unique.approve({Substrate: bob.address}, collectionId, tokenId, 0)); - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; - const transferTokenFromTx = async () => helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: bob.address}); - await expect(transferTokenFromTx()).to.be.rejected; - }); + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true; + await helper.signTransaction(alice, helper.api?.tx.unique.approve({Substrate: bob.address}, collectionId, tokenId, 0)); + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false; + const transferTokenFromTx = async () => helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: bob.address}); + await expect(transferTokenFromTx()).to.be.rejected; }); - it('Fungible', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountBefore).to.be.equal(BigInt(1)); + itSub('Fungible', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountBefore).to.be.equal(BigInt(1)); - await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountAfter).to.be.equal(BigInt(0)); + await helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountAfter).to.be.equal(BigInt(0)); - const transferTokenFromTx = async () => helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 1n); - await expect(transferTokenFromTx()).to.be.rejected; - }); + const transferTokenFromTx = async () => helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 1n); + await expect(transferTokenFromTx()).to.be.rejected; }); - it('ReFungible', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); - const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountBefore).to.be.equal(BigInt(1)); + itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}); + const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountBefore).to.be.equal(BigInt(1)); - await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); - const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); - expect(amountAfter).to.be.equal(BigInt(0)); + await helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 0n); + const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}); + expect(amountAfter).to.be.equal(BigInt(0)); - const transferTokenFromTx = async () => helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 100n); - await expect(transferTokenFromTx()).to.be.rejected; - }); + const transferTokenFromTx = async () => helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 100n); + await expect(transferTokenFromTx()).to.be.rejected; }); }); @@ -371,38 +319,33 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('1 for NFT', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - const approveTx = async () => helper.signTransaction(bob, helper.api?.tx.unique.approve({Substrate: charlie.address}, collectionId, tokenId, 2)); - await expect(approveTx()).to.be.rejected; - expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false; - }); + itSub('1 for NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + const approveTx = async () => helper.signTransaction(bob, helper.api?.tx.unique.approve({Substrate: charlie.address}, collectionId, tokenId, 2)); + await expect(approveTx()).to.be.rejected; + expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false; }); - it('Fungible', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - const approveTx = async () => helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 11n); - await expect(approveTx()).to.be.rejected; - }); + itSub('Fungible', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + const approveTx = async () => helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 11n); + await expect(approveTx()).to.be.rejected; }); - it('ReFungible', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - const approveTx = async () => helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 101n); - await expect(approveTx()).to.be.rejected; - }); + itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + const approveTx = async () => helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 101n); + await expect(approveTx()).to.be.rejected; }); }); @@ -413,67 +356,62 @@ let dave: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); }); }); - it('NFT', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: charlie.address}); + itSub('NFT', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: charlie.address}); - await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}); - const owner1 = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner1.Substrate).to.be.equal(dave.address); + await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}); + const owner1 = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner1.Substrate).to.be.equal(dave.address); - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - await helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}); - const owner2 = await helper.nft.getTokenOwner(collectionId, tokenId); - expect(owner2.Substrate).to.be.equal(alice.address); - }); + await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + await helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}); + const owner2 = await helper.nft.getTokenOwner(collectionId, tokenId); + expect(owner2.Substrate).to.be.equal(alice.address); }); - it('Fungible up to an approved amount', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - await helper.ft.mintTokens(alice, collectionId, charlie.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); + itSub('Fungible up to an approved amount', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + await helper.ft.mintTokens(alice, collectionId, 10n, charlie.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); - const daveBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - await helper.ft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); - const daveBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); - expect(daveBalanceAfter - daveBalanceBefore).to.be.equal(BigInt(1)); + const daveBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + await helper.ft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); + const daveBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address}); + expect(daveBalanceAfter - daveBalanceBefore).to.be.equal(BigInt(1)); - await helper.collection.addAdmin(alice ,collectionId, {Substrate: bob.address}); + await helper.collection.addAdmin(alice ,collectionId, {Substrate: bob.address}); - const aliceBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); - const aliceBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); - expect(aliceBalanceAfter - aliceBalanceBefore).to.be.equal(BigInt(1)); - }); + const aliceBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); + const aliceBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: alice.address}); + expect(aliceBalanceAfter - aliceBalanceBefore).to.be.equal(BigInt(1)); }); - it('ReFungible up to an approved amount', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: charlie.address, pieces: 100n}); + itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: charlie.address, pieces: 100n}); - const daveBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); - await helper.rft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); - const daveAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); - expect(daveAfter - daveBefore).to.be.equal(BigInt(1)); + const daveBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); + await helper.rft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n); + const daveAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address}); + expect(daveAfter - daveBefore).to.be.equal(BigInt(1)); - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - const aliceBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - await helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); - const aliceAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); - expect(aliceAfter - aliceBefore).to.be.equal(BigInt(1)); - }); + const aliceBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + await helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n); + const aliceAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address}); + expect(aliceAfter - aliceBefore).to.be.equal(BigInt(1)); }); }); @@ -484,45 +422,48 @@ let dave: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor); }); }); - it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async () => { - const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}}); - await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address); - await approveExpectSuccess(collectionId, 0, alice, bob.address, 1); - await approveExpectSuccess(collectionId, 0, alice, charlie.address, 1); + itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.mint(alice, 10n); + await collection.approveTokens(alice, {Substrate: bob.address}, 1n); + await collection.approveTokens(alice, {Substrate: charlie.address}, 1n); // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address); // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address); // expect(allowances1 + allowances2).to.be.eq(BigInt(2)); }); - it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async () => { - const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}}); - const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address); - await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1); - await approveExpectSuccess(collectionId, itemId, alice, charlie.address, 1); + itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const token = await collection.mintToken(alice, 10n); + await token.approve(alice, {Substrate: bob.address}, 1n); + await token.approve(alice, {Substrate: charlie.address}, 1n); // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address); // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address); // expect(allowances1 + allowances2).to.be.eq(BigInt(2)); }); // Canceled by changing approve logic - it.skip('Cannot approve for more than total user`s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async () => { - const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}}); - await createItemExpectSuccess(alice, collectionId, 'Fungible', dave.address); - await approveExpectSuccess(collectionId, 0, dave, bob.address, 5); - await approveExpectFail(collectionId, 0, dave, charlie, 6); + itSub.skip('Cannot approve for more than total user\'s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.mint(alice, 10n, {Substrate: dave.address}); + await collection.approveTokens(dave, {Substrate: bob.address}, 5n); + await expect(collection.approveTokens(dave, {Substrate: charlie.address}, 6n)) + .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received'); }); // Canceled by changing approve logic - it.skip('Cannot approve for more than total users amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async () => { - const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}}); - const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', dave.address); - await approveExpectSuccess(collectionId, itemId, dave, bob.address, 50); - await approveExpectFail(collectionId, itemId, dave, charlie, 51); + itSub.skip('Cannot approve for more than total user\'s amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const token = await collection.mintToken(alice, 100n, {Substrate: dave.address}); + await token.approve(dave, {Substrate: bob.address}, 50n); + await expect(token.approve(dave, {Substrate: charlie.address}, 51n)) + .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received'); }); }); @@ -532,19 +473,18 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('can be called by collection admin on non-owned item', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); - const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('can be called by collection admin on non-owned item', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address}); + const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address}); + await expect(approveTx()).to.be.rejected; }); }); @@ -554,139 +494,112 @@ let charlie: IKeyringPair; before(async () => { - await usingPlaygrounds(async (helper) => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor); }); }); - it('[nft] Approve for a collection that does not exist', async () => { - await usingPlaygrounds(async (helper) => { - const collectionId = 1 << 32 - 1; - const approveTx = async () => helper.nft.approveToken(bob, collectionId, 1, {Substrate: charlie.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('[nft] Approve for a collection that does not exist', async ({helper}) => { + const collectionId = 1 << 32 - 1; + const approveTx = async () => helper.nft.approveToken(bob, collectionId, 1, {Substrate: charlie.address}); + await expect(approveTx()).to.be.rejected; }); - it('[fungible] Approve for a collection that does not exist', async () => { - await usingPlaygrounds(async (helper) => { - const collectionId = 1 << 32 - 1; - const approveTx = async () => helper.ft.approveToken(bob, collectionId, 1, {Substrate: charlie.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('[fungible] Approve for a collection that does not exist', async ({helper}) => { + const collectionId = 1 << 32 - 1; + const approveTx = async () => helper.ft.approveToken(bob, collectionId, 1, {Substrate: charlie.address}); + await expect(approveTx()).to.be.rejected; }); - it('[refungible] Approve for a collection that does not exist', async function() { - await usingPlaygrounds(async (helper) => { - const collectionId = 1 << 32 - 1; - const approveTx = async () => helper.rft.approveToken(bob, collectionId, 1, {Substrate: charlie.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub.ifWithPallets('[refungible] Approve for a collection that does not exist', [Pallets.ReFungible], async ({helper}) => { + const collectionId = 1 << 32 - 1; + const approveTx = async () => helper.rft.approveToken(bob, collectionId, 1, {Substrate: charlie.address}); + await expect(approveTx()).to.be.rejected; }); - it('[nft] Approve for a collection that was destroyed', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.nft.burn(alice, collectionId); - const approveTx = async () => helper.nft.approveToken(alice, collectionId, 1, {Substrate: bob.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('[nft] Approve for a collection that was destroyed', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.nft.burn(alice, collectionId); + const approveTx = async () => helper.nft.approveToken(alice, collectionId, 1, {Substrate: bob.address}); + await expect(approveTx()).to.be.rejected; }); - it('[fungible] Approve for a collection that was destroyed', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.ft.burn(alice, collectionId); - const approveTx = async () => helper.ft.approveToken(alice, collectionId, 1, {Substrate: bob.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('[fungible] Approve for a collection that was destroyed', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.ft.burn(alice, collectionId); + const approveTx = async () => helper.ft.approveToken(alice, collectionId, 1, {Substrate: bob.address}); + await expect(approveTx()).to.be.rejected; }); - it('[refungible] Approve for a collection that was destroyed', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.rft.burn(alice, collectionId); - const approveTx = async () => helper.rft.approveToken(alice, collectionId, 1, {Substrate: bob.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub.ifWithPallets('[refungible] Approve for a collection that was destroyed', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.rft.burn(alice, collectionId); + const approveTx = async () => helper.rft.approveToken(alice, collectionId, 1, {Substrate: bob.address}); + await expect(approveTx()).to.be.rejected; }); - - it('[nft] Approve transfer of a token that does not exist', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const approveTx = async () => helper.nft.approveToken(alice, collectionId, 2, {Substrate: bob.address}); - await expect(approveTx()).to.be.rejected; - }); + + itSub('[nft] Approve transfer of a token that does not exist', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const approveTx = async () => helper.nft.approveToken(alice, collectionId, 2, {Substrate: bob.address}); + await expect(approveTx()).to.be.rejected; }); - it('[refungible] Approve transfer of a token that does not exist', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const approveTx = async () => helper.rft.approveToken(alice, collectionId, 2, {Substrate: bob.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub.ifWithPallets('[refungible] Approve transfer of a token that does not exist', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const approveTx = async () => helper.rft.approveToken(alice, collectionId, 2, {Substrate: bob.address}); + await expect(approveTx()).to.be.rejected; }); - it('[nft] Approve using the address that does not own the approved token', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); - const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('[nft] Approve using the address that does not own the approved token', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address}); + const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}); + await expect(approveTx()).to.be.rejected; }); - it('[fungible] Approve using the address that does not own the approved token', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); - const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub('[fungible] Approve using the address that does not own the approved token', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); + const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}); + await expect(approveTx()).to.be.rejected; }); - it('[refungible] Approve using the address that does not own the approved token', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}); - await expect(approveTx()).to.be.rejected; - }); + itSub.ifWithPallets('[refungible] Approve using the address that does not own the approved token', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}); + await expect(approveTx()).to.be.rejected; }); - it('should fail if approved more ReFungibles than owned', async function() { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); - await helper.rft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 100n); - await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 100n); + itSub.ifWithPallets('should fail if approved more ReFungibles than owned', [Pallets.ReFungible], async ({helper}) => { + const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n}); + await helper.rft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 100n); + await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 100n); - const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 101n); - await expect(approveTx()).to.be.rejected; - }); + const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 101n); + await expect(approveTx()).to.be.rejected; }); - it('should fail if approved more Fungibles than owned', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - await helper.ft.mintTokens(alice, collectionId, alice.address, 10n); - const tokenId = await helper.ft.getLastTokenId(collectionId); + itSub('should fail if approved more Fungibles than owned', async ({helper}) => { + const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + await helper.ft.mintTokens(alice, collectionId, 10n, alice.address); + const tokenId = await helper.ft.getLastTokenId(collectionId); - await helper.ft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n); - await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 10n); - const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 11n); - await expect(approveTx()).to.be.rejected; - }); + await helper.ft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n); + await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 10n); + const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 11n); + await expect(approveTx()).to.be.rejected; }); - it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => { - await usingPlaygrounds(async (helper) => { - const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); - const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); - await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false}); + itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { + const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}); + const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address}); + await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false}); - const approveTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); - await expect(approveTx()).to.be.rejected; - }); + const approveTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address}); + await expect(approveTx()).to.be.rejected; }); }); --- a/tests/src/eth/allowlist.test.ts +++ b/tests/src/eth/allowlist.test.ts @@ -14,10 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +import {IKeyringPair} from '@polkadot/types/types'; import {expect} from 'chai'; -import {contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3} from './util/helpers'; +import {isAllowlisted, normalizeAccountId} from '../util/helpers'; +import { + contractHelpers, + createEthAccount, + createEthAccountWithBalance, + deployFlipper, + evmCollection, + evmCollectionHelpers, + getCollectionAddressFromResult, + itWeb3, +} from './util/helpers'; +import {itEth, usingEthPlaygrounds} from './util/playgrounds'; -describe('EVM allowlist', () => { +describe('EVM contract allowlist', () => { itWeb3('Contract allowlist can be toggled', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const flipper = await deployFlipper(web3, owner); @@ -58,3 +70,79 @@ expect(await flipper.methods.getValue().call()).to.be.false; }); }); + +describe('EVM collection allowlist', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const user = helper.eth.createAccount(); + + const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false; + await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true; + + await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner}); + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false; + }); + + itEth('Collection allowlist can be added and removed by [sub] address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const user = donor; + + const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false; + await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner}); + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true; + + await collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).send({from: owner}); + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false; + }); + + itEth('Collection allowlist can not be add and remove [eth] address by not owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const user = helper.eth.createAccount(); + + const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false; + await expect(collectionEvm.methods.addToCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false; + await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); + + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true; + await expect(collectionEvm.methods.removeFromCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true; + }); + + itEth('Collection allowlist can not be add and remove [sub] address by not owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const notOwner = await helper.eth.createAccountWithBalance(donor); + const user = donor; + + const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false; + await expect(collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false; + await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner}); + + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true; + await expect(collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission'); + expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true; + }); +}); --- a/tests/src/eth/api/CollectionHelpers.sol +++ b/tests/src/eth/api/CollectionHelpers.sol @@ -14,14 +14,11 @@ /// @dev inlined interface interface CollectionHelpersEvents { - event CollectionCreated( - address indexed owner, - address indexed collectionId - ); + event CollectionCreated(address indexed owner, address indexed collectionId); } /// @title Contract, which allows users to operate with collections -/// @dev the ERC-165 identifier for this interface is 0x675f3074 +/// @dev the ERC-165 identifier for this interface is 0x88ee8ef1 interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { /// Create an NFT collection /// @param name Name of the collection @@ -45,9 +42,9 @@ string memory baseUri ) external returns (address); - /// @dev EVM selector for this function is: 0x44a68ad5, - /// or in textual repr: createRefungibleCollection(string,string,string) - function createRefungibleCollection( + /// @dev EVM selector for this function is: 0xab173450, + /// or in textual repr: createRFTCollection(string,string,string) + function createRFTCollection( string memory name, string memory description, string memory tokenPrefix @@ -67,8 +64,5 @@ /// @return bool Does the collection exist? /// @dev EVM selector for this function is: 0xc3de1494, /// or in textual repr: isCollectionExist(address) - function isCollectionExist(address collectionAddress) - external - view - returns (bool); + function isCollectionExist(address collectionAddress) external view returns (bool); } --- a/tests/src/eth/api/ContractHelpers.sol +++ b/tests/src/eth/api/ContractHelpers.sol @@ -23,7 +23,7 @@ } /// @title Magic contract, which allows users to reconfigure other contracts -/// @dev the ERC-165 identifier for this interface is 0xd77fab70 +/// @dev the ERC-165 identifier for this interface is 0x172cb4fb interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents { /// Get user, which deployed specified contract /// @dev May return zero address in case if contract is deployed @@ -111,7 +111,7 @@ function setSponsoringMode(address contractAddress, uint8 mode) external; /// Get current contract sponsoring rate limit - /// @param contractAddress Contract to get sponsoring mode of + /// @param contractAddress Contract to get sponsoring rate limit of /// @return uint32 Amount of blocks between two sponsored transactions /// @dev EVM selector for this function is: 0x610cfabd, /// or in textual repr: getSponsoringRateLimit(address) @@ -131,6 +131,28 @@ function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external; + /// Set contract sponsoring fee limit + /// @dev Sponsoring fee limit - is maximum fee that could be spent by + /// single transaction + /// @param contractAddress Contract to change sponsoring fee limit of + /// @param feeLimit Fee limit + /// @dev Only contract owner can change this setting + /// @dev EVM selector for this function is: 0x03aed665, + /// or in textual repr: setSponsoringFeeLimit(address,uint256) + function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) + external; + + /// Get current contract sponsoring fee limit + /// @param contractAddress Contract to get sponsoring fee limit of + /// @return uint256 Maximum amount of fee that could be spent by single + /// transaction + /// @dev EVM selector for this function is: 0xc3fdc9ee, + /// or in textual repr: getSponsoringFeeLimit(address) + function getSponsoringFeeLimit(address contractAddress) + external + view + returns (uint256); + /// Is specified user present in contract allow list /// @dev Contract owner always implicitly included /// @param contractAddress Contract to check allowlist of --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -13,7 +13,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xe54be640 +/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -164,6 +164,13 @@ /// or in textual repr: setCollectionAccess(uint8) function setCollectionAccess(uint8 mode) external; + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0xd63a8e11, + /// or in textual repr: allowed(address) + function allowed(address user) external view returns (bool); + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. @@ -171,6 +178,13 @@ /// or in textual repr: addToCollectionAllowList(address) function addToCollectionAllowList(address user) external; + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xd06ad267, + /// or in textual repr: addToCollectionAllowListSubstrate(uint256) + function addToCollectionAllowListSubstrate(uint256 user) external; + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. @@ -178,6 +192,13 @@ /// or in textual repr: removeFromCollectionAllowList(address) function removeFromCollectionAllowList(address user) external; + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xa31913ed, + /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256) + function removeFromCollectionAllowListSubstrate(uint256 user) external; + /// Switch permission for minting. /// /// @param mode Enable if "true". @@ -208,6 +229,14 @@ /// or in textual repr: uniqueCollectionType() function uniqueCollectionType() external returns (string memory); + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() external view returns (Tuple6 memory); + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -65,7 +65,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xe54be640 +/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -216,6 +216,13 @@ /// or in textual repr: setCollectionAccess(uint8) function setCollectionAccess(uint8 mode) external; + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0xd63a8e11, + /// or in textual repr: allowed(address) + function allowed(address user) external view returns (bool); + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. @@ -223,6 +230,13 @@ /// or in textual repr: addToCollectionAllowList(address) function addToCollectionAllowList(address user) external; + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xd06ad267, + /// or in textual repr: addToCollectionAllowListSubstrate(uint256) + function addToCollectionAllowListSubstrate(uint256 user) external; + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. @@ -230,6 +244,13 @@ /// or in textual repr: removeFromCollectionAllowList(address) function removeFromCollectionAllowList(address user) external; + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xa31913ed, + /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256) + function removeFromCollectionAllowListSubstrate(uint256 user) external; + /// Switch permission for minting. /// /// @param mode Enable if "true". @@ -260,6 +281,14 @@ /// or in textual repr: uniqueCollectionType() function uniqueCollectionType() external returns (string memory); + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() external view returns (Tuple17 memory); + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -65,7 +65,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0xe54be640 +/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0 interface Collection is Dummy, ERC165 { /// Set collection property. /// @@ -216,6 +216,13 @@ /// or in textual repr: setCollectionAccess(uint8) function setCollectionAccess(uint8 mode) external; + /// Checks that user allowed to operate with collection. + /// + /// @param user User address to check. + /// @dev EVM selector for this function is: 0xd63a8e11, + /// or in textual repr: allowed(address) + function allowed(address user) external view returns (bool); + /// Add the user to the allowed list. /// /// @param user Address of a trusted user. @@ -223,6 +230,13 @@ /// or in textual repr: addToCollectionAllowList(address) function addToCollectionAllowList(address user) external; + /// Add substrate user to allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xd06ad267, + /// or in textual repr: addToCollectionAllowListSubstrate(uint256) + function addToCollectionAllowListSubstrate(uint256 user) external; + /// Remove the user from the allowed list. /// /// @param user Address of a removed user. @@ -230,6 +244,13 @@ /// or in textual repr: removeFromCollectionAllowList(address) function removeFromCollectionAllowList(address user) external; + /// Remove substrate user from allowed list. + /// + /// @param user User substrate address. + /// @dev EVM selector for this function is: 0xa31913ed, + /// or in textual repr: removeFromCollectionAllowListSubstrate(uint256) + function removeFromCollectionAllowListSubstrate(uint256 user) external; + /// Switch permission for minting. /// /// @param mode Enable if "true". @@ -260,6 +281,14 @@ /// or in textual repr: uniqueCollectionType() function uniqueCollectionType() external returns (string memory); + /// Get collection owner. + /// + /// @return Tuble with sponsor address and his substrate mirror. + /// If address is canonical then substrate mirror is zero and vice versa. + /// @dev EVM selector for this function is: 0xdf727d3b, + /// or in textual repr: collectionOwner() + function collectionOwner() external view returns (Tuple17 memory); + /// Changes collection owner to another account /// /// @dev Owner can be changed only by current owner --- a/tests/src/eth/collectionHelpersAbi.json +++ b/tests/src/eth/collectionHelpersAbi.json @@ -59,7 +59,7 @@ { "internalType": "string", "name": "description", "type": "string" }, { "internalType": "string", "name": "tokenPrefix", "type": "string" } ], - "name": "createRefungibleCollection", + "name": "createRFTCollection", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "nonpayable", "type": "function" --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +import * as solc from 'solc'; import {expect} from 'chai'; -import { expectSubstrateEventsAtBlock } from '../util/helpers'; +import {expectSubstrateEventsAtBlock} from '../util/helpers'; +import Web3 from 'web3'; + import { contractHelpers, createEthAccountWithBalance, @@ -26,7 +29,11 @@ createEthAccount, ethBalanceViaSub, normalizeEvents, + CompiledContract, + GAS_ARGS, + subToEth, } from './util/helpers'; +import {submitTransactionAsync} from '../substrate/substrate-api'; describe('Sponsoring EVM contracts', () => { itWeb3('Self sponsored can be set by the address that deployed the contract', async ({api, web3, privateKeyWrapper}) => { @@ -478,3 +485,167 @@ expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200'); }); }); + +describe('Sponsoring Fee Limit', () => { + + let testContract: CompiledContract; + + function compileTestContract() { + if (!testContract) { + const input = { + language: 'Solidity', + sources: { + ['TestContract.sol']: { + content: + ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + contract TestContract { + event Result(bool); + + function test(uint32 cycles) public { + uint256 counter = 0; + while(true) { + counter ++; + if (counter > cycles){ + break; + } + } + emit Result(true); + } + } + `, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['*'], + }, + }, + }, + }; + const json = JSON.parse(solc.compile(JSON.stringify(input))); + const out = json.contracts['TestContract.sol']['TestContract']; + + testContract = { + abi: out.abi, + object: '0x' + out.evm.bytecode.object, + }; + } + return testContract; + } + + async function deployTestContract(web3: Web3, owner: string) { + const compiled = compileTestContract(); + const testContract = new web3.eth.Contract(compiled.abi, undefined, { + data: compiled.object, + from: owner, + ...GAS_ARGS, + }); + return await testContract.deploy({data: compiled.object}).send({from: owner}); + } + + itWeb3('Default fee limit', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + expect(await helpers.methods.getSponsoringFeeLimit(flipper.options.address).call()).to.be.equals('115792089237316195423570985008687907853269984665640564039457584007913129639935'); + }); + + itWeb3('Set fee limit', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send(); + expect(await helpers.methods.getSponsoringFeeLimit(flipper.options.address).call()).to.be.equals('100'); + }); + + itWeb3('Negative test - set fee limit by non-owner', async ({api, web3, privateKeyWrapper}) => { + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const stranger = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const flipper = await deployFlipper(web3, owner); + const helpers = contractHelpers(web3, owner); + await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected; + }); + + itWeb3('Negative test - check that eth transactions exceeding fee limit are not executed', async ({api, web3, privateKeyWrapper}) => { + const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const user = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + + const testContract = await deployTestContract(web3, owner); + const helpers = contractHelpers(web3, owner); + + await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); + + const gasPrice = BigInt(await web3.eth.getGasPrice()); + + await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); + + const originalUserBalance = await web3.eth.getBalance(user); + await testContract.methods.test(100).send({from: user, gas: 2_000_000}); + expect(await web3.eth.getBalance(user)).to.be.equal(originalUserBalance); + + await testContract.methods.test(100).send({from: user, gas: 2_100_000}); + expect(await web3.eth.getBalance(user)).to.not.be.equal(originalUserBalance); + }); + + itWeb3('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({api, web3, privateKeyWrapper}) => { + const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + + const testContract = await deployTestContract(web3, owner); + const helpers = contractHelpers(web3, owner); + + await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner}); + await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner}); + + await helpers.methods.setSponsor(testContract.options.address, sponsor).send(); + await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor}); + + const gasPrice = BigInt(await web3.eth.getGasPrice()); + + await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send(); + + const alice = privateKeyWrapper('//Alice'); + const originalAliceBalance = (await api.query.system.account(alice.address)).data.free.toBigInt(); + + await submitTransactionAsync( + alice, + api.tx.evm.call( + subToEth(alice.address), + testContract.options.address, + testContract.methods.test(100).encodeABI(), + Uint8Array.from([]), + 2_000_000n, + gasPrice, + null, + null, + [], + ), + ); + expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance); + + await submitTransactionAsync( + alice, + api.tx.evm.call( + subToEth(alice.address), + testContract.options.address, + testContract.methods.test(100).encodeABI(), + Uint8Array.from([]), + 2_100_000n, + gasPrice, + null, + null, + [], + ), + ); + expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.not.be.equal(originalAliceBalance); + }); +}); --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -41,7 +41,7 @@ const collectionCountBefore = await getCreatedCollectionCount(api); const result = await collectionHelper.methods - .createRefungibleCollection(collectionName, description, tokenPrefix) + .createRFTCollection(collectionName, description, tokenPrefix) .send(); const collectionCountAfter = await getCreatedCollectionCount(api); @@ -65,7 +65,7 @@ .call()).to.be.false; await collectionHelpers.methods - .createRefungibleCollection('A', 'A', 'A') + .createRFTCollection('A', 'A', 'A') .send(); expect(await collectionHelpers.methods @@ -76,7 +76,7 @@ itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionHelpers = evmCollectionHelpers(web3, owner); - let result = await collectionHelpers.methods.createRefungibleCollection('Sponsor collection', '1', '1').send(); + let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); @@ -96,7 +96,7 @@ itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createRefungibleCollection('Const collection', '5', '5').send(); + const result = await collectionHelpers.methods.createRFTCollection('Const collection', '5', '5').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const limits = { accountTokenOwnershipLimit: 1000, @@ -141,7 +141,7 @@ .isCollectionExist(collectionAddressForNonexistentCollection).call()) .to.be.false; - const result = await collectionHelpers.methods.createRefungibleCollection('Collection address exist', '7', '7').send(); + const result = await collectionHelpers.methods.createRFTCollection('Collection address exist', '7', '7').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); expect(await collectionHelpers.methods .isCollectionExist(collectionIdAddress).call()) @@ -164,7 +164,7 @@ const tokenPrefix = 'A'; await expect(helper.methods - .createRefungibleCollection(collectionName, description, tokenPrefix) + .createRFTCollection(collectionName, description, tokenPrefix) .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT); } @@ -174,7 +174,7 @@ const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1); const tokenPrefix = 'A'; await expect(helper.methods - .createRefungibleCollection(collectionName, description, tokenPrefix) + .createRFTCollection(collectionName, description, tokenPrefix) .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT); } { @@ -183,7 +183,7 @@ const description = 'A'; const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1); await expect(helper.methods - .createRefungibleCollection(collectionName, description, tokenPrefix) + .createRFTCollection(collectionName, description, tokenPrefix) .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT); } }); @@ -196,7 +196,7 @@ const tokenPrefix = 'A'; await expect(helper.methods - .createRefungibleCollection(collectionName, description, tokenPrefix) + .createRFTCollection(collectionName, description, tokenPrefix) .call()).to.be.rejectedWith('NotSufficientFounds'); }); @@ -204,7 +204,7 @@ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const notOwner = createEthAccount(web3); const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createRefungibleCollection('A', 'A', 'A').send(); + const result = await collectionHelpers.methods.createRFTCollection('A', 'A', 'A').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress, {type: 'ReFungible'}); const EXPECTED_ERROR = 'NoPermission'; @@ -229,7 +229,7 @@ itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createRefungibleCollection('Schema collection', 'A', 'A').send(); + const result = await collectionHelpers.methods.createRFTCollection('Schema collection', 'A', 'A').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); await expect(collectionEvm.methods --- a/tests/src/eth/fractionalizer/Fractionalizer.sol +++ b/tests/src/eth/fractionalizer/Fractionalizer.sol @@ -10,179 +10,158 @@ /// stores allowlist of NFT tokens available for fractionalization, has methods /// for fractionalization and defractionalization of NFT tokens. contract Fractionalizer { - struct Token { - address _collection; - uint256 _tokenId; - } - address rftCollection; - mapping(address => bool) nftCollectionAllowList; - mapping(address => mapping(uint256 => uint256)) public nft2rftMapping; - mapping(address => Token) public rft2nftMapping; - bytes32 refungibleCollectionType = keccak256(bytes("ReFungible")); + struct Token { + address _collection; + uint256 _tokenId; + } + address rftCollection; + mapping(address => bool) nftCollectionAllowList; + mapping(address => mapping(uint256 => uint256)) public nft2rftMapping; + mapping(address => Token) public rft2nftMapping; + bytes32 refungibleCollectionType = keccak256(bytes("ReFungible")); - receive() external payable onlyOwner {} + receive() external payable onlyOwner {} - /// @dev Method modifier to only allow contract owner to call it. - modifier onlyOwner() { - address contracthelpersAddress = 0x842899ECF380553E8a4de75bF534cdf6fBF64049; - ContractHelpers contractHelpers = ContractHelpers(contracthelpersAddress); - address contractOwner = contractHelpers.contractOwner(address(this)); - require(msg.sender == contractOwner, "Only owner can"); - _; - } + /// @dev Method modifier to only allow contract owner to call it. + modifier onlyOwner() { + address contracthelpersAddress = 0x842899ECF380553E8a4de75bF534cdf6fBF64049; + ContractHelpers contractHelpers = ContractHelpers(contracthelpersAddress); + address contractOwner = contractHelpers.contractOwner(address(this)); + require(msg.sender == contractOwner, "Only owner can"); + _; + } - /// @dev This emits when RFT collection setting is changed. - event RFTCollectionSet(address _collection); + /// @dev This emits when RFT collection setting is changed. + event RFTCollectionSet(address _collection); - /// @dev This emits when NFT collection is allowed or disallowed. - event AllowListSet(address _collection, bool _status); + /// @dev This emits when NFT collection is allowed or disallowed. + event AllowListSet(address _collection, bool _status); - /// @dev This emits when NFT token is fractionalized by contract. - event Fractionalized(address _collection, uint256 _tokenId, address _rftToken, uint128 _amount); + /// @dev This emits when NFT token is fractionalized by contract. + event Fractionalized(address _collection, uint256 _tokenId, address _rftToken, uint128 _amount); - /// @dev This emits when NFT token is defractionalized by contract. - event Defractionalized(address _rftToken, address _nftCollection, uint256 _nftTokenId); + /// @dev This emits when NFT token is defractionalized by contract. + event Defractionalized(address _rftToken, address _nftCollection, uint256 _nftTokenId); - /// Set RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens - /// would be created in this collection. - /// @dev Throws if RFT collection is already configured for this contract. - /// Throws if collection of wrong type (NFT, Fungible) is provided instead - /// of RFT collection. - /// Throws if `msg.sender` is not owner or admin of provided RFT collection. - /// Can only be called by contract owner. - /// @param _collection address of RFT collection. - function setRFTCollection(address _collection) public onlyOwner { - require( - rftCollection == address(0), - "RFT collection is already set" - ); - UniqueRefungible refungibleContract = UniqueRefungible(_collection); - string memory collectionType = refungibleContract.uniqueCollectionType(); - - require( - keccak256(bytes(collectionType)) == refungibleCollectionType, - "Wrong collection type. Collection is not refungible." - ); - require( - refungibleContract.isOwnerOrAdmin(address(this)), - "Fractionalizer contract should be an admin of the collection" - ); - rftCollection = _collection; - emit RFTCollectionSet(rftCollection); - } + /// Set RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens + /// would be created in this collection. + /// @dev Throws if RFT collection is already configured for this contract. + /// Throws if collection of wrong type (NFT, Fungible) is provided instead + /// of RFT collection. + /// Throws if `msg.sender` is not owner or admin of provided RFT collection. + /// Can only be called by contract owner. + /// @param _collection address of RFT collection. + function setRFTCollection(address _collection) public onlyOwner { + require(rftCollection == address(0), "RFT collection is already set"); + UniqueRefungible refungibleContract = UniqueRefungible(_collection); + string memory collectionType = refungibleContract.uniqueCollectionType(); - /// Creates and sets RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens - /// would be created in this collection. - /// @dev Throws if RFT collection is already configured for this contract. - /// Can only be called by contract owner. - /// @param _name name for created RFT collection. - /// @param _description description for created RFT collection. - /// @param _tokenPrefix token prefix for created RFT collection. - function createAndSetRFTCollection(string calldata _name, string calldata _description, string calldata _tokenPrefix) public onlyOwner { - require( - rftCollection == address(0), - "RFT collection is already set" - ); - address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; - rftCollection = CollectionHelpers(collectionHelpers).createRefungibleCollection(_name, _description, _tokenPrefix); - emit RFTCollectionSet(rftCollection); - } + require( + keccak256(bytes(collectionType)) == refungibleCollectionType, + "Wrong collection type. Collection is not refungible." + ); + require( + refungibleContract.isOwnerOrAdmin(address(this)), + "Fractionalizer contract should be an admin of the collection" + ); + rftCollection = _collection; + emit RFTCollectionSet(rftCollection); + } - /// Allow or disallow NFT collection tokens from being fractionalized by this contract. - /// @dev Can only be called by contract owner. - /// @param collection NFT token address. - /// @param status `true` to allow and `false` to disallow NFT token. - function setNftCollectionIsAllowed(address collection, bool status) public onlyOwner { - nftCollectionAllowList[collection] = status; - emit AllowListSet(collection, status); - } + /// Creates and sets RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens + /// would be created in this collection. + /// @dev Throws if RFT collection is already configured for this contract. + /// Can only be called by contract owner. + /// @param _name name for created RFT collection. + /// @param _description description for created RFT collection. + /// @param _tokenPrefix token prefix for created RFT collection. + function createAndSetRFTCollection( + string calldata _name, + string calldata _description, + string calldata _tokenPrefix + ) public onlyOwner { + require(rftCollection == address(0), "RFT collection is already set"); + address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F; + rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection(_name, _description, _tokenPrefix); + emit RFTCollectionSet(rftCollection); + } - /// Fractionilize NFT token. - /// @dev Takes NFT token from `msg.sender` and transfers RFT token to `msg.sender` - /// instead. Creates new RFT token if provided NFT token never was fractionalized - /// by this contract or existing RFT token if it was. - /// Throws if RFT collection isn't configured for this contract. - /// Throws if fractionalization of provided NFT token is not allowed - /// Throws if `msg.sender` is not owner of provided NFT token - /// @param _collection NFT collection address - /// @param _token id of NFT token to be fractionalized - /// @param _pieces number of pieces new RFT token would have - function nft2rft(address _collection, uint256 _token, uint128 _pieces) public { - require( - rftCollection != address(0), - "RFT collection is not set" - ); - UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); - require( - nftCollectionAllowList[_collection] == true, - "Fractionalization of this collection is not allowed by admin" - ); - require( - UniqueNFT(_collection).ownerOf(_token) == msg.sender, - "Only token owner could fractionalize it" - ); - UniqueNFT(_collection).transferFrom( - msg.sender, - address(this), - _token - ); - uint256 rftTokenId; - address rftTokenAddress; - UniqueRefungibleToken rftTokenContract; - if (nft2rftMapping[_collection][_token] == 0) { - rftTokenId = rftCollectionContract.nextTokenId(); - rftCollectionContract.mint(address(this), rftTokenId); - rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); - nft2rftMapping[_collection][_token] = rftTokenId; - rft2nftMapping[rftTokenAddress] = Token(_collection, _token); + /// Allow or disallow NFT collection tokens from being fractionalized by this contract. + /// @dev Can only be called by contract owner. + /// @param collection NFT token address. + /// @param status `true` to allow and `false` to disallow NFT token. + function setNftCollectionIsAllowed(address collection, bool status) public onlyOwner { + nftCollectionAllowList[collection] = status; + emit AllowListSet(collection, status); + } - rftTokenContract = UniqueRefungibleToken(rftTokenAddress); - } else { - rftTokenId = nft2rftMapping[_collection][_token]; - rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); - rftTokenContract = UniqueRefungibleToken(rftTokenAddress); - } - rftTokenContract.repartition(_pieces); - rftTokenContract.transfer(msg.sender, _pieces); - emit Fractionalized(_collection, _token, rftTokenAddress, _pieces); - } + /// Fractionilize NFT token. + /// @dev Takes NFT token from `msg.sender` and transfers RFT token to `msg.sender` + /// instead. Creates new RFT token if provided NFT token never was fractionalized + /// by this contract or existing RFT token if it was. + /// Throws if RFT collection isn't configured for this contract. + /// Throws if fractionalization of provided NFT token is not allowed + /// Throws if `msg.sender` is not owner of provided NFT token + /// @param _collection NFT collection address + /// @param _token id of NFT token to be fractionalized + /// @param _pieces number of pieces new RFT token would have + function nft2rft( + address _collection, + uint256 _token, + uint128 _pieces + ) public { + require(rftCollection != address(0), "RFT collection is not set"); + UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); + require( + nftCollectionAllowList[_collection] == true, + "Fractionalization of this collection is not allowed by admin" + ); + require(UniqueNFT(_collection).ownerOf(_token) == msg.sender, "Only token owner could fractionalize it"); + UniqueNFT(_collection).transferFrom(msg.sender, address(this), _token); + uint256 rftTokenId; + address rftTokenAddress; + UniqueRefungibleToken rftTokenContract; + if (nft2rftMapping[_collection][_token] == 0) { + rftTokenId = rftCollectionContract.nextTokenId(); + rftCollectionContract.mint(address(this), rftTokenId); + rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); + nft2rftMapping[_collection][_token] = rftTokenId; + rft2nftMapping[rftTokenAddress] = Token(_collection, _token); - /// Defrationalize NFT token. - /// @dev Takes RFT token from `msg.sender` and transfers corresponding NFT token - /// to `msg.sender` instead. - /// Throws if RFT collection isn't configured for this contract. - /// Throws if provided RFT token is no from configured RFT collection. - /// Throws if RFT token was not created by this contract. - /// Throws if `msg.sender` isn't owner of all RFT token pieces. - /// @param _collection RFT collection address - /// @param _token id of RFT token - function rft2nft(address _collection, uint256 _token) public { - require( - rftCollection != address(0), - "RFT collection is not set" - ); - require( - rftCollection == _collection, - "Wrong RFT collection" - ); - UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); - address rftTokenAddress = rftCollectionContract.tokenContractAddress(_token); - Token memory nftToken = rft2nftMapping[rftTokenAddress]; - require( - nftToken._collection != address(0), - "No corresponding NFT token found" - ); - UniqueRefungibleToken rftTokenContract = UniqueRefungibleToken(rftTokenAddress); - require( - rftTokenContract.balanceOf(msg.sender) == rftTokenContract.totalSupply(), - "Not all pieces are owned by the caller" - ); - rftCollectionContract.transferFrom(msg.sender, address(this), _token); - UniqueNFT(nftToken._collection).transferFrom( - address(this), - msg.sender, - nftToken._tokenId - ); - emit Defractionalized(rftTokenAddress, nftToken._collection, nftToken._tokenId); - } -} \ No newline at end of file + rftTokenContract = UniqueRefungibleToken(rftTokenAddress); + } else { + rftTokenId = nft2rftMapping[_collection][_token]; + rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); + rftTokenContract = UniqueRefungibleToken(rftTokenAddress); + } + rftTokenContract.repartition(_pieces); + rftTokenContract.transfer(msg.sender, _pieces); + emit Fractionalized(_collection, _token, rftTokenAddress, _pieces); + } + + /// Defrationalize NFT token. + /// @dev Takes RFT token from `msg.sender` and transfers corresponding NFT token + /// to `msg.sender` instead. + /// Throws if RFT collection isn't configured for this contract. + /// Throws if provided RFT token is no from configured RFT collection. + /// Throws if RFT token was not created by this contract. + /// Throws if `msg.sender` isn't owner of all RFT token pieces. + /// @param _collection RFT collection address + /// @param _token id of RFT token + function rft2nft(address _collection, uint256 _token) public { + require(rftCollection != address(0), "RFT collection is not set"); + require(rftCollection == _collection, "Wrong RFT collection"); + UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection); + address rftTokenAddress = rftCollectionContract.tokenContractAddress(_token); + Token memory nftToken = rft2nftMapping[rftTokenAddress]; + require(nftToken._collection != address(0), "No corresponding NFT token found"); + UniqueRefungibleToken rftTokenContract = UniqueRefungibleToken(rftTokenAddress); + require( + rftTokenContract.balanceOf(msg.sender) == rftTokenContract.totalSupply(), + "Not all pieces are owned by the caller" + ); + rftCollectionContract.transferFrom(msg.sender, address(this), _token); + UniqueNFT(nftToken._collection).transferFrom(address(this), msg.sender, nftToken._tokenId); + emit Defractionalized(rftTokenAddress, nftToken._collection, nftToken._tokenId); + } +} --- a/tests/src/eth/fractionalizer/fractionalizer.test.ts +++ b/tests/src/eth/fractionalizer/fractionalizer.test.ts @@ -21,7 +21,7 @@ import {readFile} from 'fs/promises'; import {executeTransaction, submitTransactionAsync} from '../../substrate/substrate-api'; import {getCreateCollectionResult, getCreateItemResult, UNIQUE, requirePallets, Pallets} from '../../util/helpers'; -import {collectionIdToAddress, CompiledContract, createEthAccountWithBalance, createNonfungibleCollection, createRefungibleCollection, GAS_ARGS, itWeb3, tokenIdFromAddress, uniqueNFT, uniqueRefungible, uniqueRefungibleToken} from '../util/helpers'; +import {collectionIdToAddress, CompiledContract, createEthAccountWithBalance, createNonfungibleCollection, createRFTCollection, GAS_ARGS, itWeb3, tokenIdFromAddress, uniqueNFT, uniqueRefungible, uniqueRefungibleToken} from '../util/helpers'; import {Contract} from 'web3-eth-contract'; import * as solc from 'solc'; @@ -123,7 +123,7 @@ itWeb3('Set RFT collection', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const fractionalizer = await deployFractionalizer(web3, owner); - const {collectionIdAddress} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress} = await createRFTCollection(api, web3, owner); const refungibleContract = uniqueRefungible(web3, collectionIdAddress, owner); await refungibleContract.methods.addCollectionAdmin(fractionalizer.options.address).send(); const result = await fractionalizer.methods.setRFTCollection(collectionIdAddress).send(); @@ -256,7 +256,7 @@ itWeb3('call setRFTCollection twice', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const {collectionIdAddress} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress} = await createRFTCollection(api, web3, owner); const refungibleContract = uniqueRefungible(web3, collectionIdAddress, owner); const fractionalizer = await deployFractionalizer(web3, owner); @@ -282,7 +282,7 @@ itWeb3('call setRFTCollection while not collection admin', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const fractionalizer = await deployFractionalizer(web3, owner); - const {collectionIdAddress} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress} = await createRFTCollection(api, web3, owner); await expect(fractionalizer.methods.setRFTCollection(collectionIdAddress).call()) .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g); @@ -368,7 +368,7 @@ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const fractionalizer = await deployFractionalizer(web3, owner); - const {collectionIdAddress: rftCollectionAddress} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress: rftCollectionAddress} = await createRFTCollection(api, web3, owner); const refungibleContract = uniqueRefungible(web3, rftCollectionAddress, owner); const rftTokenId = await refungibleContract.methods.nextTokenId().call(); await refungibleContract.methods.mint(owner, rftTokenId).send(); @@ -381,7 +381,7 @@ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const {fractionalizer} = await initFractionalizer(api, web3, privateKeyWrapper, owner); - const {collectionIdAddress: rftCollectionAddress} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress: rftCollectionAddress} = await createRFTCollection(api, web3, owner); const refungibleContract = uniqueRefungible(web3, rftCollectionAddress, owner); const rftTokenId = await refungibleContract.methods.nextTokenId().call(); await refungibleContract.methods.mint(owner, rftTokenId).send(); @@ -392,7 +392,7 @@ itWeb3('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const {collectionIdAddress: rftCollectionAddress} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress: rftCollectionAddress} = await createRFTCollection(api, web3, owner); const fractionalizer = await deployFractionalizer(web3, owner); const refungibleContract = uniqueRefungible(web3, rftCollectionAddress, owner); --- a/tests/src/eth/fungibleAbi.json +++ b/tests/src/eth/fungibleAbi.json @@ -78,6 +78,15 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "user", "type": "uint256" } + ], + "name": "addToCollectionAllowListSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" } ], @@ -88,6 +97,15 @@ }, { "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "allowed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], @@ -116,6 +134,23 @@ "type": "function" }, { + "inputs": [], + "name": "collectionOwner", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple6", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], "name": "collectionProperty", "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], @@ -261,6 +296,15 @@ "type": "function" }, { + "inputs": [ + { "internalType": "uint256", "name": "user", "type": "uint256" } + ], + "name": "removeFromCollectionAllowListSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }], "name": "setCollectionAccess", "outputs": [], --- a/tests/src/eth/nesting/nest.test.ts +++ b/tests/src/eth/nesting/nest.test.ts @@ -20,7 +20,7 @@ let donor: IKeyringPair; before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { + await usingEthPlaygrounds(async (_, privateKey) => { donor = privateKey('//Alice'); }); }); --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -109,6 +109,24 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "user", "type": "uint256" } + ], + "name": "addToCollectionAllowListSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "allowed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "approved", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } ], @@ -146,6 +164,23 @@ "type": "function" }, { + "inputs": [], + "name": "collectionOwner", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple17", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], "name": "collectionProperty", "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], @@ -376,6 +411,15 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "user", "type": "uint256" } + ], + "name": "removeFromCollectionAllowListSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } --- a/tests/src/eth/payable.test.ts +++ b/tests/src/eth/payable.test.ts @@ -22,7 +22,7 @@ let donor: IKeyringPair; before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { + await usingEthPlaygrounds(async (_, privateKey) => { donor = privateKey('//Alice'); }); }); --- a/tests/src/eth/reFungible.test.ts +++ b/tests/src/eth/reFungible.test.ts @@ -26,7 +26,7 @@ itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); const nextTokenId = await contract.methods.nextTokenId().call(); @@ -38,7 +38,7 @@ itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -63,7 +63,7 @@ itWeb3('ownerOf', async ({api, web3, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -79,7 +79,7 @@ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const receiver = createEthAccount(web3); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -103,7 +103,7 @@ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const receiver = createEthAccount(web3); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -130,7 +130,7 @@ itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, owner); - let result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + let result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const receiver = createEthAccount(web3); const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); @@ -163,7 +163,7 @@ itWeb3('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -221,7 +221,7 @@ itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -247,7 +247,7 @@ itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -304,7 +304,7 @@ itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -344,7 +344,7 @@ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const receiver = createEthAccount(web3); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -376,7 +376,7 @@ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const receiver = createEthAccount(web3); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -413,7 +413,7 @@ itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -430,7 +430,7 @@ itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => { const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); --- a/tests/src/eth/reFungibleAbi.json +++ b/tests/src/eth/reFungibleAbi.json @@ -109,6 +109,24 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "user", "type": "uint256" } + ], + "name": "addToCollectionAllowListSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "allowed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "approved", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } ], @@ -146,6 +164,23 @@ "type": "function" }, { + "inputs": [], + "name": "collectionOwner", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "field_0", "type": "address" }, + { "internalType": "uint256", "name": "field_1", "type": "uint256" } + ], + "internalType": "struct Tuple17", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [{ "internalType": "string", "name": "key", "type": "string" }], "name": "collectionProperty", "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], @@ -376,6 +411,15 @@ }, { "inputs": [ + { "internalType": "uint256", "name": "user", "type": "uint256" } + ], + "name": "removeFromCollectionAllowListSubstrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" } --- a/tests/src/eth/reFungibleToken.test.ts +++ b/tests/src/eth/reFungibleToken.test.ts @@ -15,7 +15,7 @@ // along with Unique Network. If not, see . import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE, requirePallets, Pallets} from '../util/helpers'; -import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, createRefungibleCollection, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth, uniqueRefungible, uniqueRefungibleToken} from './util/helpers'; +import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, createRFTCollection, evmCollection, evmCollectionHelpers, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth, uniqueRefungible, uniqueRefungibleToken} from './util/helpers'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; @@ -458,7 +458,7 @@ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper); const helper = evmCollectionHelpers(web3, caller); - const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const result = await helper.methods.createRFTCollection('Mint collection', '6', '6').send(); const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); @@ -658,7 +658,7 @@ itWeb3('Default parent token address and id', async ({api, web3, privateKeyWrapper}) => { const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const {collectionIdAddress, collectionId} = await createRefungibleCollection(api, web3, owner); + const {collectionIdAddress, collectionId} = await createRFTCollection(api, web3, owner); const refungibleContract = uniqueRefungible(web3, collectionIdAddress, owner); const refungibleTokenId = await refungibleContract.methods.nextTokenId().call(); await refungibleContract.methods.mint(owner, refungibleTokenId).send(); --- a/tests/src/eth/util/contractHelpersAbi.json +++ b/tests/src/eth/util/contractHelpersAbi.json @@ -134,6 +134,19 @@ "type": "address" } ], + "name": "getSponsoringFeeLimit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], "name": "getSponsoringRateLimit", "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], "stateMutability": "view", @@ -212,6 +225,20 @@ "name": "contractAddress", "type": "address" }, + { "internalType": "uint256", "name": "feeLimit", "type": "uint256" } + ], + "name": "setSponsoringFeeLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + }, { "internalType": "uint8", "name": "mode", "type": "uint8" } ], "name": "setSponsoringMode", --- a/tests/src/eth/util/helpers.ts +++ b/tests/src/eth/util/helpers.ts @@ -141,10 +141,10 @@ expect(result.success).to.be.true; } -export async function createRefungibleCollection(api: ApiPromise, web3: Web3, owner: string) { +export async function createRFTCollection(api: ApiPromise, web3: Web3, owner: string) { const collectionHelper = evmCollectionHelpers(web3, owner); const result = await collectionHelper.methods - .createRefungibleCollection('A', 'B', 'C') + .createRFTCollection('A', 'B', 'C') .send(); return await getCollectionAddressFromResult(api, result); } --- /dev/null +++ b/tests/src/eth/util/playgrounds/unique.dev.d.ts @@ -0,0 +1,17 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +declare module 'solc'; \ No newline at end of file --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 /* eslint-disable function-call-argument-newline */ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// import {readFile} from 'fs/promises'; --- a/tests/src/fungible.test.ts +++ b/tests/src/fungible.test.ts @@ -16,14 +16,9 @@ import {IKeyringPair} from '@polkadot/types/types'; import {U128_MAX} from './util/helpers'; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; -import {usingPlaygrounds} from './util/playgrounds'; - -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -chai.use(chaiAsPromised); -const expect = chai.expect; - +// todo:playgrounds get rid of globals let alice: IKeyringPair; let bob: IKeyringPair; @@ -35,131 +30,117 @@ }); }); - it('Create fungible collection and token', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'trest'}); - const defaultTokenId = await collection.getLastTokenId(); - expect(defaultTokenId).to.be.equal(0); + itSub('Create fungible collection and token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'trest'}); + const defaultTokenId = await collection.getLastTokenId(); + expect(defaultTokenId).to.be.equal(0); - await collection.mint(alice, {Substrate: alice.address}, U128_MAX); - const aliceBalance = await collection.getBalance({Substrate: alice.address}); - const itemCountAfter = await collection.getLastTokenId(); + await collection.mint(alice, U128_MAX); + const aliceBalance = await collection.getBalance({Substrate: alice.address}); + const itemCountAfter = await collection.getLastTokenId(); - expect(itemCountAfter).to.be.equal(defaultTokenId); - expect(aliceBalance).to.be.equal(U128_MAX); - }); + expect(itemCountAfter).to.be.equal(defaultTokenId); + expect(aliceBalance).to.be.equal(U128_MAX); }); - it('RPC method tokenOnewrs for fungible collection and token', async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const facelessCrowd = Array(7).fill(0).map((_, i) => ({Substrate: privateKey(`//Alice+${i}`).address})); + itSub('RPC method tokenOnewrs for fungible collection and token', async ({helper, privateKey}) => { + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const facelessCrowd = Array(7).fill(0).map((_, i) => ({Substrate: privateKey(`//Alice+${i}`).address})); - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, {Substrate: alice.address}, U128_MAX); + await collection.mint(alice, U128_MAX); - await collection.transfer(alice, {Substrate: bob.address}, 1000n); - await collection.transfer(alice, ethAcc, 900n); - - for (let i = 0; i < 7; i++) { - await collection.transfer(alice, facelessCrowd[i], 1n); - } + await collection.transfer(alice, {Substrate: bob.address}, 1000n); + await collection.transfer(alice, ethAcc, 900n); + + for (let i = 0; i < 7; i++) { + await collection.transfer(alice, facelessCrowd[i], 1n); + } - const owners = await collection.getTop10Owners(); + const owners = await collection.getTop10Owners(); - // What to expect - expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); - expect(owners.length).to.be.equal(10); - - const eleven = privateKey('//ALice+11'); - expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; - expect((await collection.getTop10Owners()).length).to.be.equal(10); - }); + // What to expect + expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); + expect(owners.length).to.be.equal(10); + + const eleven = privateKey('//ALice+11'); + expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; + expect((await collection.getTop10Owners()).length).to.be.equal(10); }); - it('Transfer token', async () => { - await usingPlaygrounds(async helper => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, {Substrate: alice.address}, 500n); + itSub('Transfer token', async ({helper}) => { + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mint(alice, 500n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); - expect(await collection.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await collection.transfer(alice, ethAcc, 140n)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); + expect(await collection.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await collection.transfer(alice, ethAcc, 140n)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(300n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n); - expect(await collection.getBalance(ethAcc)).to.be.equal(140n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(300n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n); + expect(await collection.getBalance(ethAcc)).to.be.equal(140n); - await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejected; - }); + await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejected; }); - it('Tokens multiple creation', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + itSub('Tokens multiple creation', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mintWithOneOwner(alice, {Substrate: alice.address}, [ - {value: 500n}, - {value: 400n}, - {value: 300n}, - ]); + await collection.mintWithOneOwner(alice, [ + {value: 500n}, + {value: 400n}, + {value: 300n}, + ]); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1200n); - }); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1200n); }); - it('Burn some tokens ', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, {Substrate: alice.address}, 500n); + itSub('Burn some tokens ', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mint(alice, 500n); - expect(await collection.isTokenExists(0)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); - expect(await collection.burnTokens(alice, 499n)).to.be.true; - expect(await collection.isTokenExists(0)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); + expect(await collection.isTokenExists(0)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n); + expect(await collection.burnTokens(alice, 499n)).to.be.true; + expect(await collection.isTokenExists(0)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); }); - it('Burn all tokens ', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - await collection.mint(alice, {Substrate: alice.address}, 500n); + itSub('Burn all tokens ', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + await collection.mint(alice, 500n); - expect(await collection.isTokenExists(0)).to.be.true; - expect(await collection.burnTokens(alice, 500n)).to.be.true; - expect(await collection.isTokenExists(0)).to.be.true; + expect(await collection.isTokenExists(0)).to.be.true; + expect(await collection.burnTokens(alice, 500n)).to.be.true; + expect(await collection.isTokenExists(0)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(0n); - expect(await collection.getTotalPieces()).to.be.equal(0n); - }); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(0n); + expect(await collection.getTotalPieces()).to.be.equal(0n); }); - it('Set allowance for token', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - await collection.mint(alice, {Substrate: alice.address}, 100n); + itSub('Set allowance for token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + await collection.mint(alice, 100n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(100n); - - expect(await collection.approveTokens(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(100n); + + expect(await collection.approveTokens(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); - expect(await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(80n); - expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(20n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); + expect(await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(80n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(20n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); - await collection.burnTokensFrom(bob, {Substrate: alice.address}, 10n); + await collection.burnTokensFrom(bob, {Substrate: alice.address}, 10n); - expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(70n); - expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(30n); - expect(await collection.transferFrom(bob, {Substrate: alice.address}, ethAcc, 10n)).to.be.true; - expect(await collection.getBalance(ethAcc)).to.be.equal(10n); - }); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(70n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(30n); + expect(await collection.transferFrom(bob, {Substrate: alice.address}, ethAcc, 10n)).to.be.true; + expect(await collection.getBalance(ethAcc)).to.be.equal(10n); }); }); --- a/tests/src/limits.test.ts +++ b/tests/src/limits.test.ts @@ -15,54 +15,41 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; -import usingApi from './substrate/substrate-api'; -import { - createCollectionExpectSuccess, - destroyCollectionExpectSuccess, - setCollectionLimitsExpectSuccess, - setCollectionSponsorExpectSuccess, - confirmSponsorshipExpectSuccess, - createItemExpectSuccess, - createItemExpectFailure, - transferExpectSuccess, - getFreeBalance, - waitNewBlocks, burnItemExpectSuccess, - requirePallets, - Pallets, -} from './util/helpers'; -import {expect} from 'chai'; +import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/playgrounds'; describe('Number of tokens per address (NFT)', () => { let alice: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice] = await helper.arrange.createAccounts([10n], donor); }); }); - it.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async () => { - - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 20}); + itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 20}); + for(let i = 0; i < 10; i++){ - await createItemExpectSuccess(alice, collectionId, 'NFT'); + await expect(collection.mintToken(alice)).to.be.not.rejected; } - await createItemExpectFailure(alice, collectionId, 'NFT'); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); for(let i = 1; i < 11; i++) { - await burnItemExpectSuccess(alice, collectionId, i); + await expect(collection.burnToken(alice, i)).to.be.not.rejected; } - await destroyCollectionExpectSuccess(collectionId); + await collection.burn(alice); }); - - it('Collection limits allow lower number than chain limits, collection limits are enforced', async () => { + + itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 1}); - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 1}); - await createItemExpectSuccess(alice, collectionId, 'NFT'); - await createItemExpectFailure(alice, collectionId, 'NFT'); - await burnItemExpectSuccess(alice, collectionId, 1); - await destroyCollectionExpectSuccess(collectionId); + await collection.mintToken(alice); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + + await collection.burnToken(alice, 1); + await expect(collection.burn(alice)).to.be.not.rejected; }); }); @@ -70,38 +57,43 @@ let alice: IKeyringPair; before(async function() { - await requirePallets(this, [Pallets.ReFungible]); + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); + const donor = privateKey('//Alice'); + [alice] = await helper.arrange.createAccounts([10n], donor); }); }); - it.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 20}); + itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 20}); + for(let i = 0; i < 10; i++){ - await createItemExpectSuccess(alice, collectionId, 'ReFungible'); + await expect(collection.mintToken(alice, 10n)).to.be.not.rejected; } - await createItemExpectFailure(alice, collectionId, 'ReFungible'); + await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); for(let i = 1; i < 11; i++) { - await burnItemExpectSuccess(alice, collectionId, i, 100); + await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected; } - await destroyCollectionExpectSuccess(collectionId); + await collection.burn(alice); }); - it('Collection limits allow lower number than chain limits, collection limits are enforced', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 1}); - await createItemExpectSuccess(alice, collectionId, 'ReFungible'); - await createItemExpectFailure(alice, collectionId, 'ReFungible'); - await burnItemExpectSuccess(alice, collectionId, 1, 100); - await destroyCollectionExpectSuccess(collectionId); + itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 1}); + + await collection.mintToken(alice); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); + + await collection.burnToken(alice, 1); + await expect(collection.burn(alice)).to.be.not.rejected; }); }); +// todo:playgrounds skipped ~ postponed describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => { - let alice: IKeyringPair; + /*let alice: IKeyringPair; let bob: IKeyringPair; let charlie: IKeyringPair; @@ -113,7 +105,7 @@ }); }); - it.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => { + itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); @@ -137,7 +129,7 @@ await destroyCollectionExpectSuccess(collectionId); }); - it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => { + itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); @@ -176,7 +168,7 @@ }); }); - it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => { + itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible'); @@ -202,7 +194,7 @@ await destroyCollectionExpectSuccess(collectionId); }); - it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => { + itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); @@ -243,7 +235,7 @@ }); }); - it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => { + itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => { const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7}); const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible'); @@ -267,7 +259,7 @@ await destroyCollectionExpectSuccess(collectionId); }); - it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => { + itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => { const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1}); @@ -290,7 +282,7 @@ expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true; //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore); await destroyCollectionExpectSuccess(collectionId); - }); + });*/ }); describe('Collection zero limits (NFT)', () => { @@ -299,38 +291,38 @@ let charlie: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); }); }); - it.skip('Limits have 0 in tokens per address field, the chain limits are applied', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 0}); + itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 0}); + for(let i = 0; i < 10; i++){ - await createItemExpectSuccess(alice, collectionId, 'NFT'); + await collection.mintToken(alice); } - await createItemExpectFailure(alice, collectionId, 'NFT'); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); }); - it('Limits have 0 in sponsor timeout, no limits are applied', async () => { + itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {sponsorTransferTimeout: 0}); + const token = await collection.mintToken(alice); - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 0}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob); - const aliceBalanceBefore = await getFreeBalance(alice); + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + await token.transfer(alice, {Substrate: bob.address}); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); // check setting SponsorTimeout = 0, success with next block - await waitNewBlocks(1); - await transferExpectSuccess(collectionId, tokenId, bob, charlie); - const aliceBalanceAfterSponsoredTransaction1 = await getFreeBalance(alice); + await helper.wait.newBlocks(1); + await token.transfer(bob, {Substrate: charlie.address}); + const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore); }); }); @@ -340,29 +332,28 @@ let charlie: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); }); }); - it('Limits have 0 in sponsor timeout, no limits are applied', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 0}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible'); - const aliceBalanceBefore = await getFreeBalance(alice); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); + itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.setLimits(alice, {sponsorTransferTimeout: 0}); + await collection.mint(alice, 3n); + + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + await collection.transfer(alice, {Substrate: bob.address}, 2n); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); // check setting SponsorTimeout = 0, success with next block - await waitNewBlocks(1); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible'); - const aliceBalanceAfterSponsoredTransaction1 = await getFreeBalance(alice); + await helper.wait.newBlocks(1); + await collection.transfer(bob, {Substrate: charlie.address}); + const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore); }); }); @@ -372,110 +363,112 @@ let charlie: IKeyringPair; before(async function() { - await requirePallets(this, [Pallets.ReFungible]); + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); }); }); - it.skip('Limits have 0 in tokens per address field, the chain limits are applied', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 0}); + itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {accountTokenOwnershipLimit: 0}); for(let i = 0; i < 10; i++){ - await createItemExpectSuccess(alice, collectionId, 'ReFungible'); + await collection.mintToken(alice); } - await createItemExpectFailure(alice, collectionId, 'ReFungible'); + await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/); }); - it('Limits have 0 in sponsor timeout, no limits are applied', async () => { + itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + await collection.setLimits(alice, {sponsorTransferTimeout: 0}); + const token = await collection.mintToken(alice, 3n); - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 0}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible'); - await setCollectionSponsorExpectSuccess(collectionId, alice.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Alice'); - await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible'); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible'); - const aliceBalanceBefore = await getFreeBalance(alice); + await collection.setSponsor(alice, alice.address); + await collection.confirmSponsorship(alice); + + await token.transfer(alice, {Substrate: bob.address}, 2n); + const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address); // check setting SponsorTimeout = 0, success with next block - await waitNewBlocks(1); - await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible'); - const aliceBalanceAfterSponsoredTransaction1 = await getFreeBalance(alice); + await helper.wait.newBlocks(1); + await token.transfer(bob, {Substrate: charlie.address}); + const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address); expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; - //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore); }); - - it('Effective collection limits', async () => { - await usingApi(async (api) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true}); - - { // Check that limits is undefined - const collection = await api.rpc.unique.collectionById(collectionId); - expect(collection.isSome).to.be.true; - const limits = collection.unwrap().limits; - expect(limits).to.be.any; - - expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.null; - expect(limits.sponsoredDataSize.toHuman()).to.be.null; - expect(limits.sponsoredDataRateLimit.toHuman()).to.be.null; - expect(limits.tokenLimit.toHuman()).to.be.null; - expect(limits.sponsorTransferTimeout.toHuman()).to.be.null; - expect(limits.sponsorApproveTimeout.toHuman()).to.be.null; - expect(limits.ownerCanTransfer.toHuman()).to.be.true; - expect(limits.ownerCanDestroy.toHuman()).to.be.null; - expect(limits.transfersEnabled.toHuman()).to.be.null; - } - - { // Check that limits is undefined for non-existent collection - const limits = await api.rpc.unique.effectiveCollectionLimits(11111); - expect(limits.toHuman()).to.be.null; - } - - { // Check that default values defined for collection limits - const limitsOpt = await api.rpc.unique.effectiveCollectionLimits(collectionId); - expect(limitsOpt.isNone).to.be.false; - const limits = limitsOpt.unwrap(); - - expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.eq('100,000'); - expect(limits.sponsoredDataSize.toHuman()).to.be.eq('2,048'); - expect(limits.sponsoredDataRateLimit.toHuman()).to.be.eq('SponsoringDisabled'); - expect(limits.tokenLimit.toHuman()).to.be.eq('4,294,967,295'); - expect(limits.sponsorTransferTimeout.toHuman()).to.be.eq('5'); - expect(limits.sponsorApproveTimeout.toHuman()).to.be.eq('5'); - expect(limits.ownerCanTransfer.toHuman()).to.be.true; - expect(limits.ownerCanDestroy.toHuman()).to.be.true; - expect(limits.transfersEnabled.toHuman()).to.be.true; - } +}); - { //Check the values for collection limits - await setCollectionLimitsExpectSuccess(alice, collectionId, { - accountTokenOwnershipLimit: 99_999, - sponsoredDataSize: 1024, - tokenLimit: 123, - transfersEnabled: false, - }); +describe('Effective collection limits (NFT)', () => { + let alice: IKeyringPair; - const limitsOpt = await api.rpc.unique.effectiveCollectionLimits(collectionId); - expect(limitsOpt.isNone).to.be.false; - const limits = limitsOpt.unwrap(); - - expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.eq('99,999'); - expect(limits.sponsoredDataSize.toHuman()).to.be.eq('1,024'); - expect(limits.sponsoredDataRateLimit.toHuman()).to.be.eq('SponsoringDisabled'); - expect(limits.tokenLimit.toHuman()).to.be.eq('123'); - expect(limits.sponsorTransferTimeout.toHuman()).to.be.eq('5'); - expect(limits.sponsorApproveTimeout.toHuman()).to.be.eq('5'); - expect(limits.ownerCanTransfer.toHuman()).to.be.true; - expect(limits.ownerCanDestroy.toHuman()).to.be.true; - expect(limits.transfersEnabled.toHuman()).to.be.false; - } + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice] = await helper.arrange.createAccounts([10n], donor); }); }); -}); + + itSub('Effective collection limits', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + { + // Check that limits are undefined + const collectionInfo = await collection.getData(); + const limits = collectionInfo?.raw.limits; + expect(limits).to.be.any; + + expect(limits.accountTokenOwnershipLimit).to.be.null; + expect(limits.sponsoredDataSize).to.be.null; + expect(limits.sponsoredDataRateLimit).to.be.null; + expect(limits.tokenLimit).to.be.null; + expect(limits.sponsorTransferTimeout).to.be.null; + expect(limits.sponsorApproveTimeout).to.be.null; + expect(limits.ownerCanTransfer).to.be.true; + expect(limits.ownerCanDestroy).to.be.null; + expect(limits.transfersEnabled).to.be.null; + } + + { // Check that limits is undefined for non-existent collection + const limits = await helper.collection.getEffectiveLimits(999999); + expect(limits).to.be.null; + } + { // Check that default values defined for collection limits + const limits = await collection.getEffectiveLimits(); + expect(limits.accountTokenOwnershipLimit).to.be.eq(100000); + expect(limits.sponsoredDataSize).to.be.eq(2048); + expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null}); + expect(limits.tokenLimit).to.be.eq(4294967295); + expect(limits.sponsorTransferTimeout).to.be.eq(5); + expect(limits.sponsorApproveTimeout).to.be.eq(5); + expect(limits.ownerCanTransfer).to.be.true; + expect(limits.ownerCanDestroy).to.be.true; + expect(limits.transfersEnabled).to.be.true; + } + + { + // Check the values for collection limits + await collection.setLimits(alice, { + accountTokenOwnershipLimit: 99_999, + sponsoredDataSize: 1024, + tokenLimit: 123, + transfersEnabled: false, + }); + + const limits = await collection.getEffectiveLimits(); + + expect(limits.accountTokenOwnershipLimit).to.be.eq(99999); + expect(limits.sponsoredDataSize).to.be.eq(1024); + expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null}); + expect(limits.tokenLimit).to.be.eq(123); + expect(limits.sponsorTransferTimeout).to.be.eq(5); + expect(limits.sponsorApproveTimeout).to.be.eq(5); + expect(limits.ownerCanTransfer).to.be.true; + expect(limits.ownerCanDestroy).to.be.true; + expect(limits.transfersEnabled).to.be.false; + } + }); +}); --- a/tests/src/nextSponsoring.test.ts +++ b/tests/src/nextSponsoring.test.ts @@ -14,100 +14,84 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {ApiPromise} from '@polkadot/api'; import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {default as usingApi} from './substrate/substrate-api'; -import { - createCollectionExpectSuccess, - setCollectionSponsorExpectSuccess, - confirmSponsorshipExpectSuccess, - createItemExpectSuccess, - transferExpectSuccess, - normalizeAccountId, - getNextSponsored, - requirePallets, - Pallets, -} from './util/helpers'; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import {expect, itSub, Pallets, usingPlaygrounds} from './util/playgrounds'; +const SPONSORING_TIMEOUT = 5; - describe('Integration Test getNextSponsored(collection_id, owner, item_id):', () => { let alice: IKeyringPair; let bob: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); }); }); - it('NFT', async () => { - await usingApi(async (api: ApiPromise) => { + itSub('NFT', async ({helper}) => { + // Non-existing collection + expect(await helper.collection.getTokenNextSponsored(0, 0, {Substrate: alice.address})).to.be.null; - // Not existing collection - expect(await getNextSponsored(api, 0, normalizeAccountId(alice), 0)).to.be.equal(-1); + const collection = await helper.nft.mintCollection(alice, {}); + const token = await collection.mintToken(alice); - const collectionId = await createCollectionExpectSuccess(); - const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address); + // Check with Disabled sponsoring state + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; + + // Check with Unconfirmed sponsoring state + await collection.setSponsor(alice, bob.address); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; - // Check with Disabled sponsoring state - expect(await getNextSponsored(api, collectionId, normalizeAccountId(alice), itemId)).to.be.equal(-1); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); + // Check with Confirmed sponsoring state + await collection.confirmSponsorship(bob); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0); - // Check with Unconfirmed sponsoring state - expect(await getNextSponsored(api, collectionId, normalizeAccountId(alice), itemId)).to.be.equal(-1); - await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); + // Check after transfer + await token.transfer(alice, {Substrate: bob.address}); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); - // Check with Confirmed sponsoring state - expect(await getNextSponsored(api, collectionId, normalizeAccountId(alice), itemId)).to.be.equal(0); + // Non-existing token + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; + }); - // After transfer - await transferExpectSuccess(collectionId, itemId, alice, bob, 1); - expect(await getNextSponsored(api, collectionId, normalizeAccountId(alice), itemId)).to.be.lessThanOrEqual(5); + itSub('Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {}); + await collection.mint(alice, 10n); - // Not existing token - expect(await getNextSponsored(api, collectionId, normalizeAccountId(alice), itemId+1)).to.be.equal(-1); - }); - }); + // Check with Disabled sponsoring state + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; - it('Fungible', async () => { - await usingApi(async (api: ApiPromise) => { - - const createMode = 'Fungible'; - const funCollectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}}); - await createItemExpectSuccess(alice, funCollectionId, createMode); - await setCollectionSponsorExpectSuccess(funCollectionId, bob.address); - await confirmSponsorshipExpectSuccess(funCollectionId, '//Bob'); - expect(await getNextSponsored(api, funCollectionId, normalizeAccountId(alice), 0)).to.be.equal(0); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + // Check with Confirmed sponsoring state + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.equal(0); - await transferExpectSuccess(funCollectionId, 0, alice, bob, 10, 'Fungible'); - expect(await getNextSponsored(api, funCollectionId, normalizeAccountId(alice), 0)).to.be.lessThanOrEqual(5); - }); + // Check after transfer + await collection.transfer(alice, {Substrate: bob.address}); + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); }); - it('ReFungible', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {}); + const token = await collection.mintToken(alice, 10n); - await usingApi(async (api: ApiPromise) => { + // Check with Disabled sponsoring state + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null; - const createMode = 'ReFungible'; - const refunCollectionId = await createCollectionExpectSuccess({mode: {type: createMode}}); - const refunItemId = await createItemExpectSuccess(alice, refunCollectionId, createMode); - await setCollectionSponsorExpectSuccess(refunCollectionId, bob.address); - await confirmSponsorshipExpectSuccess(refunCollectionId, '//Bob'); - expect(await getNextSponsored(api, refunCollectionId, normalizeAccountId(alice), refunItemId)).to.be.equal(0); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + + // Check with Confirmed sponsoring state + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0); - await transferExpectSuccess(refunCollectionId, refunItemId, alice, bob, 10, 'ReFungible'); - expect(await getNextSponsored(api, refunCollectionId, normalizeAccountId(alice), refunItemId)).to.be.lessThanOrEqual(5); + // Check after transfer + await token.transfer(alice, {Substrate: bob.address}); + expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT); - // Not existing token - expect(await getNextSponsored(api, refunCollectionId, normalizeAccountId(alice), refunItemId+1)).to.be.equal(-1); - }); + // Non-existing token + expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null; }); }); --- a/tests/src/overflow.test.ts +++ b/tests/src/overflow.test.ts @@ -23,6 +23,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; +// todo:playgrounds skipped ~ postponed describe.skip('Integration Test fungible overflows', () => { let alice: IKeyringPair; let bob: IKeyringPair; --- a/tests/src/pallet-presence.test.ts +++ b/tests/src/pallet-presence.test.ts @@ -14,13 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {ApiPromise} from '@polkadot/api'; -import {expect} from 'chai'; -import usingApi from './substrate/substrate-api'; - -function getModuleNames(api: ApiPromise): string[] { - return api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()); -} +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; // Pallets that must always be present const requiredPallets = [ @@ -62,8 +56,8 @@ describe('Pallet presence', () => { before(async () => { - await usingApi(async api => { - const chain = await api.rpc.system.chain(); + await usingPlaygrounds(async helper => { + const chain = await helper.api!.rpc.system.chain(); const refungible = 'refungible'; const scheduler = 'scheduler'; @@ -80,23 +74,15 @@ }); }); - it('Required pallets are present', async () => { - await usingApi(async api => { - for (let i=0; i { + expect(helper.fetchAllPalletNames()).to.contain.members([...requiredPallets]); }); - it('Governance and consensus pallets are present', async () => { - await usingApi(async api => { - for (let i=0; i { + expect(helper.fetchAllPalletNames()).to.contain.members([...consensusPallets]); }); - it('No extra pallets are included', async () => { - await usingApi(async api => { - expect(getModuleNames(api).sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort()); - }); + + itSub('No extra pallets are included', async ({helper}) => { + expect(helper.fetchAllPalletNames().sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort()); }); }); --- a/tests/src/refungible.test.ts +++ b/tests/src/refungible.test.ts @@ -15,18 +15,7 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; - -import {usingPlaygrounds} from './util/playgrounds'; -import { - getModuleNames, - Pallets, - requirePallets, -} from './util/helpers'; - -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -chai.use(chaiAsPromised); -const expect = chai.expect; +import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/playgrounds'; let alice: IKeyringPair; let bob: IKeyringPair; @@ -34,255 +23,231 @@ describe('integration test: Refungible functionality:', async () => { before(async function() { - await requirePallets(this, [Pallets.ReFungible]); + await usingPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); - await usingPlaygrounds(async (helper, privateKey) => { alice = privateKey('//Alice'); bob = privateKey('//Bob'); - if (!getModuleNames(helper.api!).includes(Pallets.ReFungible)) this.skip(); }); }); - it('Create refungible collection and token', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + itSub('Create refungible collection and token', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const itemCountBefore = await collection.getLastTokenId(); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); - - const itemCountAfter = await collection.getLastTokenId(); - - // What to expect - expect(token?.tokenId).to.be.gte(itemCountBefore); - expect(itemCountAfter).to.be.equal(itemCountBefore + 1); - expect(itemCountAfter.toString()).to.be.equal(token?.tokenId.toString()); - }); + const itemCountBefore = await collection.getLastTokenId(); + const token = await collection.mintToken(alice, 100n); + + const itemCountAfter = await collection.getLastTokenId(); + + // What to expect + expect(token?.tokenId).to.be.gte(itemCountBefore); + expect(itemCountAfter).to.be.equal(itemCountBefore + 1); + expect(itemCountAfter.toString()).to.be.equal(token?.tokenId.toString()); }); - it('Checking RPC methods when interacting with maximum allowed values (MAX_REFUNGIBLE_PIECES)', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const token = await collection.mintToken(alice, {Substrate: alice.address}, MAX_REFUNGIBLE_PIECES); - - expect(await collection.getTokenBalance(token.tokenId, {Substrate: alice.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); - - await collection.transferToken(alice, token.tokenId, {Substrate: bob.address}, MAX_REFUNGIBLE_PIECES); - expect(await collection.getTokenBalance(token.tokenId, {Substrate: bob.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); - expect(await token.getTotalPieces()).to.be.equal(MAX_REFUNGIBLE_PIECES); - - await expect(collection.mintToken(alice, {Substrate: alice.address}, MAX_REFUNGIBLE_PIECES + 1n)).to.eventually.be.rejected; - }); + itSub('Checking RPC methods when interacting with maximum allowed values (MAX_REFUNGIBLE_PIECES)', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + + const token = await collection.mintToken(alice, MAX_REFUNGIBLE_PIECES); + + expect(await collection.getTokenBalance(token.tokenId, {Substrate: alice.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); + + await collection.transferToken(alice, token.tokenId, {Substrate: bob.address}, MAX_REFUNGIBLE_PIECES); + expect(await collection.getTokenBalance(token.tokenId, {Substrate: bob.address})).to.be.equal(MAX_REFUNGIBLE_PIECES); + expect(await token.getTotalPieces()).to.be.equal(MAX_REFUNGIBLE_PIECES); + + await expect(collection.mintToken(alice, MAX_REFUNGIBLE_PIECES + 1n)) + .to.eventually.be.rejectedWith(/refungible\.WrongRefungiblePieces/); }); - it('RPC method tokenOnewrs for refungible collection and token', async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const facelessCrowd = Array(7).fill(0).map((_, i) => ({Substrate: privateKey(`//Alice+${i}`).address})); + itSub('RPC method tokenOnewrs for refungible collection and token', async ({helper, privateKey}) => { + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const facelessCrowd = Array(7).fill(0).map((_, i) => ({Substrate: privateKey(`//Alice+${i}`).address})); - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 10_000n); + const token = await collection.mintToken(alice, 10_000n); - await token.transfer(alice, {Substrate: bob.address}, 1000n); - await token.transfer(alice, ethAcc, 900n); - - for (let i = 0; i < 7; i++) { - await token.transfer(alice, facelessCrowd[i], 50n * BigInt(i + 1)); - } + await token.transfer(alice, {Substrate: bob.address}, 1000n); + await token.transfer(alice, ethAcc, 900n); + + for (let i = 0; i < 7; i++) { + await token.transfer(alice, facelessCrowd[i], 50n * BigInt(i + 1)); + } - const owners = await token.getTop10Owners(); + const owners = await token.getTop10Owners(); - // What to expect - expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); - expect(owners.length).to.be.equal(10); - - const eleven = privateKey('//ALice+11'); - expect(await token.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; - expect((await token.getTop10Owners()).length).to.be.equal(10); - }); + // What to expect + expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); + expect(owners.length).to.be.equal(10); + + const eleven = privateKey('//ALice+11'); + expect(await token.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; + expect((await token.getTop10Owners()).length).to.be.equal(10); }); - it('Transfer token pieces', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); + itSub('Transfer token pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; - - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); - - await expect(token.transfer(alice, {Substrate: bob.address}, 41n)).to.eventually.be.rejected; - }); + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; + + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); + + await expect(token.transfer(alice, {Substrate: bob.address}, 41n)) + .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('Create multiple tokens', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - // TODO: fix mintMultipleTokens - // await collection.mintMultipleTokens(alice, [ - // {owner: {Substrate: alice.address}, pieces: 1n}, - // {owner: {Substrate: alice.address}, pieces: 2n}, - // {owner: {Substrate: alice.address}, pieces: 100n}, - // ]); - await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, [ - {pieces: 1n}, - {pieces: 2n}, - {pieces: 100n}, - ]); - const lastTokenId = await collection.getLastTokenId(); - expect(lastTokenId).to.be.equal(3); - expect(await collection.getTokenBalance(lastTokenId, {Substrate: alice.address})).to.be.equal(100n); - }); + itSub('Create multiple tokens', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + // TODO: fix mintMultipleTokens + // await collection.mintMultipleTokens(alice, [ + // {owner: {Substrate: alice.address}, pieces: 1n}, + // {owner: {Substrate: alice.address}, pieces: 2n}, + // {owner: {Substrate: alice.address}, pieces: 100n}, + // ]); + await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, [ + {pieces: 1n}, + {pieces: 2n}, + {pieces: 100n}, + ]); + const lastTokenId = await collection.getLastTokenId(); + expect(lastTokenId).to.be.equal(3); + expect(await collection.getTokenBalance(lastTokenId, {Substrate: alice.address})).to.be.equal(100n); }); - it('Burn some pieces', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); - expect(await collection.isTokenExists(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - expect((await token.burn(alice, 99n)).success).to.be.true; - expect(await collection.isTokenExists(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n); - }); + itSub('Burn some pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + expect(await collection.isTokenExists(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + expect((await token.burn(alice, 99n)).success).to.be.true; + expect(await collection.isTokenExists(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n); }); - it('Burn all pieces', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); - - expect(await collection.isTokenExists(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + itSub('Burn all pieces', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + expect(await collection.isTokenExists(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - expect((await token.burn(alice, 100n)).success).to.be.true; - expect(await collection.isTokenExists(token.tokenId)).to.be.false; - }); + expect((await token.burn(alice, 100n)).success).to.be.true; + expect(await collection.isTokenExists(token.tokenId)).to.be.false; }); - it('Burn some pieces for multiple users', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); + itSub('Burn some pieces for multiple users', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); - expect(await collection.isTokenExists(token.tokenId)).to.be.true; - - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await collection.isTokenExists(token.tokenId)).to.be.true; + + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n); - expect((await token.burn(alice, 40n)).success).to.be.true; + expect((await token.burn(alice, 40n)).success).to.be.true; - expect(await collection.isTokenExists(token.tokenId)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); + expect(await collection.isTokenExists(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); - expect((await token.burn(bob, 59n)).success).to.be.true; + expect((await token.burn(bob, 59n)).success).to.be.true; - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n); - expect(await collection.isTokenExists(token.tokenId)).to.be.true; + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n); + expect(await collection.isTokenExists(token.tokenId)).to.be.true; - expect((await token.burn(bob, 1n)).success).to.be.true; + expect((await token.burn(bob, 1n)).success).to.be.true; - expect(await collection.isTokenExists(token.tokenId)).to.be.false; - }); + expect(await collection.isTokenExists(token.tokenId)).to.be.false; }); - it('Set allowance for token', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); - - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); + itSub('Set allowance for token', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n); - expect(await token.approve(alice, {Substrate: bob.address}, 60n)).to.be.true; - expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); + expect(await token.approve(alice, {Substrate: bob.address}, 60n)).to.be.true; + expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n); - expect(await token.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(80n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(20n); - expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); - }); + expect(await token.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(80n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(20n); + expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n); }); - it('Repartition', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); + itSub('Repartition', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); - expect(await token.repartition(alice, 200n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(200n); - expect(await token.getTotalPieces()).to.be.equal(200n); - - expect(await token.transfer(alice, {Substrate: bob.address}, 110n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(90n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(110n); - - await expect(token.repartition(alice, 80n)).to.eventually.be.rejected; - - expect(await token.transfer(alice, {Substrate: bob.address}, 90n)).to.be.true; - expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); - expect(await token.getBalance({Substrate: bob.address})).to.be.equal(200n); - - expect(await token.repartition(bob, 150n)).to.be.true; - await expect(token.transfer(bob, {Substrate: alice.address}, 160n)).to.eventually.be.rejected; + expect(await token.repartition(alice, 200n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(200n); + expect(await token.getTotalPieces()).to.be.equal(200n); + + expect(await token.transfer(alice, {Substrate: bob.address}, 110n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(90n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(110n); + + await expect(token.repartition(alice, 80n)) + .to.eventually.be.rejectedWith(/refungible\.RepartitionWhileNotOwningAllPieces/); + + expect(await token.transfer(alice, {Substrate: bob.address}, 90n)).to.be.true; + expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n); + expect(await token.getBalance({Substrate: bob.address})).to.be.equal(200n); - }); + expect(await token.repartition(bob, 150n)).to.be.true; + await expect(token.transfer(bob, {Substrate: alice.address}, 160n)) + .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('Repartition with increased amount', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); - await token.repartition(alice, 200n); - const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event); - expect(chainEvents).to.include.deep.members([{ - method: 'ItemCreated', - section: 'common', - index: '0x4202', - data: [ - helper.api!.createType('u32', collection.collectionId).toHuman(), - helper.api!.createType('u32', token.tokenId).toHuman(), - {Substrate: alice.address}, - '100', - ], - }]); - }); + itSub('Repartition with increased amount', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + await token.repartition(alice, 200n); + const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event); + expect(chainEvents).to.include.deep.members([{ + method: 'ItemCreated', + section: 'common', + index: '0x4202', + data: [ + helper.api!.createType('u32', collection.collectionId).toHuman(), + helper.api!.createType('u32', token.tokenId).toHuman(), + {Substrate: alice.address}, + '100', + ], + }]); }); - it('Repartition with decreased amount', async () => { - await usingPlaygrounds(async helper => { - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - const token = await collection.mintToken(alice, {Substrate: alice.address}, 100n); - await token.repartition(alice, 50n); - const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event); - expect(chainEvents).to.include.deep.members([{ - method: 'ItemDestroyed', - section: 'common', - index: '0x4203', - data: [ - helper.api!.createType('u32', collection.collectionId).toHuman(), - helper.api!.createType('u32', token.tokenId).toHuman(), - {Substrate: alice.address}, - '50', - ], - }]); - }); + itSub('Repartition with decreased amount', async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + const token = await collection.mintToken(alice, 100n); + await token.repartition(alice, 50n); + const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event); + expect(chainEvents).to.include.deep.members([{ + method: 'ItemDestroyed', + section: 'common', + index: '0x4203', + data: [ + helper.api!.createType('u32', collection.collectionId).toHuman(), + helper.api!.createType('u32', token.tokenId).toHuman(), + {Substrate: alice.address}, + '50', + ], + }]); }); - it('Create new collection with properties', async () => { - await usingPlaygrounds(async helper => { - const properties = [{key: 'key1', value: 'val1'}]; - const tokenPropertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]; - const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties, tokenPropertyPermissions}); - const info = await collection.getData(); - expect(info?.raw.properties).to.be.deep.equal(properties); - expect(info?.raw.tokenPropertyPermissions).to.be.deep.equal(tokenPropertyPermissions); - }); + itSub('Create new collection with properties', async ({helper}) => { + const properties = [{key: 'key1', value: 'val1'}]; + const tokenPropertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]; + const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties, tokenPropertyPermissions}); + const info = await collection.getData(); + expect(info?.raw.properties).to.be.deep.equal(properties); + expect(info?.raw.tokenPropertyPermissions).to.be.deep.equal(tokenPropertyPermissions); }); }); --- a/tests/src/removeCollectionAdmin.test.ts +++ b/tests/src/removeCollectionAdmin.test.ts @@ -14,115 +14,99 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {usingPlaygrounds} from './util/playgrounds'; +import {IKeyringPair} from '@polkadot/types/types'; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; -chai.use(chaiAsPromised); -const expect = chai.expect; +describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; -describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => { - it('Remove collection admin.', async () => { + before(async () => { await usingPlaygrounds(async (helper, privateKey) => { - const alice = privateKey('//Alice'); - const bob = privateKey('//Bob'); - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - const collectionInfo = await collection.getData(); - expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address); - // first - add collection admin Bob - await collection.addAdmin(alice, {Substrate: bob.address}); + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); + }); + }); + + itSub('Remove collection admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-1', tokenPrefix: 'RCA'}); + const collectionInfo = await collection.getData(); + expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address); + // first - add collection admin Bob + await collection.addAdmin(alice, {Substrate: bob.address}); - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: helper.address.normalizeSubstrate(bob.address)}); + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); - // then remove bob from admins of collection - await collection.removeAdmin(alice, {Substrate: bob.address}); + // then remove bob from admins of collection + await collection.removeAdmin(alice, {Substrate: bob.address}); - const adminListAfterRemoveAdmin = await collection.getAdmins(); - expect(adminListAfterRemoveAdmin).not.to.be.deep.contains({Substrate: helper.address.normalizeSubstrate(bob.address)}); - }); + const adminListAfterRemoveAdmin = await collection.getAdmins(); + expect(adminListAfterRemoveAdmin).not.to.be.deep.contains({Substrate: bob.address}); }); - it('Remove admin from collection that has no admins', async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const alice = privateKey('//Alice'); - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + itSub('Remove admin from collection that has no admins', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-2', tokenPrefix: 'RCA'}); - const adminListBeforeAddAdmin = await collection.getAdmins(); - expect(adminListBeforeAddAdmin).to.have.lengthOf(0); + const adminListBeforeAddAdmin = await collection.getAdmins(); + expect(adminListBeforeAddAdmin).to.have.lengthOf(0); - // await expect(collection.removeAdmin(alice, {Substrate: alice.address})).to.be.rejectedWith('Unable to remove collection admin'); - await collection.removeAdmin(alice, {Substrate: alice.address}); - }); + await collection.removeAdmin(alice, {Substrate: alice.address}); }); }); describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => { - it('Can\'t remove collection admin from not existing collection', async () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { await usingPlaygrounds(async (helper, privateKey) => { - // tslint:disable-next-line: no-bitwise - const collectionId = (1 << 32) - 1; - const alice = privateKey('//Alice'); - const bob = privateKey('//Bob'); + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); + }); + }); - await expect(helper.collection.removeAdmin(alice, collectionId, {Substrate: bob.address})).to.be.rejected; + itSub('Can\'t remove collection admin from not existing collection', async ({helper}) => { + const collectionId = (1 << 32) - 1; - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - }); + await expect(helper.collection.removeAdmin(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('Can\'t remove collection admin from deleted collection', async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const alice = privateKey('//Alice'); - const bob = privateKey('//Bob'); - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + itSub('Can\'t remove collection admin from deleted collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-2', tokenPrefix: 'RCA'}); - expect(await collection.burn(alice)).to.be.true; + expect(await collection.burn(alice)).to.be.true; - await expect(helper.collection.removeAdmin(alice, collection.collectionId, {Substrate: bob.address})).to.be.rejected; - - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - }); + await expect(helper.collection.removeAdmin(alice, collection.collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('Regular user can\'t remove collection admin', async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const alice = privateKey('//Alice'); - const bob = privateKey('//Bob'); - const charlie = privateKey('//Charlie'); - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); + itSub('Regular user can\'t remove collection admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-3', tokenPrefix: 'RCA'}); - await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.addAdmin(alice, {Substrate: bob.address}); - await expect(collection.removeAdmin(charlie, {Substrate: bob.address})).to.be.rejected; - - // Verifying that nothing bad happened (network is live, new collections can be created, etc.) - await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - }); + await expect(collection.removeAdmin(charlie, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.NoPermission/); }); - it('Admin can\'t remove collection admin.', async () => { - await usingPlaygrounds(async (helper, privateKey) => { - const alice = privateKey('//Alice'); - const bob = privateKey('//Bob'); - const charlie = privateKey('//Charlie'); - const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}); - - await collection.addAdmin(alice, {Substrate: bob.address}); - await collection.addAdmin(alice, {Substrate: charlie.address}); + itSub('Admin can\'t remove collection admin.', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-4', tokenPrefix: 'RCA'}); + + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.addAdmin(alice, {Substrate: charlie.address}); - const adminListAfterAddAdmin = await collection.getAdmins(); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: helper.address.normalizeSubstrate(bob.address)}); - expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: helper.address.normalizeSubstrate(charlie.address)}); + const adminListAfterAddAdmin = await collection.getAdmins(); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address}); + expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: charlie.address}); - await expect(collection.removeAdmin(charlie, {Substrate: bob.address})).to.be.rejected; + await expect(collection.removeAdmin(charlie, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.NoPermission/); - const adminListAfterRemoveAdmin = await collection.getAdmins(); - expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: helper.address.normalizeSubstrate(bob.address)}); - expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: helper.address.normalizeSubstrate(charlie.address)}); - }); + const adminListAfterRemoveAdmin = await collection.getAdmins(); + expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: bob.address}); + expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: charlie.address}); }); }); --- a/tests/src/removeCollectionSponsor.test.ts +++ b/tests/src/removeCollectionSponsor.test.ts @@ -14,138 +14,112 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {default as usingApi, submitTransactionExpectFailAsync} from './substrate/substrate-api'; -import { - createCollectionExpectSuccess, - setCollectionSponsorExpectSuccess, - destroyCollectionExpectSuccess, - confirmSponsorshipExpectSuccess, - confirmSponsorshipExpectFailure, - createItemExpectSuccess, - findUnusedAddress, - removeCollectionSponsorExpectSuccess, - removeCollectionSponsorExpectFailure, - normalizeAccountId, - addCollectionAdminExpectSuccess, - getCreatedCollectionCount, -} from './util/helpers'; import {IKeyringPair} from '@polkadot/types/types'; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; -let alice: IKeyringPair; -let bob: IKeyringPair; - describe('integration test: ext. removeCollectionSponsor():', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('Removing NFT collection sponsor stops sponsorship', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); - await removeCollectionSponsorExpectSuccess(collectionId); + itSub('Removing NFT collection sponsor stops sponsorship', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-1', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await collection.removeSponsor(alice); - await usingApi(async (api, privateKeyWrapper) => { - // Find unused address - const zeroBalance = await findUnusedAddress(api, privateKeyWrapper); + // Find unused address + const [zeroBalance] = await helper.arrange.createAccounts([0n], donor); - // Mint token for unused address - const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', zeroBalance.address); + // Mint token for unused address + const token = await collection.mintToken(alice, {Substrate: zeroBalance.address}); - // Transfer this tokens from unused address to Alice - should fail - const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt(); - const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0); - const badTransaction = async function () { - await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice); - }; - await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees'); - const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt(); + // Transfer this tokens from unused address to Alice - should fail + const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address); + await expect(token.transfer(zeroBalance, {Substrate: alice.address})) + .to.be.rejectedWith('Inability to pay some fees'); + const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address); - expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore); - }); + expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore); }); - it('Remove a sponsor after it was already removed', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); - await removeCollectionSponsorExpectSuccess(collectionId); - await removeCollectionSponsorExpectSuccess(collectionId); + itSub('Remove a sponsor after it was already removed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-2', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await expect(collection.removeSponsor(alice)).to.not.be.rejected; + await expect(collection.removeSponsor(alice)).to.not.be.rejected; }); - it('Remove sponsor in a collection that never had the sponsor set', async () => { - const collectionId = await createCollectionExpectSuccess(); - await removeCollectionSponsorExpectSuccess(collectionId); + itSub('Remove sponsor in a collection that never had the sponsor set', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-3', tokenPrefix: 'RCS'}); + await expect(collection.removeSponsor(alice)).to.not.be.rejected; }); - it('Remove sponsor for a collection that had the sponsor set, but not confirmed', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await removeCollectionSponsorExpectSuccess(collectionId); + itSub('Remove sponsor for a collection that had the sponsor set, but not confirmed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-4', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await expect(collection.removeSponsor(alice)).to.not.be.rejected; }); }); describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); }); }); - it('(!negative test!) Remove sponsor for a collection that never existed', async () => { - // Find the collection that never existed - let collectionId = 0; - await usingApi(async (api) => { - collectionId = await getCreatedCollectionCount(api) + 1; - }); - - await removeCollectionSponsorExpectFailure(collectionId); + itSub('(!negative test!) Remove sponsor for a collection that never existed', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.collection.removeSponsor(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('(!negative test!) Remove sponsor for a collection with collection admin permissions', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await removeCollectionSponsorExpectFailure(collectionId, '//Bob'); + itSub('(!negative test!) Remove sponsor for a collection with collection admin permissions', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-1', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.addAdmin(alice, {Substrate: charlie.address}); + await expect(collection.removeSponsor(charlie)).to.be.rejectedWith(/common\.NoPermission/); }); - it('(!negative test!) Remove sponsor for a collection by regular user', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await removeCollectionSponsorExpectFailure(collectionId, '//Bob'); + itSub('(!negative test!) Remove sponsor for a collection by regular user', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-2', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await expect(collection.removeSponsor(charlie)).to.be.rejectedWith(/common\.NoPermission/); }); - it('(!negative test!) Remove sponsor in a destroyed collection', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await destroyCollectionExpectSuccess(collectionId); - await removeCollectionSponsorExpectFailure(collectionId); + itSub('(!negative test!) Remove sponsor in a destroyed collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-3', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.burn(alice); + await expect(collection.removeSponsor(alice)).to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('Set - remove - confirm: fails', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await removeCollectionSponsorExpectSuccess(collectionId); - await confirmSponsorshipExpectFailure(collectionId, '//Bob'); + itSub('Set - remove - confirm: fails', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-4', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.removeSponsor(alice); + await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/); }); - it('Set - confirm - remove - confirm: Sponsor cannot come back', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); - await removeCollectionSponsorExpectSuccess(collectionId); - await confirmSponsorshipExpectFailure(collectionId, '//Bob'); + itSub('Set - confirm - remove - confirm: Sponsor cannot come back', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-5', tokenPrefix: 'RCS'}); + await collection.setSponsor(alice, bob.address); + await collection.confirmSponsorship(bob); + await collection.removeSponsor(alice); + await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/); }); - }); --- a/tests/src/removeFromAllowList.test.ts +++ b/tests/src/removeFromAllowList.test.ts @@ -14,58 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {default as usingApi} from './substrate/substrate-api'; -import { - createCollectionExpectSuccess, - destroyCollectionExpectSuccess, - enableAllowListExpectSuccess, - addToAllowListExpectSuccess, - removeFromAllowListExpectSuccess, - isAllowlisted, - findNotExistingCollection, - removeFromAllowListExpectFailure, - disableAllowListExpectSuccess, - normalizeAccountId, - addCollectionAdminExpectSuccess, -} from './util/helpers'; import {IKeyringPair} from '@polkadot/types/types'; - -chai.use(chaiAsPromised); -const expect = chai.expect; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; describe('Integration Test removeFromAllowList', () => { let alice: IKeyringPair; let bob: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('ensure bob is not in allowlist after removal', async () => { - await usingApi(async api => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); + itSub('ensure bob is not in allowlist after removal', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-1', tokenPrefix: 'RFAL'}); - await removeFromAllowListExpectSuccess(alice, collectionId, normalizeAccountId(bob.address)); - expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false; - }); + const collectionInfo = await collection.getData(); + expect(collectionInfo!.raw.permissions.access).to.not.equal('AllowList'); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: bob.address}); + expect(await collection.getAllowList()).to.deep.contains({Substrate: bob.address}); + + await collection.removeFromAllowList(alice, {Substrate: bob.address}); + expect(await collection.getAllowList()).to.be.empty; }); - it('allows removal from collection with unset allowlist status', async () => { - await usingApi(async () => { - const collectionWithoutAllowlistId = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionWithoutAllowlistId); - await addToAllowListExpectSuccess(alice, collectionWithoutAllowlistId, bob.address); - await disableAllowListExpectSuccess(alice, collectionWithoutAllowlistId); + itSub('allows removal from collection with unset allowlist status', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-2', tokenPrefix: 'RFAL'}); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: bob.address}); + expect(await collection.getAllowList()).to.deep.contains({Substrate: bob.address}); - await removeFromAllowListExpectSuccess(alice, collectionWithoutAllowlistId, normalizeAccountId(bob.address)); - }); + await collection.setPermissions(alice, {access: 'Normal'}); + + await collection.removeFromAllowList(alice, {Substrate: bob.address}); + expect(await collection.getAllowList()).to.be.empty; }); }); @@ -74,29 +61,26 @@ let bob: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('fails on removal from not existing collection', async () => { - await usingApi(async (api) => { - const collectionId = await findNotExistingCollection(api); - - await removeFromAllowListExpectFailure(alice, collectionId, normalizeAccountId(bob.address)); - }); + itSub('fails on removal from not existing collection', async ({helper}) => { + const nonExistentCollectionId = (1 << 32) - 1; + await expect(helper.collection.removeFromAllowList(alice, nonExistentCollectionId, {Substrate: alice.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('fails on removal from removed collection', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionId); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); - await destroyCollectionExpectSuccess(collectionId); + itSub('fails on removal from removed collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-3', tokenPrefix: 'RFAL'}); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: bob.address}); - await removeFromAllowListExpectFailure(alice, collectionId, normalizeAccountId(bob.address)); - }); + await collection.burn(alice); + await expect(collection.removeFromAllowList(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); }); @@ -106,41 +90,45 @@ let charlie: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); }); }); - it('ensure address is not in allowlist after removal', async () => { - await usingApi(async api => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await addToAllowListExpectSuccess(alice, collectionId, charlie.address); - await removeFromAllowListExpectSuccess(bob, collectionId, normalizeAccountId(charlie.address)); - expect(await isAllowlisted(api, collectionId, charlie.address)).to.be.false; - }); + itSub('ensure address is not in allowlist after removal', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-4', tokenPrefix: 'RFAL'}); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + await collection.addToAllowList(bob, {Substrate: charlie.address}); + await collection.removeFromAllowList(bob, {Substrate: charlie.address}); + + expect(await collection.getAllowList()).to.be.empty; }); - it('Collection admin allowed to remove from allowlist with unset allowlist status', async () => { - await usingApi(async () => { - const collectionWithoutAllowlistId = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionWithoutAllowlistId); - await addCollectionAdminExpectSuccess(alice, collectionWithoutAllowlistId, bob.address); - await addToAllowListExpectSuccess(alice, collectionWithoutAllowlistId, charlie.address); - await disableAllowListExpectSuccess(alice, collectionWithoutAllowlistId); - await removeFromAllowListExpectSuccess(bob, collectionWithoutAllowlistId, normalizeAccountId(charlie.address)); - }); + itSub('Collection admin allowed to remove from allowlist with unset allowlist status', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-5', tokenPrefix: 'RFAL'}); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.addToAllowList(alice, {Substrate: charlie.address}); + + await collection.setPermissions(bob, {access: 'Normal'}); + await collection.removeFromAllowList(bob, {Substrate: charlie.address}); + + expect(await collection.getAllowList()).to.be.empty; }); - it('Regular user can`t remove from allowlist', async () => { - await usingApi(async () => { - const collectionWithoutAllowlistId = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionWithoutAllowlistId); - await addToAllowListExpectSuccess(alice, collectionWithoutAllowlistId, charlie.address); - await removeFromAllowListExpectFailure(bob, collectionWithoutAllowlistId, normalizeAccountId(charlie.address)); - }); + itSub('Regular user can`t remove from allowlist', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'RemoveFromAllowList-6', tokenPrefix: 'RFAL'}); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: charlie.address}); + + await expect(collection.removeFromAllowList(bob, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.NoPermission/); + expect(await collection.getAllowList()).to.deep.contain({Substrate: charlie.address}); }); }); --- a/tests/src/removeFromContractAllowList.test.ts +++ b/tests/src/removeFromContractAllowList.test.ts @@ -20,6 +20,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {expect} from 'chai'; +// todo:playgrounds skipped again describe.skip('Integration Test removeFromContractAllowList', () => { let bob: IKeyringPair; --- a/tests/src/rpc.test.ts +++ b/tests/src/rpc.test.ts @@ -1,57 +1,68 @@ -import {IKeyringPair} from '@polkadot/types/types'; -import {expect} from 'chai'; -import usingApi from './substrate/substrate-api'; -import {createCollection, createCollectionExpectSuccess, createFungibleItemExpectSuccess, CrossAccountId, getTokenOwner, normalizeAccountId, transfer, U128_MAX} from './util/helpers'; +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. -let alice: IKeyringPair; -let bob: IKeyringPair; +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . +import {IKeyringPair} from '@polkadot/types/types'; +import {usingPlaygrounds, itSub, expect} from './util/playgrounds'; +import {crossAccountIdFromLower} from './util/playgrounds/unique'; + describe('integration test: RPC methods', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); }); }); - - it('returns None for fungible collection', async () => { - await usingApi(async api => { - const collection = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await expect(getTokenOwner(api, collection, 0)).to.be.rejectedWith(/^owner == null$/); - }); + itSub('returns None for fungible collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'RPC-1', tokenPrefix: 'RPC'}); + const owner = (await helper.callRpc('api.rpc.unique.tokenOwner', [collection.collectionId, 0])).toJSON() as any; + expect(owner).to.be.null; }); - it('RPC method tokenOwners for fungible collection and token', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; - const facelessCrowd = Array.from(Array(7).keys()).map(i => normalizeAccountId(privateKeyWrapper(i.toString()))); - - const createCollectionResult = await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}}); - const collectionId = createCollectionResult.collectionId; - const aliceTokenId = await createFungibleItemExpectSuccess(alice, collectionId, {Value: U128_MAX}, alice.address); - - await transfer(api, collectionId, aliceTokenId, alice, bob, 1000n); - await transfer(api, collectionId, aliceTokenId, alice, ethAcc, 900n); - - for (let i = 0; i < 7; i++) { - await transfer(api, collectionId, aliceTokenId, alice, facelessCrowd[i], 1); - } - - const owners = await api.rpc.unique.tokenOwners(collectionId, aliceTokenId); - const ids = (owners.toJSON() as CrossAccountId[]).map(s => normalizeAccountId(s)); - const aliceID = normalizeAccountId(alice); - const bobId = normalizeAccountId(bob); + itSub('RPC method tokenOwners for fungible collection and token', async ({helper}) => { + // Set-up a few token owners of all stripes + const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'}; + const facelessCrowd = (await helper.arrange.createAccounts([0n, 0n, 0n, 0n, 0n, 0n, 0n], donor)) + .map(i => {return {Substrate: i.address};}); + + const collection = await helper.ft.mintCollection(alice, {name: 'RPC-2', tokenPrefix: 'RPC'}); + // mint some maximum (u128) amounts of tokens possible + await collection.mint(alice, (1n << 128n) - 1n); + + await collection.transfer(alice, {Substrate: bob.address}, 1000n); + await collection.transfer(alice, ethAcc, 900n); + + for (let i = 0; i < facelessCrowd.length; i++) { + await collection.transfer(alice, facelessCrowd[i], 1n); + } + // Set-up over + + const owners = await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0]); + const ids = (owners.toJSON() as any[]).map(crossAccountIdFromLower); - // What to expect - // tslint:disable-next-line:no-unused-expression - expect(ids).to.deep.include.members([aliceID, ethAcc, bobId, ...facelessCrowd]); - expect(owners.length == 10).to.be.true; - - const eleven = privateKeyWrapper('11'); - expect(await transfer(api, collectionId, aliceTokenId, alice, eleven, 10n)).to.be.true; - expect((await api.rpc.unique.tokenOwners(collectionId, aliceTokenId)).length).to.be.equal(10); - }); + expect(ids).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]); + expect(owners.length == 10).to.be.true; + + // Make sure only 10 results are returned with this RPC + const [eleven] = await helper.arrange.createAccounts([0n], donor); + expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true; + expect((await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0])).length).to.be.equal(10); }); }); \ No newline at end of file --- a/tests/src/scheduler.test.ts +++ b/tests/src/scheduler.test.ts @@ -44,6 +44,7 @@ chai.use(chaiAsPromised); +// todo:playgrounds skipped ~ postponed describe.skip('Scheduling token and balance transfers', () => { let alice: IKeyringPair; let bob: IKeyringPair; --- a/tests/src/setChainLimits.test.ts +++ b/tests/src/setChainLimits.test.ts @@ -23,6 +23,7 @@ IChainLimits, } from './util/helpers'; +// todo:playgrounds skipped ~ postponed describe.skip('Negative Integration Test setChainLimits', () => { let alice: IKeyringPair; let bob: IKeyringPair; --- a/tests/src/setCollectionLimits.test.ts +++ b/tests/src/setCollectionLimits.test.ts @@ -15,26 +15,8 @@ // along with Unique Network. If not, see . // https://unique-network.readthedocs.io/en/latest/jsapi.html#setchainlimits -import {ApiPromise} from '@polkadot/api'; import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import usingApi, {submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api'; -import { - createCollectionExpectSuccess, getCreatedCollectionCount, - getCreateItemResult, - setCollectionLimitsExpectFailure, - setCollectionLimitsExpectSuccess, - addCollectionAdminExpectSuccess, - queryCollectionExpectSuccess, -} from './util/helpers'; - -chai.use(chaiAsPromised); -const expect = chai.expect; - -let alice: IKeyringPair; -let bob: IKeyringPair; -let collectionIdForTesting: number; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; const accountTokenOwnershipLimit = 0; const sponsoredDataSize = 0; @@ -42,197 +24,177 @@ const tokenLimit = 10; describe('setCollectionLimits positive', () => { - let tx; - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}}); - }); - }); - it('execute setCollectionLimits with predefined params ', async () => { - await usingApi(async (api: ApiPromise) => { - tx = api.tx.unique.setCollectionLimits( - collectionIdForTesting, - { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredDataSize: sponsoredDataSize, - tokenLimit: tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - }, - ); - const events = await submitTransactionAsync(alice, tx); - const result = getCreateItemResult(events); - - // get collection limits defined previously - const collectionInfo = await queryCollectionExpectSuccess(api, collectionIdForTesting); + let alice: IKeyringPair; + let bob: IKeyringPair; - // tslint:disable-next-line:no-unused-expression - expect(result.success).to.be.true; - expect(collectionInfo.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.equal(accountTokenOwnershipLimit); - expect(collectionInfo.limits.sponsoredDataSize.unwrap().toNumber()).to.be.equal(sponsoredDataSize); - expect(collectionInfo.limits.tokenLimit.unwrap().toNumber()).to.be.equal(tokenLimit); - expect(collectionInfo.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.equal(sponsorTransferTimeout); - expect(collectionInfo.limits.ownerCanTransfer.unwrap().toJSON()).to.be.true; - expect(collectionInfo.limits.ownerCanDestroy.unwrap().toJSON()).to.be.true; + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); }); }); - it('Set the same token limit twice', async () => { - await usingApi(async (api: ApiPromise) => { + itSub('execute setCollectionLimits with predefined params', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-1', tokenPrefix: 'SCL'}); - const collectionLimits = { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredMintSize: sponsoredDataSize, - tokenLimit: tokenLimit, + await collection.setLimits( + alice, + { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, sponsorTransferTimeout, ownerCanTransfer: true, ownerCanDestroy: true, - }; + }, + ); - // The first time - const tx1 = api.tx.unique.setCollectionLimits( - collectionIdForTesting, - collectionLimits, - ); - const events1 = await submitTransactionAsync(alice, tx1); - const result1 = getCreateItemResult(events1); - expect(result1.success).to.be.true; - const collectionInfo1 = await queryCollectionExpectSuccess(api, collectionIdForTesting); - expect(collectionInfo1.limits.tokenLimit.unwrap().toNumber()).to.be.equal(tokenLimit); + // get collection limits defined previously + const collectionInfo = await collection.getEffectiveLimits(); + + expect(collectionInfo.accountTokenOwnershipLimit).to.be.equal(accountTokenOwnershipLimit); + expect(collectionInfo.sponsoredDataSize).to.be.equal(sponsoredDataSize); + expect(collectionInfo.tokenLimit).to.be.equal(tokenLimit); + expect(collectionInfo.sponsorTransferTimeout).to.be.equal(sponsorTransferTimeout); + expect(collectionInfo.ownerCanTransfer).to.be.true; + expect(collectionInfo.ownerCanDestroy).to.be.true; + }); + + itSub('Set the same token limit twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-2', tokenPrefix: 'SCL'}); + + const collectionLimits = { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + }; + + await collection.setLimits(alice, collectionLimits); + + const collectionInfo1 = await collection.getEffectiveLimits(); + + expect(collectionInfo1.tokenLimit).to.be.equal(tokenLimit); - // The second time - const tx2 = api.tx.unique.setCollectionLimits( - collectionIdForTesting, - collectionLimits, - ); - const events2 = await submitTransactionAsync(alice, tx2); - const result2 = getCreateItemResult(events2); - expect(result2.success).to.be.true; - const collectionInfo2 = await queryCollectionExpectSuccess(api, collectionIdForTesting); - expect(collectionInfo2.limits.tokenLimit.unwrap().toNumber()).to.be.equal(tokenLimit); - }); + await collection.setLimits(alice, collectionLimits); + const collectionInfo2 = await collection.getEffectiveLimits(); + expect(collectionInfo2.tokenLimit).to.be.equal(tokenLimit); }); - it('execute setCollectionLimits from admin collection', async () => { - await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob.address); - await usingApi(async (api: ApiPromise) => { - tx = api.tx.unique.setCollectionLimits( - collectionIdForTesting, - { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - }, - ); - await expect(submitTransactionAsync(bob, tx)).to.be.not.rejected; - }); + itSub('execute setCollectionLimits from admin collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-3', tokenPrefix: 'SCL'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + const collectionLimits = { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + }; + + await expect(collection.setLimits(alice, collectionLimits)).to.not.be.rejected; }); }); describe('setCollectionLimits negative', () => { - let tx; + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}}); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor); }); }); - it('execute setCollectionLimits for not exists collection', async () => { - await usingApi(async (api: ApiPromise) => { - const collectionCount = await getCreatedCollectionCount(api); - const nonExistedCollectionId = collectionCount + 1; - tx = api.tx.unique.setCollectionLimits( - nonExistedCollectionId, - { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - }, - ); - await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; - }); + + itSub('execute setCollectionLimits for not exists collection', async ({helper}) => { + const nonExistentCollectionId = (1 << 32) - 1; + await expect(helper.collection.setLimits( + alice, + nonExistentCollectionId, + { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + }, + )).to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('execute setCollectionLimits from user who is not owner of this collection', async () => { - await usingApi(async (api: ApiPromise) => { - tx = api.tx.unique.setCollectionLimits( - collectionIdForTesting, - { - accountTokenOwnershipLimit, - sponsoredDataSize, - // sponsoredMintSize, - tokenLimit, - }, - ); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; - }); + + itSub('execute setCollectionLimits from user who is not owner of this collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-1', tokenPrefix: 'SCL'}); + + await expect(collection.setLimits(bob, { + accountTokenOwnershipLimit, + sponsoredDataSize, + // sponsoredMintSize, + tokenLimit, + })).to.be.rejectedWith(/common\.NoPermission/); }); - it('fails when trying to enable OwnerCanTransfer after it was disabled', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionLimitsExpectSuccess(alice, collectionId, { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredMintSize: sponsoredDataSize, - tokenLimit: tokenLimit, + itSub('fails when trying to enable OwnerCanTransfer after it was disabled', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-2', tokenPrefix: 'SCL'}); + + await collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, sponsorTransferTimeout, ownerCanTransfer: false, ownerCanDestroy: true, }); - await setCollectionLimitsExpectFailure(alice, collectionId, { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredMintSize: sponsoredDataSize, - tokenLimit: tokenLimit, + + await expect(collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, sponsorTransferTimeout, ownerCanTransfer: true, ownerCanDestroy: true, - }); + })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/); }); - it('fails when trying to enable OwnerCanDestroy after it was disabled', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionLimitsExpectSuccess(alice, collectionId, { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredMintSize: sponsoredDataSize, - tokenLimit: tokenLimit, + itSub('fails when trying to enable OwnerCanDestroy after it was disabled', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-3', tokenPrefix: 'SCL'}); + + await collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, sponsorTransferTimeout, ownerCanTransfer: true, ownerCanDestroy: false, }); - await setCollectionLimitsExpectFailure(alice, collectionId, { + + await expect(collection.setLimits(alice, { + accountTokenOwnershipLimit, + sponsoredDataSize, + tokenLimit, + sponsorTransferTimeout, + ownerCanTransfer: true, + ownerCanDestroy: true, + })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/); + }); + + itSub('Setting the higher token limit fails', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-4', tokenPrefix: 'SCL'}); + + const collectionLimits = { accountTokenOwnershipLimit: accountTokenOwnershipLimit, sponsoredMintSize: sponsoredDataSize, tokenLimit: tokenLimit, sponsorTransferTimeout, ownerCanTransfer: true, ownerCanDestroy: true, - }); - }); - - it('Setting the higher token limit fails', async () => { - await usingApi(async () => { - - const collectionId = await createCollectionExpectSuccess(); - const collectionLimits = { - accountTokenOwnershipLimit: accountTokenOwnershipLimit, - sponsoredMintSize: sponsoredDataSize, - tokenLimit: tokenLimit, - sponsorTransferTimeout, - ownerCanTransfer: true, - ownerCanDestroy: true, - }; + }; - // The first time - await setCollectionLimitsExpectSuccess(alice, collectionId, collectionLimits); + // The first time + await collection.setLimits(alice, collectionLimits); - // The second time - higher token limit - collectionLimits.tokenLimit += 1; - await setCollectionLimitsExpectFailure(alice, collectionId, collectionLimits); - }); + // The second time - higher token limit + collectionLimits.tokenLimit += 1; + await expect(collection.setLimits(alice, collectionLimits)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/); }); - }); --- a/tests/src/setCollectionSponsor.test.ts +++ b/tests/src/setCollectionSponsor.test.ts @@ -14,93 +14,106 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {default as usingApi} from './substrate/substrate-api'; -import {createCollectionExpectSuccess, - setCollectionSponsorExpectSuccess, - destroyCollectionExpectSuccess, - setCollectionSponsorExpectFailure, - addCollectionAdminExpectSuccess, - getCreatedCollectionCount, - requirePallets, - Pallets, -} from './util/helpers'; import {IKeyringPair} from '@polkadot/types/types'; - -chai.use(chaiAsPromised); - -let alice: IKeyringPair; -let bob: IKeyringPair; -let charlie: IKeyringPair; +import {itSub, usingPlaygrounds, expect, Pallets} from './util/playgrounds'; describe('integration test: ext. setCollectionSponsor():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); }); }); - it('Set NFT collection sponsor', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); + itSub('Set NFT collection sponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-1-NFT', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); }); - it('Set Fungible collection sponsor', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); + + itSub('Set Fungible collection sponsor', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'SetCollectionSponsor-1-FT', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); }); - it('Set ReFungible collection sponsor', async function() { - await requirePallets(this, [Pallets.ReFungible]); - const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); + itSub.ifWithPallets('Set ReFungible collection sponsor', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'SetCollectionSponsor-1-RFT', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); }); - it('Set the same sponsor repeatedly', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); + itSub('Set the same sponsor repeatedly', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-2', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: bob.address, + }); }); - it('Replace collection sponsor', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectSuccess(collectionId, bob.address); - await setCollectionSponsorExpectSuccess(collectionId, charlie.address); + + itSub('Replace collection sponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-3', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected; + await expect(collection.setSponsor(alice, charlie.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: charlie.address, + }); }); - it('Collection admin add sponsor', async () => { - const collectionId = await createCollectionExpectSuccess(); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await setCollectionSponsorExpectSuccess(collectionId, charlie.address, '//Bob'); + + itSub('Collection admin add sponsor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-4', tokenPrefix: 'SCS'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await expect(collection.setSponsor(bob, charlie.address)).to.be.not.rejected; + + expect((await collection.getData())?.raw.sponsorship).to.deep.equal({ + Unconfirmed: charlie.address, + }); }); }); describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 5n], donor); }); }); - it('(!negative test!) Add sponsor with a non-owner', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionSponsorExpectFailure(collectionId, bob.address, '//Bob'); + itSub('(!negative test!) Add sponsor with a non-owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-1', tokenPrefix: 'SCS'}); + await expect(collection.setSponsor(bob, bob.address)) + .to.be.rejectedWith(/common\.NoPermission/); }); - it('(!negative test!) Add sponsor to a collection that never existed', async () => { - // Find the collection that never existed - let collectionId = 0; - await usingApi(async (api) => { - collectionId = await getCreatedCollectionCount(api) + 1; - }); - await setCollectionSponsorExpectFailure(collectionId, bob.address); + itSub('(!negative test!) Add sponsor to a collection that never existed', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.collection.setSponsor(alice, collectionId, bob.address)) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('(!negative test!) Add sponsor to a collection that was destroyed', async () => { - const collectionId = await createCollectionExpectSuccess(); - await destroyCollectionExpectSuccess(collectionId); - await setCollectionSponsorExpectFailure(collectionId, bob.address); + + itSub('(!negative test!) Add sponsor to a collection that was destroyed', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-2', tokenPrefix: 'SCS'}); + await collection.burn(alice); + await expect(collection.setSponsor(alice, bob.address)) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); }); --- a/tests/src/setContractSponsoringRateLimit.test.ts +++ b/tests/src/setContractSponsoringRateLimit.test.ts @@ -25,6 +25,7 @@ setContractSponsoringRateLimitExpectSuccess, } from './util/helpers'; +// todo:playgrounds postponed skipped test describe.skip('Integration Test setContractSponsoringRateLimit', () => { it('ensure sponsored contract can\'t be called twice without pause for free', async () => { await usingApi(async (api, privateKeyWrapper) => { --- a/tests/src/setMintPermission.test.ts +++ b/tests/src/setMintPermission.test.ts @@ -15,65 +15,57 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; -import usingApi from './substrate/substrate-api'; -import { - addToAllowListExpectSuccess, - createCollectionExpectSuccess, - createItemExpectFailure, - createItemExpectSuccess, - destroyCollectionExpectSuccess, - enableAllowListExpectSuccess, - findNotExistingCollection, - setMintPermissionExpectFailure, - setMintPermissionExpectSuccess, - addCollectionAdminExpectSuccess, -} from './util/helpers'; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; describe('Integration Test setMintPermission', () => { let alice: IKeyringPair; let bob: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('ensure allow-listed non-privileged address can mint tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); + itSub('ensure allow-listed non-privileged address can mint tokens', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-1', description: '', tokenPrefix: 'SMP'}); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: bob.address}); - await createItemExpectSuccess(bob, collectionId, 'NFT'); - }); + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected; }); - it('can be enabled twice', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await setMintPermissionExpectSuccess(alice, collectionId, true); - }); + itSub('can be enabled twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-2', description: '', tokenPrefix: 'SMP'}); + expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList'); + + await collection.setPermissions(alice, {mintMode: true}); + await collection.setPermissions(alice, {mintMode: true}); + expect((await collection.getData())?.raw.permissions.mintMode).to.be.true; }); - it('can be disabled twice', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await setMintPermissionExpectSuccess(alice, collectionId, true); - await setMintPermissionExpectSuccess(alice, collectionId, false); - await setMintPermissionExpectSuccess(alice, collectionId, false); - }); + itSub('can be disabled twice', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-3', description: '', tokenPrefix: 'SMP'}); + expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); + + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); + expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); + + await collection.setPermissions(alice, {access: 'Normal', mintMode: false}); + await collection.setPermissions(alice, {access: 'Normal', mintMode: false}); + expect((await collection.getData())?.raw.permissions.access).to.equal('Normal'); + expect((await collection.getData())?.raw.permissions.mintMode).to.equal(false); }); - it('Collection admin success on set', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - await setMintPermissionExpectSuccess(bob, collectionId, true); - }); + itSub('Collection admin success on set', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-4', description: '', tokenPrefix: 'SMP'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + await collection.setPermissions(bob, {access: 'AllowList', mintMode: true}); + + expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList'); + expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true); }); }); @@ -82,41 +74,38 @@ let bob: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('fails on not existing collection', async () => { - await usingApi(async (api) => { - const nonExistingCollection = await findNotExistingCollection(api); - await setMintPermissionExpectFailure(alice, nonExistingCollection, true); - }); + itSub('fails on not existing collection', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.collection.setPermissions(alice, collectionId, {mintMode: true})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('fails on removed collection', async () => { - await usingApi(async () => { - const removedCollectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await destroyCollectionExpectSuccess(removedCollectionId); + itSub('fails on removed collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-Neg-1', tokenPrefix: 'SMP'}); + await collection.burn(alice); - await setMintPermissionExpectFailure(alice, removedCollectionId, true); - }); + await expect(collection.setPermissions(alice, {mintMode: true})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('fails when not collection owner tries to set mint status', async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectFailure(bob, collectionId, true); + itSub('fails when non-owner tries to set mint status', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-Neg-2', tokenPrefix: 'SMP'}); + + await expect(collection.setPermissions(bob, {mintMode: true})) + .to.be.rejectedWith(/common\.NoPermission/); }); - it('ensure non-allow-listed non-privileged address can\'t mint tokens', async () => { - await usingApi(async () => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); - await enableAllowListExpectSuccess(alice, collectionId); - await setMintPermissionExpectSuccess(alice, collectionId, true); + itSub('ensure non-allow-listed non-privileged address can\'t mint tokens', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'SetMintPermission-Neg-3', tokenPrefix: 'SMP'}); + await collection.setPermissions(alice, {mintMode: true}); - await createItemExpectFailure(bob, collectionId, 'NFT'); - }); + await expect(collection.mintToken(bob, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); }); }); --- a/tests/src/setPublicAccessMode.test.ts +++ b/tests/src/setPublicAccessMode.test.ts @@ -15,111 +15,79 @@ // along with Unique Network. If not, see . // https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion -import {ApiPromise} from '@polkadot/api'; import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import usingApi, {submitTransactionExpectFailAsync} from './substrate/substrate-api'; -import { - addToAllowListExpectSuccess, - createCollectionExpectSuccess, - createItemExpectSuccess, - destroyCollectionExpectSuccess, - enablePublicMintingExpectSuccess, - enableAllowListExpectSuccess, - normalizeAccountId, - addCollectionAdminExpectSuccess, - getCreatedCollectionCount, -} from './util/helpers'; - -chai.use(chaiAsPromised); -const expect = chai.expect; - -let alice: IKeyringPair; -let bob: IKeyringPair; +import {itSub, usingPlaygrounds, expect} from './util/playgrounds'; describe('Integration Test setPublicAccessMode(): ', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('Run extrinsic with collection id parameters, set the allowlist mode for the collection', async () => { - await usingApi(async () => { - const collectionId: number = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionId); - await enablePublicMintingExpectSuccess(alice, collectionId); - await addToAllowListExpectSuccess(alice, collectionId, bob.address); - await createItemExpectSuccess(bob, collectionId, 'NFT', bob.address); - }); + itSub('Runs extrinsic with collection id parameters, sets the allowlist mode for the collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-1', tokenPrefix: 'TF'}); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + await collection.addToAllowList(alice, {Substrate: bob.address}); + + await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.not.rejected; + }); + + itSub('Allowlisted collection limits', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-2', tokenPrefix: 'TF'}); + await collection.setPermissions(alice, {access: 'AllowList', mintMode: true}); + + await expect(collection.mintToken(bob, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.AddressNotInAllowlist/); }); - it('Allowlisted collection limits', async () => { - await usingApi(async (api: ApiPromise) => { - const collectionId = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionId); - await enablePublicMintingExpectSuccess(alice, collectionId); - const tx = api.tx.unique.createItem(collectionId, normalizeAccountId(bob.address), 'NFT'); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; - }); + itSub('setPublicAccessMode by collection admin', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-4', tokenPrefix: 'TF'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + await expect(collection.setPermissions(bob, {access: 'AllowList'})).to.be.not.rejected; }); }); describe('Negative Integration Test ext. setPublicAccessMode(): ', () => { - it('Set a non-existent collection', async () => { - await usingApi(async (api: ApiPromise) => { - // tslint:disable-next-line: radix - const collectionId = await getCreatedCollectionCount(api) + 1; - const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: 'AllowList'}); - await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; + let alice: IKeyringPair; + let bob: IKeyringPair; + + before(async () => { + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor); }); }); - it('Set the collection that has been deleted', async () => { - await usingApi(async (api: ApiPromise) => { - // tslint:disable-next-line: no-bitwise - const collectionId = await createCollectionExpectSuccess(); - await destroyCollectionExpectSuccess(collectionId); - const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: 'AllowList'}); - await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; - }); + itSub('Sets a non-existent collection', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList'})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('Re-set the list mode already set in quantity', async () => { - await usingApi(async () => { - const collectionId: number = await createCollectionExpectSuccess(); - await enableAllowListExpectSuccess(alice, collectionId); - await enableAllowListExpectSuccess(alice, collectionId); - }); - }); + itSub('Sets the collection that has been deleted', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-1', tokenPrefix: 'TF'}); + await collection.burn(alice); - it('Execute method not on behalf of the collection owner', async () => { - await usingApi(async (api: ApiPromise) => { - // tslint:disable-next-line: no-bitwise - const collectionId = await createCollectionExpectSuccess(); - const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: 'AllowList'}); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; - }); + await expect(collection.setPermissions(alice, {access: 'AllowList'})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('setPublicAccessMode by collection admin', async () => { - await usingApi(async (api: ApiPromise) => { - // tslint:disable-next-line: no-bitwise - const collectionId = await createCollectionExpectSuccess(); - await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); - const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: 'AllowList'}); - await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.not.rejected; - }); + itSub('Re-sets the list mode already set in quantity', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-2', tokenPrefix: 'TF'}); + await collection.setPermissions(alice, {access: 'AllowList'}); + await collection.setPermissions(alice, {access: 'AllowList'}); }); -}); -describe('Negative Integration Test ext. collection admin setPublicAccessMode(): ', () => { - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - }); + itSub('Executes method as a malefactor', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'PublicAccess-Neg-3', tokenPrefix: 'TF'}); + + await expect(collection.setPermissions(bob, {access: 'AllowList'})) + .to.be.rejectedWith(/common\.NoPermission/); }); }); --- a/tests/src/toggleContractAllowList.test.ts +++ b/tests/src/toggleContractAllowList.test.ts @@ -31,6 +31,7 @@ const value = 0; const gasLimit = 3000n * 1000000n; +// todo:playgrounds skipped ~ postpone describe.skip('Integration Test toggleContractAllowList', () => { it('Enable allow list contract mode', async () => { --- a/tests/src/transfer.test.ts +++ b/tests/src/transfer.test.ts @@ -14,387 +14,314 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {ApiPromise} from '@polkadot/api'; import {IKeyringPair} from '@polkadot/types/types'; -import {expect} from 'chai'; -import getBalance from './substrate/get-balance'; -import {default as usingApi, submitTransactionAsync} from './substrate/substrate-api'; -import { - burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, - destroyCollectionExpectSuccess, - findUnusedAddress, - getCreateCollectionResult, - getCreateItemResult, - transferExpectFailure, - transferExpectSuccess, - addCollectionAdminExpectSuccess, - getCreatedCollectionCount, - toSubstrateAddress, - getTokenOwner, - normalizeAccountId, - getBalance as getTokenBalance, - transferFromExpectSuccess, - transferFromExpectFail, - requirePallets, - Pallets, -} from './util/helpers'; -import { - subToEth, - itWeb3, -} from './eth/util/helpers'; -import {request} from 'https'; - -let alice: IKeyringPair; -let bob: IKeyringPair; -let charlie: IKeyringPair; +import {itEth, usingEthPlaygrounds} from './eth/util/playgrounds'; +import {itSub, Pallets, usingPlaygrounds, expect} from './util/playgrounds'; describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); }); }); - it('Balance transfers and check balance', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alice.address, bob.address]); + itSub('Balance transfers and check balance', async ({helper}) => { + const alicesBalanceBefore = await helper.balance.getSubstrate(alice.address); + const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - const transfer = api.tx.balances.transfer(bob.address, 1n); - const events = await submitTransactionAsync(alice, transfer); - const result = getCreateItemResult(events); - // tslint:disable-next-line:no-unused-expression - expect(result.success).to.be.true; + expect(await helper.balance.transferToSubstrate(alice, bob.address, 1n)).to.be.true; - const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alice.address, bob.address]); + const alicesBalanceAfter = await helper.balance.getSubstrate(alice.address); + const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address); - // tslint:disable-next-line:no-unused-expression - expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true; - // tslint:disable-next-line:no-unused-expression - expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true; - }); + expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true; + expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true; }); - it('Inability to pay fees error message is correct', async () => { - await usingApi(async (api, privateKeyWrapper) => { - // Find unused address - const pk = await findUnusedAddress(api, privateKeyWrapper); + itSub('Inability to pay fees error message is correct', async ({helper, privateKey}) => { + const donor = privateKey('//Alice'); + const [zero] = await helper.arrange.createAccounts([0n], donor); - const badTransfer = api.tx.balances.transfer(bob.address, 1n); - // const events = await submitTransactionAsync(pk, badTransfer); - const badTransaction = async () => { - const events = await submitTransactionAsync(pk, badTransfer); - const result = getCreateCollectionResult(events); - // tslint:disable-next-line:no-unused-expression - expect(result.success).to.be.false; - }; - await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low'); - }); + // console.error = () => {}; + // The following operation throws an error into the console and the logs. Pay it no heed as long as the test succeeds. + await expect(helper.balance.transferToSubstrate(zero, donor.address, 1n)) + .to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low'); }); - it('[nft] User can transfer owned token', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 'NFT'); + itSub('[nft] User can transfer owned token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-1-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await nft.transfer(alice, {Substrate: bob.address}); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: bob.address}); }); - it('[fungible] User can transfer owned token', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob, 1, 'Fungible'); + itSub('[fungible] User can transfer owned token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-1-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await collection.transfer(alice, {Substrate: bob.address}, 9n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); }); - it('[refungible] User can transfer owned token', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub.ifWithPallets('[refungible] User can transfer owned token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await transferExpectSuccess( - reFungibleCollectionId, - newReFungibleTokenId, - alice, - bob, - 100, - 'ReFungible', - ); + await rft.transfer(alice, {Substrate: bob.address}, 9n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n); }); - it('[nft] Collection admin can transfer owned token', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - await addCollectionAdminExpectSuccess(alice, nftCollectionId, bob.address); - const newNftTokenId = await createItemExpectSuccess(bob, nftCollectionId, 'NFT', bob.address); - await transferExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, 1, 'NFT'); + itSub('[nft] Collection admin can transfer owned token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-2-NFT', description: '', tokenPrefix: 'T'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + const nft = await collection.mintToken(bob, {Substrate: bob.address}); + await nft.transfer(bob, {Substrate: alice.address}); + + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); }); - it('[fungible] Collection admin can transfer owned token', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await addCollectionAdminExpectSuccess(alice, fungibleCollectionId, bob.address); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible', bob.address); - await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, 1, 'Fungible'); + itSub('[fungible] Collection admin can transfer owned token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-2-FT', description: '', tokenPrefix: 'T'}); + await collection.addAdmin(alice, {Substrate: bob.address}); + + await collection.mint(bob, 10n, {Substrate: bob.address}); + await collection.transfer(bob, {Substrate: alice.address}, 1n); + + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n); }); - it('[refungible] Collection admin can transfer owned token', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub.ifWithPallets('[refungible] Collection admin can transfer owned token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-2-RFT', description: '', tokenPrefix: 'T'}); + await collection.addAdmin(alice, {Substrate: bob.address}); - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await addCollectionAdminExpectSuccess(alice, reFungibleCollectionId, bob.address); - const newReFungibleTokenId = await createItemExpectSuccess(bob, reFungibleCollectionId, 'ReFungible', bob.address); - await transferExpectSuccess( - reFungibleCollectionId, - newReFungibleTokenId, - bob, - alice, - 100, - 'ReFungible', - ); + const rft = await collection.mintToken(bob, 10n, {Substrate: bob.address}); + await rft.transfer(bob, {Substrate: alice.address}, 1n); + + expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n); }); }); describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor); }); }); - it('[nft] Transfer with not existed collection_id', async () => { - await usingApi(async (api) => { - const nftCollectionCount = await getCreatedCollectionCount(api); - await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1); - }); + itSub('[nft] Transfer with not existed collection_id', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.nft.transferToken(alice, collectionId, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('[fungible] Transfer with not existed collection_id', async () => { - await usingApi(async (api) => { - const fungibleCollectionCount = await getCreatedCollectionCount(api); - await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1); - }); + itSub('[fungible] Transfer with not existed collection_id', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.ft.transfer(alice, collectionId, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + itSub.ifWithPallets('[refungible] Transfer with not existed collection_id', [Pallets.ReFungible], async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.rft.transferToken(alice, collectionId, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('[refungible] Transfer with not existed collection_id', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub('[nft] Transfer with deleted collection_id', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-1-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); - await usingApi(async (api) => { - const reFungibleCollectionCount = await getCreatedCollectionCount(api); - await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1); - }); - }); + await nft.burn(alice); + await collection.burn(alice); - it('[nft] Transfer with deleted collection_id', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId); - await destroyCollectionExpectSuccess(nftCollectionId); - await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1); + await expect(nft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('[fungible] Transfer with deleted collection_id', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10); - await destroyCollectionExpectSuccess(fungibleCollectionId); - await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1); - }); + itSub('[fungible] Transfer with deleted collection_id', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-1-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); - it('[refungible] Transfer with deleted collection_id', async function() { - await requirePallets(this, [Pallets.ReFungible]); + await collection.burnTokens(alice, 10n); + await collection.burn(alice); - const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100); - await destroyCollectionExpectSuccess(reFungibleCollectionId); - await transferExpectFailure( - reFungibleCollectionId, - newReFungibleTokenId, - alice, - bob, - 1, - ); + await expect(collection.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); + + itSub.ifWithPallets('[refungible] Transfer with deleted collection_id', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-1-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); - it('[nft] Transfer with not existed item_id', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - await transferExpectFailure(nftCollectionId, 2, alice, bob, 1); + await rft.burn(alice, 10n); + await collection.burn(alice); + + await expect(rft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.CollectionNotFound/); }); - it('[fungible] Transfer with not existed item_id', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await transferExpectFailure(fungibleCollectionId, 2, alice, bob, 1); + itSub('[nft] Transfer with not existed item_id', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-2-NFT', description: '', tokenPrefix: 'T'}); + await expect(collection.transferToken(alice, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenNotFound/); }); - it('[refungible] Transfer with not existed item_id', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub('[fungible] Transfer with not existed item_id', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-2-FT', description: '', tokenPrefix: 'T'}); + await expect(collection.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + }); - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await transferExpectFailure( - reFungibleCollectionId, - 2, - alice, - bob, - 1, - ); + itSub.ifWithPallets('[refungible] Transfer with not existed item_id', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-2-RFT', description: '', tokenPrefix: 'T'}); + await expect(collection.transferToken(alice, 1, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('[nft] Transfer with deleted item_id', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1); - await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1); + itSub('[nft] Transfer with deleted item_id', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await nft.burn(alice); + + await expect(nft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenNotFound/); }); - it('[fungible] Transfer with deleted item_id', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10); - await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, alice, bob, 1); + itSub('[fungible] Transfer with deleted item_id', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-3-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await collection.burnTokens(alice, 10n); + + await expect(collection.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('[refungible] Transfer with deleted item_id', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub.ifWithPallets('[refungible] Transfer with deleted item_id', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-3-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); - const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100); - await transferExpectFailure( - reFungibleCollectionId, - newReFungibleTokenId, - alice, - bob, - 1, - ); + await rft.burn(alice, 10n); + + await expect(rft.transfer(alice, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('[nft] Transfer with recipient that is not owner', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await transferExpectFailure(nftCollectionId, newNftTokenId, charlie, bob, 1); + itSub('[nft] Transfer with recipient that is not owner', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-4-NFT', description: '', tokenPrefix: 'T'}); + const nft = await collection.mintToken(alice); + + await expect(nft.transfer(bob, {Substrate: bob.address})) + .to.be.rejectedWith(/common\.NoPermission/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); }); - it('[fungible] Transfer with recipient that is not owner', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await transferExpectFailure(fungibleCollectionId, newFungibleTokenId, charlie, bob, 1); + itSub('[fungible] Transfer with recipient that is not owner', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-4-FT', description: '', tokenPrefix: 'T'}); + await collection.mint(alice, 10n); + + await expect(collection.transfer(bob, {Substrate: bob.address}, 9n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(10n); }); - it('[refungible] Transfer with recipient that is not owner', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub.ifWithPallets('[refungible] Transfer with recipient that is not owner', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'}); + const rft = await collection.mintToken(alice, 10n); - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await transferExpectFailure( - reFungibleCollectionId, - newReFungibleTokenId, - charlie, - bob, - 1, - ); + await expect(rft.transfer(bob, {Substrate: bob.address}, 9n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(0n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(10n); }); }); -describe('Zero value transfer(From)', () => { - before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); +describe('Transfers to self (potentially over substrate-evm boundary)', () => { + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_, privateKey) => { + donor = privateKey('//Alice'); }); }); + + itEth('Transfers to self. In case of same frontend', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 100n); - it('NFT', async () => { - await usingApi(async (api: ApiPromise) => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); + const ownerProxy = helper.address.substrateToEth(owner.address); - const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0); - await submitTransactionAsync(alice, transferTx); - const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId)); + // transfer to own proxy + await collection.transfer(owner, {Ethereum: ownerProxy}, 10n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); - expect(toSubstrateAddress(address)).to.be.equal(alice.address); - }); + // transfer-from own proxy to own proxy again + await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Ethereum: ownerProxy}, 5n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); }); - - it('RFT', async function() { - await requirePallets(this, [Pallets.ReFungible]); - await usingApi(async (api: ApiPromise) => { - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId); - const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId); + itEth('Transfers to self. In case of substrate-evm boundary', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 100n); - const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0); - await submitTransactionAsync(alice, transferTx); + const ownerProxy = helper.address.substrateToEth(owner.address); - const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId); - const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId); + // transfer to own proxy + await collection.transfer(owner, {Ethereum: ownerProxy}, 10n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n); - expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice); - expect((balanceBeforeBob)).to.be.equal(balanceAfterBob); - }); + // transfer-from own proxy to self + await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Substrate: owner.address}, 5n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(95n); + expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(5n); }); - - it('Fungible', async () => { - await usingApi(async (api: ApiPromise) => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId); - const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId); - const transferTx = api.tx.unique.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0); - await submitTransactionAsync(alice, transferTx); - - const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId); - const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId); + itEth('Transfers to self. In case of inside substrate-evm', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 100n); - expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice); - expect((balanceBeforeBob)).to.be.equal(balanceAfterBob); - }); - }); -}); + // transfer to self again + await collection.transfer(owner, {Substrate: owner.address}, 10n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n); -describe('Transfers to self (potentially over substrate-evm boundary)', () => { - itWeb3('Transfers to self. In case of same frontend', async ({api, privateKeyWrapper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const aliceProxy = subToEth(alice.address); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address}); - await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, 10, 'Fungible'); - const balanceAliceBefore = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId); - await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, {Ethereum: aliceProxy}, 10, 'Fungible'); - const balanceAliceAfter = await getTokenBalance(api, collectionId, {Ethereum: aliceProxy}, tokenId); - expect(balanceAliceBefore).to.be.eq(balanceAliceAfter); + // transfer-from self to self again + await collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 5n); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n); }); - itWeb3('Transfers to self. In case of substrate-evm boundary', async ({api, privateKeyWrapper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const aliceProxy = subToEth(alice.address); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address}); - const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId); - await transferExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy} , 10, 'Fungible'); - await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: aliceProxy}, alice, 10, 'Fungible'); - const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId); - expect(balanceAliceBefore).to.be.eq(balanceAliceAfter); - }); + itEth('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({helper}) => { + const [owner] = await helper.arrange.createAccounts([10n], donor); + const collection = await helper.ft.mintCollection(owner, {}); + await collection.mint(owner, 10n); - itWeb3('Transfers to self. In case of inside substrate-evm', async ({api, privateKeyWrapper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address}); - const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId); - await transferExpectSuccess(collectionId, tokenId, alice, alice , 10, 'Fungible'); - await transferFromExpectSuccess(collectionId, tokenId, alice, alice, alice, 10, 'Fungible'); - const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId); - expect(balanceAliceBefore).to.be.eq(balanceAliceAfter); - }); + // transfer to self again + await expect(collection.transfer(owner, {Substrate: owner.address}, 11n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); - itWeb3('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({api, privateKeyWrapper}) => { - const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible', {Substrate: alice.address}); - const balanceAliceBefore = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId); - await transferExpectFailure(collectionId, tokenId, alice, alice , 11); - await transferFromExpectFail(collectionId, tokenId, alice, alice, alice, 11); - const balanceAliceAfter = await getTokenBalance(api, collectionId, normalizeAccountId(alice), tokenId); - expect(balanceAliceBefore).to.be.eq(balanceAliceAfter); + // transfer-from self to self again + await expect(collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 12n)) + .to.be.rejectedWith(/common\.TokenValueTooLow/); + expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(10n); }); }); --- a/tests/src/transferFrom.test.ts +++ b/tests/src/transferFrom.test.ts @@ -14,97 +14,73 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {ApiPromise} from '@polkadot/api'; import {IKeyringPair} from '@polkadot/types/types'; -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {default as usingApi} from './substrate/substrate-api'; -import { - approveExpectFail, - approveExpectSuccess, - createCollectionExpectSuccess, - createFungibleItemExpectSuccess, - createItemExpectSuccess, - getAllowance, - transferFromExpectFail, - transferFromExpectSuccess, - burnItemExpectSuccess, - setCollectionLimitsExpectSuccess, - getCreatedCollectionCount, - requirePallets, - Pallets, -} from './util/helpers'; +import {itSub, Pallets, usingPlaygrounds, expect} from './util/playgrounds'; -chai.use(chaiAsPromised); -const expect = chai.expect; - describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => { let alice: IKeyringPair; let bob: IKeyringPair; let charlie: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor); }); }); - it('[nft] Execute the extrinsic and check nftItemList - owner of token', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address); + itSub('[nft] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-1', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + await nft.approve(alice, {Substrate: bob.address}); + expect(await nft.isApproved({Substrate: bob.address})).to.be.true; - await transferFromExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, charlie, 1, 'NFT'); + await nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address}); }); - it('[fungible] Execute the extrinsic and check nftItemList - owner of token', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address); - await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1, 'Fungible'); + itSub('[fungible] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-2', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); + await collection.approveTokens(alice, {Substrate: bob.address}, 7n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n); + + await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.equal(6n); + expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(4n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n); }); - - it('[refungible] Execute the extrinsic and check nftItemList - owner of token', async function() { - await requirePallets(this, [Pallets.ReFungible]); - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 100); - await transferFromExpectSuccess( - reFungibleCollectionId, - newReFungibleTokenId, - bob, - alice, - charlie, - 100, - 'ReFungible', - ); + itSub.ifWithPallets('[refungible] Execute the extrinsic and check nftItemList - owner of token', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-3', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); + await rft.approve(alice, {Substrate: bob.address}, 7n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n); + + await rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.equal(6n); + expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(4n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n); }); - it('Should reduce allowance if value is big', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const alice = privateKeyWrapper('//Alice'); - const bob = privateKeyWrapper('//Bob'); - const charlie = privateKeyWrapper('//Charlie'); - - // fungible - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createFungibleItemExpectSuccess(alice, fungibleCollectionId, {Value: 500000n}); + itSub('Should reduce allowance if value is big', async ({helper}) => { + // fungible + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-4', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 500000n); - await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 500000n); - await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 500000n, 'Fungible'); - expect(await getAllowance(api, fungibleCollectionId, alice.address, bob.address, newFungibleTokenId)).to.equal(0n); - }); + await collection.approveTokens(alice, {Substrate: bob.address}, 500000n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(500000n); + await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 500000n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(0n); }); - it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => { - const collectionId = await createCollectionExpectSuccess(); - await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true}); - const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address); + itSub('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-5', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); - await transferFromExpectSuccess(collectionId, itemId, alice, bob, charlie); + const nft = await collection.mintToken(alice, {Substrate: bob.address}); + await nft.transferFrom(alice, {Substrate: bob.address}, {Substrate: charlie.address}); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address}); }); }); @@ -114,245 +90,263 @@ let charlie: IKeyringPair; before(async () => { - await usingApi(async (api, privateKeyWrapper) => { - alice = privateKeyWrapper('//Alice'); - bob = privateKeyWrapper('//Bob'); - charlie = privateKeyWrapper('//Charlie'); + await usingPlaygrounds(async (helper, privateKey) => { + const donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor); }); }); - it('[nft] transferFrom for a collection that does not exist', async () => { - await usingApi(async (api: ApiPromise) => { - const nftCollectionCount = await getCreatedCollectionCount(api); - await approveExpectFail(nftCollectionCount + 1, 1, alice, bob); + itSub('transferFrom for a collection that does not exist', async ({helper}) => { + const collectionId = (1 << 32) - 1; + await expect(helper.collection.approveToken(alice, collectionId, 0, {Substrate: bob.address}, 1n)) + .to.be.rejectedWith(/common\.CollectionNotFound/); + await expect(helper.collection.transferTokenFrom(bob, collectionId, 0, {Substrate: alice.address}, {Substrate: bob.address}, 1n)) + .to.be.rejectedWith(/common\.CollectionNotFound/); + }); + + /* itSub('transferFrom for a collection that was destroyed', async ({helper}) => { + this test copies approve negative test + }); */ + + /* itSub('transferFrom a token that does not exist', async ({helper}) => { + this test copies approve negative test + }); */ + + /* itSub('transferFrom a token that was deleted', async ({helper}) => { + this test copies approve negative test + }); */ + + itSub('[nft] transferFrom for not approved address', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + + await expect(nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); + }); + + itSub('[fungible] transferFrom for not approved address', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); - await transferFromExpectFail(nftCollectionCount + 1, 1, bob, alice, charlie, 1); - }); + await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); }); - it('[fungible] transferFrom for a collection that does not exist', async () => { - await usingApi(async (api: ApiPromise) => { - const fungibleCollectionCount = await getCreatedCollectionCount(api); - await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob); + itSub.ifWithPallets('[refungible] transferFrom for not approved address', [Pallets.ReFungible], async({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-3', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); - await transferFromExpectFail(fungibleCollectionCount + 1, 0, bob, alice, charlie, 1); - }); + await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address})) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); }); - it('[refungible] transferFrom for a collection that does not exist', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub('[nft] transferFrom incorrect token count', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-4', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); - await usingApi(async (api: ApiPromise) => { - const reFungibleCollectionCount = await getCreatedCollectionCount(api); - await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob); + await nft.approve(alice, {Substrate: bob.address}); + expect(await nft.isApproved({Substrate: bob.address})).to.be.true; - await transferFromExpectFail(reFungibleCollectionCount + 1, 1, bob, alice, charlie, 1); - }); + await expect(helper.collection.transferTokenFrom( + bob, + collection.collectionId, + nft.tokenId, + {Substrate: alice.address}, + {Substrate: charlie.address}, + 2n, + )).to.be.rejectedWith(/nonfungible\.NonfungibleItemsHaveNoAmount/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); }); - /* it('transferFrom for a collection that was destroyed', async () => { - await usingApi(async (api: ApiPromise) => { - this test copies approve negative test - }); - }); */ + itSub('[fungible] transferFrom incorrect token count', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-5', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); - /* it('transferFrom a token that does not exist', async () => { - await usingApi(async (api: ApiPromise) => { - this test copies approve negative test - }); - }); */ + await collection.approveTokens(alice, {Substrate: bob.address}, 2n); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(2n); - /* it('transferFrom a token that was deleted', async () => { - await usingApi(async (api: ApiPromise) => { - this test copies approve negative test - }); - }); */ + await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); + }); + + itSub.ifWithPallets('[refungible] transferFrom incorrect token count', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-6', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); - it('[nft] transferFrom for not approved address', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); + await rft.approve(alice, {Substrate: bob.address}, 5n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(5n); - await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1); + await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 7n)) + .to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); }); - it('[fungible] transferFrom for not approved address', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1); + itSub('[nft] execute transferFrom from account that is not owner of collection', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-7', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); + + await expect(nft.approve(charlie, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + expect(await nft.isApproved({Substrate: bob.address})).to.be.false; + + await expect(nft.transferFrom( + charlie, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address}); }); - it('[refungible] transferFrom for not approved address', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub('[fungible] execute transferFrom from account that is not owner of collection', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-8', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10000n); + + await expect(collection.approveTokens(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n); + expect(await collection.getApprovedTokens({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n); - const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await transferFromExpectFail( - reFungibleCollectionId, - newReFungibleTokenId, - bob, - alice, + await expect(collection.transferFrom( charlie, - 1, - ); + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n); + expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); }); - it('[nft] transferFrom incorrect token count', async () => { - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address); + itSub.ifWithPallets('[refungible] execute transferFrom from account that is not owner of collection', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-9', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10000n); - await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 2); - }); + await expect(rft.approve(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n); + expect(await rft.getApprovedPieces({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n); - it('[fungible] transferFrom incorrect token count', async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address); - await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 2); + await expect(rft.transferFrom( + charlie, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n); + expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n); + expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n); }); - it('[refungible] transferFrom incorrect token count', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub('transferFrom burnt token before approve NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-10', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + const nft = await collection.mintToken(alice); - const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address); - await transferFromExpectFail( - reFungibleCollectionId, - newReFungibleTokenId, + await nft.burn(alice); + await expect(nft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TokenNotFound/); + + await expect(nft.transferFrom( bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); + }); + + itSub('transferFrom burnt token before approve Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-11', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + await collection.mint(alice, 10n); + + await collection.burnTokens(alice, 10n); + await expect(collection.approveTokens(alice, {Substrate: bob.address})).to.be.not.rejected; + + await expect(collection.transferFrom( alice, - charlie, - 2, - ); + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('[nft] execute transferFrom from account that is not owner of collection', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const dave = privateKeyWrapper('//Dave'); - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - try { - await approveExpectFail(nftCollectionId, newNftTokenId, dave, bob); - await transferFromExpectFail(nftCollectionId, newNftTokenId, dave, alice, charlie, 1); - } catch (e) { - // tslint:disable-next-line:no-unused-expression - expect(e).to.be.exist; - } + itSub.ifWithPallets('transferFrom burnt token before approve ReFungible', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-12', description: '', tokenPrefix: 'TF'}); + await collection.setLimits(alice, {ownerCanTransfer: true}); + const rft = await collection.mintToken(alice, 10n); + + await rft.burn(alice, 10n); + await expect(rft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/); - // await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1); - }); + await expect(rft.transferFrom( + alice, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('[fungible] execute transferFrom from account that is not owner of collection', async () => { - await usingApi(async (api, privateKeyWrapper) => { - const dave = privateKeyWrapper('//Dave'); + itSub('transferFrom burnt token after approve NFT', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-13', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice); - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - try { - await approveExpectFail(fungibleCollectionId, newFungibleTokenId, dave, bob); - await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, dave, alice, charlie, 1); - } catch (e) { - // tslint:disable-next-line:no-unused-expression - expect(e).to.be.exist; - } - }); - }); + await nft.approve(alice, {Substrate: bob.address}); + expect(await nft.isApproved({Substrate: bob.address})).to.be.true; - it('[refungible] execute transferFrom from account that is not owner of collection', async function() { - await requirePallets(this, [Pallets.ReFungible]); + await nft.burn(alice); - await usingApi(async (api, privateKeyWrapper) => { - const dave = privateKeyWrapper('//Dave'); - const reFungibleCollectionId = await - createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - try { - await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, dave, bob); - await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, dave, alice, charlie, 1); - } catch (e) { - // tslint:disable-next-line:no-unused-expression - expect(e).to.be.exist; - } - }); - }); - it('transferFrom burnt token before approve NFT', async () => { - await usingApi(async () => { - // nft - const nftCollectionId = await createCollectionExpectSuccess(); - await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {ownerCanTransfer: true}); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1); - await approveExpectFail(nftCollectionId, newNftTokenId, alice, bob); - await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1); - }); + await expect(nft.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); }); - it('transferFrom burnt token before approve Fungible', async () => { - await usingApi(async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - await setCollectionLimitsExpectSuccess(alice, fungibleCollectionId, {ownerCanTransfer: true}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10); - await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address); - await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1); - }); - }); - it('transferFrom burnt token before approve ReFungible', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub('transferFrom burnt token after approve Fungible', async ({helper}) => { + const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-14', description: '', tokenPrefix: 'TF'}); + await collection.mint(alice, 10n); - await usingApi(async () => { - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - await setCollectionLimitsExpectSuccess(alice, reFungibleCollectionId, {ownerCanTransfer: true}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100); - await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, alice, bob); - await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice, charlie, 1); + await collection.approveTokens(alice, {Substrate: bob.address}); + expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(1n); - }); - }); + await collection.burnTokens(alice, 10n); - it('transferFrom burnt token after approve NFT', async () => { - await usingApi(async () => { - // nft - const nftCollectionId = await createCollectionExpectSuccess(); - const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT'); - await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address); - await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1); - await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1); - }); + await expect(collection.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.TokenValueTooLow/); }); - it('transferFrom burnt token after approve Fungible', async () => { - await usingApi(async () => { - const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); - const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible'); - await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address); - await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10); - await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1); - }); - }); - it('transferFrom burnt token after approve ReFungible', async function() { - await requirePallets(this, [Pallets.ReFungible]); + itSub.ifWithPallets('transferFrom burnt token after approve ReFungible', [Pallets.ReFungible], async ({helper}) => { + const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-15', description: '', tokenPrefix: 'TF'}); + const rft = await collection.mintToken(alice, 10n); - await usingApi(async () => { - const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); - const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible'); - await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address); - await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100); - await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice, charlie, 1); + await rft.approve(alice, {Substrate: bob.address}, 10n); + expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(10n); + + await rft.burn(alice, 10n); - }); + await expect(rft.transferFrom( + bob, + {Substrate: alice.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); }); - it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => { - const collectionId = await createCollectionExpectSuccess(); - const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address); - await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false}); + itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => { + const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-16', description: '', tokenPrefix: 'TF'}); + const nft = await collection.mintToken(alice, {Substrate: bob.address}); - await transferFromExpectFail(collectionId, itemId, alice, bob, charlie); + await collection.setLimits(alice, {ownerCanTransfer: false}); + + await expect(nft.transferFrom( + alice, + {Substrate: bob.address}, + {Substrate: charlie.address}, + )).to.be.rejectedWith(/common\.ApprovedValueTooLow/); }); }); --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -1651,7 +1651,7 @@ }); } -export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId) { +export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId | IKeyringPair) { return (await api.rpc.unique.allowed(collectionId, normalizeAccountId(address))).toJSON(); } --- a/tests/src/util/playgrounds/index.ts +++ b/tests/src/util/playgrounds/index.ts @@ -2,12 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import {IKeyringPair} from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; import {Context} from 'mocha'; import config from '../../config'; import '../../interfaces/augment-api-events'; import {DevUniqueHelper, SilentLogger, SilentConsole} from './unique.dev'; +chai.use(chaiAsPromised); +export const expect = chai.expect; + export const usingPlaygrounds = async (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise) => { const silentConsole = new SilentConsole(); silentConsole.enable(); --- a/tests/src/util/playgrounds/unique.dev.ts +++ b/tests/src/util/playgrounds/unique.dev.ts @@ -257,6 +257,7 @@ } async forParachainBlockNumber(blockNumber: bigint) { + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve) => { const unsubscribe = await this.helper.api!.rpc.chain.subscribeNewHeads(async (data: any) => { if (data.number.toNumber() >= blockNumber) { @@ -268,6 +269,7 @@ } async forRelayBlockNumber(blockNumber: bigint) { + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve) => { const unsubscribe = await this.helper.api!.query.parachainSystem.validationData(async (data: any) => { if (data.value.relayParentNumber.toNumber() >= blockNumber) { --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -730,6 +730,18 @@ } /** + * Check if user is in allow list. + * + * @param collectionId ID of collection + * @param user Account to check + * @example await getAdmins(1) + * @returns is user in allow list + */ + async allowed(collectionId: number, user: ICrossAccountId): Promise { + return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON(); + } + + /** * Adds an address to allow list * @param signer keyring of signer * @param collectionId ID of collection @@ -1666,7 +1678,7 @@ * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n); * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async mintTokens(signer: TSigner, collectionId: number, owner: ICrossAccountId | string, amount: bigint): Promise { + async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise { const creationResult = await this.helper.executeExtrinsic( signer, 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, { @@ -1687,7 +1699,7 @@ * @param tokens array of tokens with properties and pieces * @returns ```true``` if extrinsic success, otherwise ```false``` */ - async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {value: bigint}[]): Promise { + async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise { const rawTokens = []; for (const token of tokens) { const raw = {Fungible: {Value: token.value}}; @@ -2114,10 +2126,6 @@ async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) { return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj); - } - - async enableCertainPermissions(signer: TSigner, accessMode: 'AllowList' | 'Normal' | undefined = 'AllowList', mintMode: boolean | undefined = true) { - return await this.setPermissions(signer, {access: accessMode, mintMode: mintMode}); } async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) { @@ -2203,7 +2211,7 @@ return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj); } - async mintToken(signer: TSigner, owner: ICrossAccountId, properties?: IProperty[]) { + async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) { return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties}); } @@ -2278,11 +2286,11 @@ return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount); } - async mintToken(signer: TSigner, owner: ICrossAccountId, pieces=100n, properties?: IProperty[]) { + async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) { return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties}); } - async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]) { + async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) { return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens); } @@ -2305,12 +2313,12 @@ class UniqueFTCollection extends UniqueCollectionBase { - async mint(signer: TSigner, owner: ICrossAccountId, amount: bigint) { - return await this.helper.ft.mintTokens(signer, this.collectionId, owner, amount); + async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) { + return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner); } - async mintWithOneOwner(signer: TSigner, owner: ICrossAccountId, tokens: {value: bigint}[]) { - return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, owner, tokens); + async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) { + return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner); } async getBalance(addressObj: ICrossAccountId) { --- a/tests/src/xcmTransfer.test.ts +++ b/tests/src/xcmTransfer.test.ts @@ -33,6 +33,7 @@ const KARURA_PORT = '9946'; const TRANSFER_AMOUNT = 2000000000000000000000000n; +// todo:playgrounds refit when XCM drops describe.skip('Integration test: Exchanging QTZ with Karura', () => { let alice: IKeyringPair; --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -17,47 +17,47 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== +"@babel/compat-data@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.0.tgz#2a592fd89bacb1fcde68de31bee4f2f2dacb0e86" + integrity sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw== "@babel/core@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" - integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.0.tgz#d2f5f4f2033c00de8096be3c9f45772563e150c3" + integrity sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.10" + "@babel/generator" "^7.19.0" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-module-transforms" "^7.19.0" + "@babel/helpers" "^7.19.0" + "@babel/parser" "^7.19.0" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.10" - "@babel/types" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.18.10": - version "7.18.12" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" - integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== +"@babel/generator@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" + integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== dependencies: - "@babel/types" "^7.18.10" + "@babel/types" "^7.19.0" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== +"@babel/helper-compilation-targets@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz#537ec8339d53e806ed422f1e06c8f17d55b96bb0" + integrity sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA== dependencies: - "@babel/compat-data" "^7.18.8" + "@babel/compat-data" "^7.19.0" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.20.2" semver "^6.3.0" @@ -67,13 +67,13 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -89,19 +89,19 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== +"@babel/helper-module-transforms@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" + integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-simple-access" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" "@babel/helper-simple-access@^7.18.6": version "7.18.6" @@ -132,14 +132,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helpers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" - integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== +"@babel/helpers@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" + integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== dependencies: - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" "@babel/highlight@^7.18.6": version "7.18.6" @@ -150,10 +150,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.18.11": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" - integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== +"@babel/parser@^7.18.10", "@babel/parser@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" + integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== "@babel/register@^7.18.9": version "7.18.9" @@ -167,13 +167,13 @@ source-map-support "^0.5.16" "@babel/runtime@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.18.10", "@babel/template@^7.18.6": +"@babel/template@^7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== @@ -182,26 +182,26 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" - integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== +"@babel/traverse@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.0.tgz#eb9c561c7360005c592cc645abafe0c3c4548eed" + integrity sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" + "@babel/generator" "^7.19.0" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.11" - "@babel/types" "^7.18.10" + "@babel/parser" "^7.19.0" + "@babel/types" "^7.19.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" - integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" + integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== dependencies: "@babel/helper-string-parser" "^7.18.10" "@babel/helper-validator-identifier" "^7.18.6" @@ -214,14 +214,14 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint/eslintrc@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.1.tgz#de0807bfeffc37b964a7d0400e0c348ce5a2543d" + integrity sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" + espree "^9.4.0" globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -246,180 +246,181 @@ ethereumjs-util "^7.1.5" "@ethersproject/abi@^5.6.3": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.4.tgz#f6e01b6ed391a505932698ecc0d9e7a99ee60362" - integrity sha512-TTeZUlCeIHG6527/2goZA6gW5F8Emoc7MrZDC7hhP84aRGvW3TEdTnZR08Ls88YXM1m2SuK42Osw/jSi3uO8gg== + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== dependencies: - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" -"@ethersproject/abstract-provider@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz#02ddce150785caf0c77fe036a0ebfcee61878c59" - integrity sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ== +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.3" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/web" "^5.6.1" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz#491f07fc2cbd5da258f46ec539664713950b0b33" - integrity sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ== +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.1.tgz#ab57818d9aefee919c5721d28cd31fd95eff413d" - integrity sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q== +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.1.tgz#2c40d8a0310c9d1606c2c37ae3092634b41d87cb" - integrity sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw== +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== dependencies: - "@ethersproject/bytes" "^5.6.1" + "@ethersproject/bytes" "^5.7.0" -"@ethersproject/bignumber@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.2.tgz#72a0717d6163fab44c47bcc82e0c550ac0315d66" - integrity sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw== +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.1.tgz#24f916e411f82a8a60412344bf4a813b917eefe7" - integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== dependencies: - "@ethersproject/logger" "^5.6.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.1.tgz#e2e974cac160dd101cf79fdf879d7d18e8cb1370" - integrity sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg== +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== dependencies: - "@ethersproject/bignumber" "^5.6.2" + "@ethersproject/bignumber" "^5.7.0" -"@ethersproject/hash@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.1.tgz#224572ea4de257f05b4abf8ae58b03a67e99b0f4" - integrity sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA== +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" -"@ethersproject/keccak256@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.1.tgz#b867167c9b50ba1b1a92bccdd4f2d6bd168a91cc" - integrity sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA== +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== dependencies: - "@ethersproject/bytes" "^5.6.1" + "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@^5.6.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.6.0.tgz#d7db1bfcc22fd2e4ab574cba0bb6ad779a9a3e7a" - integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg== +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== -"@ethersproject/networks@^5.6.3": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.4.tgz#51296d8fec59e9627554f5a8a9c7791248c8dc07" - integrity sha512-KShHeHPahHI2UlWdtDMn2lJETcbtaJge4k7XSjDR9h79QTd6yQJmv6Cp2ZA4JdqWnhszAOLSuJEd9C0PRw7hSQ== +"@ethersproject/networks@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.0.tgz#df72a392f1a63a57f87210515695a31a245845ad" + integrity sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA== dependencies: - "@ethersproject/logger" "^5.6.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/properties@^5.6.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.6.0.tgz#38904651713bc6bdd5bdd1b0a4287ecda920fa04" - integrity sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg== +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== dependencies: - "@ethersproject/logger" "^5.6.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.1.tgz#df8311e6f9f24dcb03d59a2bac457a28a4fe2bd8" - integrity sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ== +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/signing-key@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.2.tgz#8a51b111e4d62e5a62aee1da1e088d12de0614a3" - integrity sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ== +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" bn.js "^5.2.1" elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/strings@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.1.tgz#dbc1b7f901db822b5cafd4ebf01ca93c373f8952" - integrity sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw== +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.2.tgz#793a774c01ced9fe7073985bb95a4b4e57a6370b" - integrity sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q== +"@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== dependencies: - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" -"@ethersproject/web@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.1.tgz#6e2bd3ebadd033e6fe57d072db2b69ad2c9bdf5d" - integrity sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA== +"@ethersproject/web@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.0.tgz#40850c05260edad8b54827923bbad23d96aac0bc" + integrity sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA== dependencies: - "@ethersproject/base64" "^5.6.1" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" "@humanwhocodes/config-array@^0.10.4": version "0.10.4" @@ -435,6 +436,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -597,31 +603,22 @@ rxjs "^7.5.6" "@polkadot/keyring@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.1.4.tgz#7c60002cb442d2a160ee215b21c1319e85d97eaf" - integrity sha512-dCMejp5heZwKSFeO+1vCHFoo1h1KgNvu4AaKQdNxpyr/3eCINrCFI74/qT9XGypblxd61caOpJcMl8B1R/UWFA== + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.1.7.tgz#d51be1dc5807c961889847d8f0e10e4bbdd19d3f" + integrity sha512-lArwaAS3hDs+HHupDIC4r2mFaAfmNQV2YzwL2wM5zhOqB2RugN03BFrgwNll0y9/Bg8rYDqM3Y5BvVMzgMZ6XA== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/util" "10.1.4" - "@polkadot/util-crypto" "10.1.4" + "@polkadot/util" "10.1.7" + "@polkadot/util-crypto" "10.1.7" -"@polkadot/networks@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.1.tgz#d3deeff5c4cfad8c1eec85732351d80d1b2d0934" - integrity sha512-upM8r0mrsCVA+vPVbJUjtnkAfdleBMHB+Fbxvy3xtbK1IFpzQTUhSOQb6lBnBAPBFGyxMtQ3TytnInckAdYZeg== +"@polkadot/networks@10.1.7", "@polkadot/networks@^10.1.4": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.7.tgz#33b38d70409e2daf0990ef18ff150c6718ffb700" + integrity sha512-ol864SZ/GwAF72GQOPRy+Y9r6NtgJJjMBlDLESvV5VK64eEB0MRSSyiOdd7y/4SumR9crrrNimx3ynACFgxZ8A== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/util" "10.1.1" - "@substrate/ss58-registry" "^1.24.0" - -"@polkadot/networks@10.1.4", "@polkadot/networks@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.4.tgz#d8b375aad8f858f611165d8288eb5eab7275ca24" - integrity sha512-5wMwqD+DeVMh29OZZBVkA4DQE9EBsUj5FjmUS2CloA8RzE6SV0qL34zhTwOdq95KJV1OoDbp9aGjCBqhEuozKw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "10.1.4" - "@substrate/ss58-registry" "^1.25.0" + "@polkadot/util" "10.1.7" + "@substrate/ss58-registry" "^1.28.0" "@polkadot/rpc-augment@9.2.2": version "9.2.2" @@ -758,64 +755,34 @@ "@polkadot/util-crypto" "^10.1.4" rxjs "^7.5.6" -"@polkadot/util-crypto@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.1.tgz#c6e16e626e55402fdb44c8bb20ce4a9d7c50b9db" - integrity sha512-R0V++xXbL2pvnCFIuXnKc/TlNhBkyxcno1u8rmjYNuH9S5GOmi2jY/8cNhbrwk6wafBsi+xMPHrEbUnduk82Ag== +"@polkadot/util-crypto@10.1.7", "@polkadot/util-crypto@^10.1.4": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.7.tgz#fe5ea006bf23ae19319f3ac9236905a984a65e2f" + integrity sha512-zGmSU7a0wdWfpDtfc+Q7fUuW+extu9o1Uh4JpkuwwZ/dxmyW5xlfqVsIScM1pdPyjJsyamX8KwsKiVsW4slasg== dependencies: "@babel/runtime" "^7.18.9" "@noble/hashes" "1.1.2" "@noble/secp256k1" "1.6.3" - "@polkadot/networks" "10.1.1" - "@polkadot/util" "10.1.1" + "@polkadot/networks" "10.1.7" + "@polkadot/util" "10.1.7" "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.1" - "@polkadot/x-randomvalues" "10.1.1" - "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@10.1.4", "@polkadot/util-crypto@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.4.tgz#1d65a9b3d979f1cb078636a413cdf664db760a8b" - integrity sha512-6rdUwCdbwmQ0PBWBNYh55RsXAcFjhco/TGLuM7GJ7YufrN9qqv1sr40HlneLbtpiZnfukZ3q/qOpj0h7Hrw2JQ== - dependencies: - "@babel/runtime" "^7.18.9" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.3" - "@polkadot/networks" "10.1.4" - "@polkadot/util" "10.1.4" - "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.4" - "@polkadot/x-randomvalues" "10.1.4" + "@polkadot/x-bigint" "10.1.7" + "@polkadot/x-randomvalues" "10.1.7" "@scure/base" "1.1.1" ed2curve "^0.3.0" tweetnacl "^1.0.3" -"@polkadot/util@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.1.tgz#5aa20eac03806e70dc21e618a7f8cd767dac0fd0" - integrity sha512-/g0sEqOOXfiNmQnWcFK3H1+wKBjbJEfGj6lTmbQ0xnL4TS5mFFQ7ZZEvxD60EkoXVMuCmSSh9E54goNLzh+Zyg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-bigint" "10.1.1" - "@polkadot/x-global" "10.1.1" - "@polkadot/x-textdecoder" "10.1.1" - "@polkadot/x-textencoder" "10.1.1" - "@types/bn.js" "^5.1.0" - bn.js "^5.2.1" - -"@polkadot/util@10.1.4", "@polkadot/util@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.4.tgz#29654dd52d5028fd9ca175e9cebad605fa79396c" - integrity sha512-MHz1UxYXuV+XxPl+GR++yOUE0OCiVd+eJBqLgpjpVJNRkudbAmfGAbB2TNR0+76M0fevIeHj4DGEd0gY6vqKLw== +"@polkadot/util@10.1.7", "@polkadot/util@^10.1.4": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.7.tgz#c54ca2a5b29cb834b40d8a876baefa3a0efb93af" + integrity sha512-s7gDLdNb4HUpoe3faXwoO6HwiUp8pi66voYKiUYRh1kEtW1o9vGBgyZPHPGC/FBgILzTJKii/9XxnSex60zBTA== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/x-bigint" "10.1.4" - "@polkadot/x-global" "10.1.4" - "@polkadot/x-textdecoder" "10.1.4" - "@polkadot/x-textencoder" "10.1.4" - "@types/bn.js" "^5.1.0" + "@polkadot/x-bigint" "10.1.7" + "@polkadot/x-global" "10.1.7" + "@polkadot/x-textdecoder" "10.1.7" + "@polkadot/x-textencoder" "10.1.7" + "@types/bn.js" "^5.1.1" bn.js "^5.2.1" "@polkadot/wasm-bridge@6.3.1": @@ -869,101 +836,62 @@ dependencies: "@babel/runtime" "^7.18.9" -"@polkadot/x-bigint@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.1.tgz#c084cfdfe48633da07423f4d9916563882947563" - integrity sha512-YNYN64N4icKyqiDIw0tcGyWwz3g/282Kk0ozfcA5TM0wGRe2BwmoB4gYrZ7pJDxvsHnRPR6Dw0r9Xxh8DNIzHQ== +"@polkadot/x-bigint@10.1.7", "@polkadot/x-bigint@^10.1.4": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.7.tgz#1338689476ffdbb9f9cb243df1954ae8186134b9" + integrity sha512-uaClHpI6cnDumIfejUKvNTkB43JleEb0V6OIufDKJ/e1aCLE3f/Ws9ggwL8ea05lQP5k5xqOzbPdizi/UvrgKQ== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.1" - -"@polkadot/x-bigint@10.1.4", "@polkadot/x-bigint@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.4.tgz#a084a9d2f80f25ffd529faafdf95cd6c3044ef74" - integrity sha512-qgLetTukFhkxNxNcUWMmnrfE9bp4TNbrqNoVBVH7wqSuEVpDPITBXsQ/78LbaaZGWD80Ew0wGxcZ/rqX+dLVUA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.4" + "@polkadot/x-global" "10.1.7" "@polkadot/x-fetch@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.1.4.tgz#72db88007c74f3aee47f72091a33d553f7ca241a" - integrity sha512-hVhLpOvx+ys6klkqWJnINi9FU/JcDnc+6cyU9fa+Dum3mqO1XnngOYDO9mpf5HODIwrFNFmohll9diRP+TW0yQ== + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.1.7.tgz#1b76051a495563403a20ef235a8558c6d91b11a6" + integrity sha512-NL+xrlqUoCLwCIAvQLwOA189gSUgeSGOFjCmZ9uMkBqf35KXeZoHWse6YaoseTSlnAal3dQOGbXnYWZ4Ck2OSA== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.4" + "@polkadot/x-global" "10.1.7" "@types/node-fetch" "^2.6.2" node-fetch "^3.2.10" -"@polkadot/x-global@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.1.tgz#d0d90ef71fd94f59605e8c73dcd1aa3e3dd4fc37" - integrity sha512-wB3rZTTNN14umLSfR2GLL0dJrlGM1YRUNw7XvbA+3B8jxGCIOmjSyAkdZBeiCxg2XIbJD3EkB0hBhga2mNuS6g== +"@polkadot/x-global@10.1.7", "@polkadot/x-global@^10.1.4": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.7.tgz#91a472ac2f83fd0858dcd0df528844a5b650790e" + integrity sha512-k2ZUZyBVgDnP/Ysxapa0mthn63j6gsN2V0kZejEQPyOfCHtQQkse3jFvAWdslpWoR8j2k8SN5O6reHc0F4f7mA== dependencies: "@babel/runtime" "^7.18.9" -"@polkadot/x-global@10.1.4", "@polkadot/x-global@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.4.tgz#657f7054fe07a7c027b4d18fcfa3438d2ffaef07" - integrity sha512-67f53H872wHvmjmL96DvhC3dG7gKRG1ghEbHXeFIGwkix+9zGEMV9krYW1+OAvGAuCQZqUIUGiJ7lad4Zjb7wQ== +"@polkadot/x-randomvalues@10.1.7": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.7.tgz#d537f1f7bf3fb03e6c08ae6e6ac36e069c1f9844" + integrity sha512-3er4UYOlozLGgFYWwcbmcFslmO8m82u4cAGR4AaEag0VdV7jLO/M5lTmivT/3rtLSww6sjkEfr522GM2Q5lmFg== dependencies: "@babel/runtime" "^7.18.9" + "@polkadot/x-global" "10.1.7" -"@polkadot/x-randomvalues@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.1.tgz#3b1f590e6641e322e3a28bb4f17f0a53005d9ada" - integrity sha512-opVFNEnzCir7cWsFfyDqNlrGazkpjnL+JpkxE/b9WmSco6y0IUzn/Q7rL3EaBzBEvxY0/J8KeSGGs3W+mf6tBQ== +"@polkadot/x-textdecoder@10.1.7": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.7.tgz#1dd4e6141b1669acdd321a4da1fc6fdc271b7908" + integrity sha512-iAFOHludmZFOyVL8sQFv4TDqbcqQU5gwwYv74duTA+WQBgbSITJrBahSCV/rXOjUqds9pzQO3qBFzziznNnkiQ== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.1" + "@polkadot/x-global" "10.1.7" -"@polkadot/x-randomvalues@10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.4.tgz#de337a046826223081697e6fc1991c547f685787" - integrity sha512-sfYz3GmyG739anj07Y+8PUX+95upO1zlsADAEfK1w1mMpTw97xEoMZf66CduAQOe43gEwQXc/JuKq794C/Hr7Q== +"@polkadot/x-textencoder@10.1.7": + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.7.tgz#b208601f33b936c7a059f126dbb6b26a87f45864" + integrity sha512-GzjaWZDbgzZ0IQT60xuZ7cZ0wnlNVYMqpfI9KvBc58X9dPI3TIMwzbXDVzZzpjY1SAqJGs4hJse9HMWZazfhew== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.4" - -"@polkadot/x-textdecoder@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.1.tgz#536d0093749fcc14a60d4ae29c35f699dea7e651" - integrity sha512-a52ah/sUS+aGZcCCL7BhrytAeV/7kiqu1zbuCoZtIzxP6x34a2vcic3bLPoyynLcX2ruzvLKFhJDGOJ4Bq5lcA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.1" - -"@polkadot/x-textdecoder@10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.4.tgz#d85028f6fcd00adc1e3581ab97668a61299270f9" - integrity sha512-B8XcAmJLnuppSr4RUNPevh5MH3tWZBwBR0wUsSdIyiGXuncgnkj9jmpbGLgV1tSn+BGxX3SNsRho3/4CNmndWQ== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.4" - -"@polkadot/x-textencoder@10.1.1": - version "10.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.1.tgz#c1a86b3d0fe0ca65d30c8ce5c6f75c4035e95847" - integrity sha512-prTzUXXW9OxFyf17EwGSBxe2GvVFG60cmKV8goC50nghhNMl1y0GdGpvKNQTFG6hIk5fIon9/pBpWsas4iAf+Q== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.1" - -"@polkadot/x-textencoder@10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.4.tgz#253e828bb571eb2a92a8377acd57d9bfcbe52fe8" - integrity sha512-vDpo0rVV4jBmr0L2tCZPZzxmzV2vZhpH1Dw9H7MpmZSPePz4ZF+o4RBJz/ocwQh3+1qV1SKQm7+fj4lPwUZdEw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.4" + "@polkadot/x-global" "10.1.7" "@polkadot/x-ws@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.1.4.tgz#b3fa515598bc6f8e85d92754d5f1c4be19ca44a1" - integrity sha512-hi7hBRRCLlHgqVW2p5TkoJuTxV7sVprl+aAnmcIpPU4J8Ai6PKQvXR+fLK01T8moBYmH5ztHrBWvY/XRzmQ8Vg== + version "10.1.7" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.1.7.tgz#b1fbfe3e16fa809f35f24ef47fde145b018d8cdc" + integrity sha512-aNkotxHx3qPVjiItD9lbNONs4GNzqeeZ98wHtCjd9JWl/g+xNkOVF3xQ8++1qSHPBEYSwKh9URjQH2+CD2XlvQ== dependencies: "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.4" + "@polkadot/x-global" "10.1.7" "@types/websocket" "^1.0.5" websocket "^1.0.34" @@ -972,7 +900,7 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== -"@sindresorhus/is@^4.6.0": +"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== @@ -999,10 +927,17 @@ pako "^2.0.4" websocket "^1.0.32" -"@substrate/ss58-registry@^1.24.0", "@substrate/ss58-registry@^1.25.0": - version "1.25.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.25.0.tgz#0fcd8c9c0e53963a88fbed41f2cbd8a1a5c74cde" - integrity sha512-LmCH4QJRdHaeLsLTPSgJaXguMoIW+Ig9fA9LRPpeya9HefVAJ7gZuUYinldv+QmX7evNm5CL0rspNUS8l1DvXg== +"@substrate/ss58-registry@^1.28.0": + version "1.29.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.29.0.tgz#0dea078271b5318c5eff7176e1df1f9b2c27e43f" + integrity sha512-KTqwZgTjtWPhCAUJJx9qswP/p9cRKUU9GOHYUDKNdISFDiFafWmpI54JHfYLkgjvkSKEUgRZnvLpe0LMF1fXvw== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" "@szmarczak/http-timer@^5.0.1": version "5.0.1" @@ -1031,14 +966,14 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" -"@types/cacheable-request@^6.0.2": +"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": version "6.0.2" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== @@ -1096,11 +1031,6 @@ version "4.0.1" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - -"@types/json-buffer@~3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" - integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== "@types/json-schema@^7.0.9": version "7.0.11" @@ -1128,9 +1058,9 @@ form-data "^3.0.0" "@types/node@*": - version "18.7.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.5.tgz#f1c1d4b7d8231c0278962347163656f9c36f3e83" - integrity sha512-NcKK6Ts+9LqdHJaW6HQmgr7dT/i3GOHG+pt6BiWv++5SnjtRd4NXeiuN2kA153SjhXPR/AhHIPHPbrsbpUVOww== + version "18.7.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.16.tgz#0eb3cce1e37c79619943d2fd903919fc30850601" + integrity sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg== "@types/node@^12.12.6": version "12.20.55" @@ -1171,13 +1101,13 @@ "@types/node" "*" "@typescript-eslint/eslint-plugin@^5.26.0": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.1.tgz#c0a480d05211660221eda963cc844732fe9b1714" - integrity sha512-S1iZIxrTvKkU3+m63YUOxYPKaP+yWDQrdhxTglVDVEVBf+aCSw85+BmJnyUaQQsk5TXFG/LpBu9fa+LrAQ91fQ== + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.36.2.tgz#6df092a20e0f9ec748b27f293a12cb39d0c1fe4d" + integrity sha512-OwwR8LRwSnI98tdc2z7mJYgY60gf7I9ZfGjN5EjCwwns9bdTuQfAXcsjSB2wSQ/TVNYSGKf4kzVXbNGaZvwiXw== dependencies: - "@typescript-eslint/scope-manager" "5.33.1" - "@typescript-eslint/type-utils" "5.33.1" - "@typescript-eslint/utils" "5.33.1" + "@typescript-eslint/scope-manager" "5.36.2" + "@typescript-eslint/type-utils" "5.36.2" + "@typescript-eslint/utils" "5.36.2" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -1186,68 +1116,69 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.26.0": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.1.tgz#e4b253105b4d2a4362cfaa4e184e2d226c440ff3" - integrity sha512-IgLLtW7FOzoDlmaMoXdxG8HOCByTBXrB1V2ZQYSEV1ggMmJfAkMWTwUjjzagS6OkfpySyhKFkBw7A9jYmcHpZA== + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.36.2.tgz#3ddf323d3ac85a25295a55fcb9c7a49ab4680ddd" + integrity sha512-qS/Kb0yzy8sR0idFspI9Z6+t7mqk/oRjnAYfewG+VN73opAUvmYL3oPIMmgOX6CnQS6gmVIXGshlb5RY/R22pA== dependencies: - "@typescript-eslint/scope-manager" "5.33.1" - "@typescript-eslint/types" "5.33.1" - "@typescript-eslint/typescript-estree" "5.33.1" + "@typescript-eslint/scope-manager" "5.36.2" + "@typescript-eslint/types" "5.36.2" + "@typescript-eslint/typescript-estree" "5.36.2" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.33.1": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.1.tgz#8d31553e1b874210018ca069b3d192c6d23bc493" - integrity sha512-8ibcZSqy4c5m69QpzJn8XQq9NnqAToC8OdH/W6IXPXv83vRyEDPYLdjAlUx8h/rbusq6MkW4YdQzURGOqsn3CA== +"@typescript-eslint/scope-manager@5.36.2": + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.36.2.tgz#a75eb588a3879ae659514780831370642505d1cd" + integrity sha512-cNNP51L8SkIFSfce8B1NSUBTJTu2Ts4nWeWbFrdaqjmn9yKrAaJUBHkyTZc0cL06OFHpb+JZq5AUHROS398Orw== dependencies: - "@typescript-eslint/types" "5.33.1" - "@typescript-eslint/visitor-keys" "5.33.1" + "@typescript-eslint/types" "5.36.2" + "@typescript-eslint/visitor-keys" "5.36.2" -"@typescript-eslint/type-utils@5.33.1": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.1.tgz#1a14e94650a0ae39f6e3b77478baff002cec4367" - integrity sha512-X3pGsJsD8OiqhNa5fim41YtlnyiWMF/eKsEZGsHID2HcDqeSC5yr/uLOeph8rNF2/utwuI0IQoAK3fpoxcLl2g== +"@typescript-eslint/type-utils@5.36.2": + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.36.2.tgz#752373f4babf05e993adf2cd543a763632826391" + integrity sha512-rPQtS5rfijUWLouhy6UmyNquKDPhQjKsaKH0WnY6hl/07lasj8gPaH2UD8xWkePn6SC+jW2i9c2DZVDnL+Dokw== dependencies: - "@typescript-eslint/utils" "5.33.1" + "@typescript-eslint/typescript-estree" "5.36.2" + "@typescript-eslint/utils" "5.36.2" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.33.1": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.1.tgz#3faef41793d527a519e19ab2747c12d6f3741ff7" - integrity sha512-7K6MoQPQh6WVEkMrMW5QOA5FO+BOwzHSNd0j3+BlBwd6vtzfZceJ8xJ7Um2XDi/O3umS8/qDX6jdy2i7CijkwQ== +"@typescript-eslint/types@5.36.2": + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.36.2.tgz#a5066e500ebcfcee36694186ccc57b955c05faf9" + integrity sha512-9OJSvvwuF1L5eS2EQgFUbECb99F0mwq501w0H0EkYULkhFa19Qq7WFbycdw1PexAc929asupbZcgjVIe6OK/XQ== -"@typescript-eslint/typescript-estree@5.33.1": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.1.tgz#a573bd360790afdcba80844e962d8b2031984f34" - integrity sha512-JOAzJ4pJ+tHzA2pgsWQi4804XisPHOtbvwUyqsuuq8+y5B5GMZs7lI1xDWs6V2d7gE/Ez5bTGojSK12+IIPtXA== +"@typescript-eslint/typescript-estree@5.36.2": + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.36.2.tgz#0c93418b36c53ba0bc34c61fe9405c4d1d8fe560" + integrity sha512-8fyH+RfbKc0mTspfuEjlfqA4YywcwQK2Amcf6TDOwaRLg7Vwdu4bZzyvBZp4bjt1RRjQ5MDnOZahxMrt2l5v9w== dependencies: - "@typescript-eslint/types" "5.33.1" - "@typescript-eslint/visitor-keys" "5.33.1" + "@typescript-eslint/types" "5.36.2" + "@typescript-eslint/visitor-keys" "5.36.2" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.33.1": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.1.tgz#171725f924fe1fe82bb776522bb85bc034e88575" - integrity sha512-uphZjkMaZ4fE8CR4dU7BquOV6u0doeQAr8n6cQenl/poMaIyJtBu8eys5uk6u5HiDH01Mj5lzbJ5SfeDz7oqMQ== +"@typescript-eslint/utils@5.36.2": + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.36.2.tgz#b01a76f0ab244404c7aefc340c5015d5ce6da74c" + integrity sha512-uNcopWonEITX96v9pefk9DC1bWMdkweeSsewJ6GeC7L6j2t0SJywisgkr9wUTtXk90fi2Eljj90HSHm3OGdGRg== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.33.1" - "@typescript-eslint/types" "5.33.1" - "@typescript-eslint/typescript-estree" "5.33.1" + "@typescript-eslint/scope-manager" "5.36.2" + "@typescript-eslint/types" "5.36.2" + "@typescript-eslint/typescript-estree" "5.36.2" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.33.1": - version "5.33.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.1.tgz#0155c7571c8cd08956580b880aea327d5c34a18b" - integrity sha512-nwIxOK8Z2MPWltLKMLOEZwmfBZReqUdbEoHQXeCpa+sRVARe5twpJGHCB4dk9903Yaf0nMAlGbQfaAH92F60eg== +"@typescript-eslint/visitor-keys@5.36.2": + version "5.36.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.36.2.tgz#2f8f78da0a3bad3320d2ac24965791ac39dace5a" + integrity sha512-BtRvSR6dEdrNt7Net2/XDjbYKU5Ml6GqJgVfXT0CxTCJlnIqK7rAGreuWKMT2t8cFUT2Msv5oxw0GMRD7T5J7A== dependencies: - "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/types" "5.36.2" eslint-visitor-keys "^3.3.0" "@ungap/promise-all-settled@1.1.2": @@ -1621,6 +1552,11 @@ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + cacheable-lookup@^6.0.4: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" @@ -1658,9 +1594,9 @@ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001370: - version "1.0.30001377" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001377.tgz#fa446cef27f25decb0c7420759c9ea17a2221a70" - integrity sha512-I5XeHI1x/mRSGl96LFOaSk528LA/yZG3m3iQgImGujjO8gotd/DL8QaI1R1h1dg5ATeI2jqPblMpKq4Tr5iKfQ== + version "1.0.30001393" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz#1aa161e24fe6af2e2ccda000fc2b94be0b0db356" + integrity sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA== caseless@~0.12.0: version "0.12.0" @@ -1834,14 +1770,6 @@ resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compress-brotli@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db" - integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ== - dependencies: - "@types/json-buffer" "~3.0.0" - json-buffer "~3.0.1" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2016,13 +1944,6 @@ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== -decompress-response@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== - dependencies: - mimic-response "^1.0.0" - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -2042,7 +1963,7 @@ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -defer-to-connect@^2.0.1: +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== @@ -2115,11 +2036,6 @@ version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== - -duplexer3@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" - integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== ecc-jsbn@~0.1.1: version "0.1.2" @@ -2142,9 +2058,9 @@ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.202: - version "1.4.221" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.221.tgz#1ff8425d257a8bfc8269d552a426993c5b525471" - integrity sha512-aWg2mYhpxZ6Q6Xvyk7B2ziBca4YqrCDlXzmcD7wuRs65pVEVkMT1u2ifdjpAQais2O2o0rW964ZWWWYRlAL/kw== + version "1.4.246" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.246.tgz#802132d1bbd3ff32ce82fcd6a6ed6ab59b4366dc" + integrity sha512-/wFCHUE+Hocqr/LlVGsuKLIw4P2lBWwFIDcNMDpJGzyIysQV4aycpoOitAs32FT94EHKnNqDR/CVZJFbXEufJA== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -2177,15 +2093,15 @@ once "^1.4.0" es-abstract@^1.19.0, es-abstract@^1.19.5, es-abstract@^1.20.0: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + version "1.20.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3" + integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" + get-intrinsic "^1.1.2" get-symbol-description "^1.0.0" has "^1.0.3" has-property-descriptors "^1.0.0" @@ -2197,9 +2113,9 @@ is-shared-array-buffer "^1.0.2" is-string "^1.0.7" is-weakref "^1.0.2" - object-inspect "^1.12.0" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" + object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" @@ -2299,13 +2215,14 @@ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.16.0: - version "8.22.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48" - integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA== + version "8.23.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.0.tgz#a184918d288820179c6041bb3ddcc99ce6eea040" + integrity sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA== dependencies: - "@eslint/eslintrc" "^1.3.0" + "@eslint/eslintrc" "^1.3.1" "@humanwhocodes/config-array" "^0.10.4" "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" + "@humanwhocodes/module-importer" "^1.0.1" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2315,7 +2232,7 @@ eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.3" + espree "^9.4.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -2341,12 +2258,11 @@ strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.2, espree@^9.3.3: - version "9.3.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" - integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" @@ -2518,11 +2434,11 @@ vary "~1.1.2" ext@^1.1.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" - integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== dependencies: - type "^2.5.0" + type "^2.7.2" extend@~3.0.2: version "3.0.2" @@ -2545,9 +2461,9 @@ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -2654,9 +2570,9 @@ integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== follow-redirects@^1.12.1: version "1.15.1" @@ -2781,7 +2697,7 @@ resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== @@ -2789,11 +2705,6 @@ function-bind "^1.1.1" has "^1.0.3" has-symbols "^1.0.3" - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== get-stream@^5.1.0: version "5.2.0" @@ -2911,25 +2822,22 @@ p-cancelable "^3.0.0" responselike "^2.0.0" -got@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== +got@^11.8.5: + version "11.8.5" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" + integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.2.10" @@ -2988,22 +2896,10 @@ dependencies: get-intrinsic "^1.1.1" -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.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" has-tostringtag@^1.0.0: version "1.0.0" @@ -3080,6 +2976,14 @@ jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + http2-wrapper@^2.1.10: version "2.1.11" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.1.11.tgz#d7c980c7ffb85be3859b6a96c800b2951ae257ef" @@ -3244,16 +3148,6 @@ version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" - integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== - -is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== is-plain-obj@^2.1.0: version "2.1.0" @@ -3274,11 +3168,6 @@ dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" - -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-shared-array-buffer@^1.0.2: version "1.0.2" @@ -3287,11 +3176,6 @@ dependencies: call-bind "^1.0.2" -is-stream@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== - is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -3348,14 +3232,6 @@ version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" @@ -3389,7 +3265,7 @@ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-buffer@3.0.1, json-buffer@~3.0.1: +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== @@ -3446,11 +3322,10 @@ readable-stream "^3.6.0" keyv@^4.0.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.3.tgz#6c1bcda6353a9e96fc1b4e1aeb803a6e35090ba9" - integrity sha512-AcysI17RvakTh8ir03+a3zJr5r0ovnAH/XTXei/4HIv3bL2K/jzvgivLK9UuI/JbU1aJjM3NSAnVvVVd3n+4DQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.0.tgz#dbce9ade79610b6e641a9a65f2f6499ba06b9bc6" + integrity sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA== dependencies: - compress-brotli "^1.3.8" json-buffer "3.0.1" kind-of@^6.0.2: @@ -3505,11 +3380,6 @@ integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== dependencies: get-func-name "^2.0.0" - -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lowercase-keys@^2.0.0: version "2.0.0" @@ -3885,7 +3755,7 @@ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -3895,10 +3765,10 @@ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" - integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" @@ -3943,21 +3813,16 @@ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - p-limit@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3985,13 +3850,6 @@ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" - -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA== - dependencies: - p-finally "^1.0.0" p-try@^2.0.0: version "2.2.0" @@ -4113,11 +3971,6 @@ version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg== process@^0.11.10: version "0.11.10" @@ -4299,7 +4152,7 @@ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resolve-alpn@^1.2.0: +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -4641,15 +4494,15 @@ has-flag "^4.0.0" swarm-js@^0.1.40: - version "0.1.40" - resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" - integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + version "0.1.42" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" + integrity sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ== dependencies: bluebird "^3.5.0" buffer "^5.0.5" eth-lib "^0.1.26" fs-extra "^4.0.2" - got "^7.1.0" + got "^11.8.5" mime-types "^2.1.16" mkdirp-promise "^5.0.1" mock-fs "^4.1.0" @@ -4675,7 +4528,7 @@ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -timed-out@^4.0.0, timed-out@^4.0.1: +timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== @@ -4800,7 +4653,7 @@ resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== -type@^2.5.0: +type@^2.7.2: version "2.7.2" resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== @@ -4813,14 +4666,14 @@ is-typedarray "^1.0.0" typescript@^4.7.2: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + version "4.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88" + integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig== uglify-js@^3.1.4: - version "3.16.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.3.tgz#94c7a63337ee31227a18d03b8a3041c210fd1f1d" - integrity sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw== + version "3.17.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.0.tgz#55bd6e9d19ce5eef0d5ad17cd1f587d85b180a85" + integrity sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg== ultron@~1.1.0: version "1.1.1" @@ -4848,9 +4701,9 @@ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + version "1.0.7" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.7.tgz#16279639cff1d0f800b14792de43d97df2d11b7d" + integrity sha512-iN/XYesmZ2RmmWAiI4Z5rq0YqSiv0brj9Ce9CfhNE4xIW2h+MFxcgkxIzZ+ShkFPUkjU3gQ+3oypadD3RAMtrg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -4862,23 +4715,11 @@ dependencies: punycode "^2.1.0" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA== - dependencies: - prepend-http "^1.0.1" - 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 sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== -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" - integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A== - utf-8-validate@^5.0.2: version "5.0.9" resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3" @@ -4927,11 +4768,6 @@ version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== varint@^5.0.0: version "5.0.2" -- gitstuff