diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2a488605e..4a89badc2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,30 @@ # Default code owners for entire repository -* @SimiHunjan @ochikov @petreze +* @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers + +######################### +##### Core Files ###### +######################### + +# NOTE: Must be placed last to ensure enforcement over all other rules + +# Protection Rules for Github Configuration Files and Actions Workflows +/.github/ @hashgraph/devops-ci @hashgraph/release-engineering-managers +/.github/workflows/ @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers + +# Codacy Tool Configurations +/config/ @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers +.remarkrc @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers + +# Self-protection for root CODEOWNERS files (this file should not exist and should definitely require approval) +/CODEOWNERS @hashgraph/release-engineering-managers + +# Protect the repository root files +/README.md @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers +**/LICENSE @hashgraph/release-engineering-managers + +# CodeCov configuration +**/codecov.yml @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers + +# Git Ignore definitions +**/.gitignore @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers +**/.gitignore.* @hashgraph/devops-ci @hashgraph/release-engineering-managers @hashgraph/hedera-sdk @hashgraph/hedera-sdk-js-maintainers diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9dbcaf7fd..daef1fa43 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,9 @@ defaults: permissions: contents: read +env: + CG_EXEC: export R_UID=$(id -u); CGROUP_LOGLEVEL=DEBUG cgexec -g cpu,memory:user.slice/user-${R_UID}.slice/user@${R_UID}.service/e2e-${{ github.run_id }} --sticky ionice -c 2 -n 2 nice -n 19 + jobs: build: name: Build using Node ${{ matrix.node }} @@ -28,20 +31,25 @@ jobs: node: [ "16", "18" ] steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -60,15 +68,61 @@ jobs: node: [ "16" ] steps: + - name: Setup Control Groups + run: | + echo "::group::Get System Configuration" + USR_ID="$(id -un)" + GRP_ID="$(id -gn)" + E2E_MEM_LIMIT="30064771072" + AGENT_MEM_LIMIT="2147483648" + USER_SLICE="user.slice/user-$(id -u).slice" + USER_SERVICE="${USER_SLICE}/user@$(id -u).service" + E2E_GROUP_NAME="${USER_SERVICE}/e2e-${{ github.run_id }}" + AGENT_GROUP_NAME="${USER_SERVICE}/agent-${{ github.run_id }}" + echo "::endgroup::" + + echo "::group::Install Control Group Tools" + if ! command -v cgcreate >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y cgroup-tools + fi + echo "::endgroup::" + + echo "::group::Create Control Groups" + sudo cgcreate -g cpu,memory:${USER_SLICE} -a ${USR_ID}:${GRP_ID} -t ${USR_ID}:${GRP_ID} + sudo cgcreate -g cpu,memory:${USER_SERVICE} -a ${USR_ID}:${GRP_ID} -t ${USR_ID}:${GRP_ID} + sudo cgcreate -g cpu,memory:${E2E_GROUP_NAME} -a ${USR_ID}:${GRP_ID} -t ${USR_ID}:${GRP_ID} + sudo cgcreate -g cpu,memory:${AGENT_GROUP_NAME} -a ${USR_ID}:${GRP_ID} -t ${USR_ID}:${GRP_ID} + echo "::endgroup::" + + echo "::group::Set Control Group Limits" + cgset -r cpu.weight=768 ${E2E_GROUP_NAME} + cgset -r cpu.weight=500 ${AGENT_GROUP_NAME} + cgset -r memory.max=${E2E_MEM_LIMIT} ${E2E_GROUP_NAME} + cgset -r memory.max=${AGENT_MEM_LIMIT} ${AGENT_GROUP_NAME} + cgset -r memory.swap.max=${E2E_MEM_LIMIT} ${E2E_GROUP_NAME} + cgset -r memory.swap.max=${AGENT_MEM_LIMIT} ${AGENT_GROUP_NAME} + echo "::endgroup::" + + echo "::group::Move Runner Processes to Control Groups" + sudo cgclassify --sticky -g cpu,memory:${AGENT_GROUP_NAME} $(pgrep 'Runner.Listener' | tr '\n' ' ') + sudo cgclassify -g cpu,memory:${AGENT_GROUP_NAME} $(pgrep 'Runner.Worker' | tr '\n' ' ') + echo "::endgroup::" + + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: recursive - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: "Create env file" run: | @@ -79,9 +133,9 @@ jobs: cat .env - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -102,38 +156,38 @@ jobs: id: start-local-node if: ${{ steps.build-sdk.conclusion == 'success' && !cancelled() && always() }} run: | - npx @hashgraph/hedera-local start -d --network local --balance=100000 + ${{ env.CG_EXEC }} npx @hashgraph/hedera-local start -d -—network local --balance=100000 # Wait for the network to fully start - sleep 30 + sleep 30 - name: Run Hedera SDK Integration Tests Codecov if: ${{ steps.build-sdk.conclusion == 'success' && steps.start-local-node.conclusion == 'success' && !cancelled() && always() }} - run: task test:integration:codecov + run: ${{ env.CG_EXEC }} task test:integration:codecov - name: Stop the local node id: stop-local-node if: ${{ steps.start-local-node.conclusion == 'success' && !cancelled() && always() }} - run: npx @hashgraph/hedera-local stop + run: ${{ env.CG_EXEC }} npx @hashgraph/hedera-local stop - name: Build @hashgraph/cryptography working-directory: packages/cryptography if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }} - run: task build + run: ${{ env.CG_EXEC }} task build - name: Unit Test @hashgraph/cryptography working-directory: packages/cryptography if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }} - run: task test:unit + run: ${{ env.CG_EXEC }} task test:unit - name: Codecov @hashgraph/cryptography working-directory: packages/cryptography if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }} - run: task test:unit:codecov + run: ${{ env.CG_EXEC }} task test:unit:codecov - name: Unit Test @hashgraph/sdk if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && steps.playwright-deps.conclusion == 'success' && !cancelled() && always() }} - run: task test:unit + run: ${{ env.CG_EXEC }} task test:unit - name: Codecov @hashgraph/sdk if: ${{ steps.build-sdk.conclusion == 'success' && steps.stop-local-node.conclusion == 'success' && !cancelled() && always() }} - run: task test:unit:codecov + run: ${{ env.CG_EXEC }} task test:unit:codecov diff --git a/.github/workflows/common_js.yml b/.github/workflows/common_js.yml index 4b879b62d..3dccceed4 100644 --- a/.github/workflows/common_js.yml +++ b/.github/workflows/common_js.yml @@ -27,20 +27,25 @@ jobs: node: [ "16", "18" ] steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: true - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 1060a35b1..e6b0a8489 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -11,21 +11,27 @@ defaults: permissions: pages: write contents: read + id-token: write jobs: build-and-deploy-docs: name: Documentation runs-on: [self-hosted, Linux, medium, ephemeral] steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: recursive - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -33,9 +39,9 @@ jobs: node-version: 18 - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Build @hashgraph/sdk run: task build @@ -50,6 +56,6 @@ jobs: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@decdde0ac072f6dcbe43649d82d9c635fff5b4e4 # v4.0.4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish_release.yaml b/.github/workflows/publish_release.yaml index 335c5e0d1..0ceb34176 100644 --- a/.github/workflows/publish_release.yaml +++ b/.github/workflows/publish_release.yaml @@ -26,14 +26,36 @@ jobs: validate-release: name: Validate Release runs-on: [self-hosted, Linux, medium, ephemeral] + outputs: + # Project tag tag: ${{ steps.tag.outputs.name }} - version: ${{ steps.tag.outputs.version }} - prerelease: ${{ steps.tag.outputs.prerelease }} - type: ${{ steps.tag.outputs.type }} + + # main package + sdk-version: ${{ steps.tag.outputs.version }} + sdk-prerelease: ${{ steps.tag.outputs.prerelease }} + sdk-type: ${{ steps.tag.outputs.type }} + + # proto sub-package + proto-version: ${{ steps.npm-package.outputs.proto-version }} + proto-prerelease: ${{ steps.proto-package.outputs.prerelease }} + proto-type: ${{ steps.proto-package.outputs.type }} + proto-publish-required: ${{ steps.proto-required.outputs.publish-required }} + + # crypto sub-package + crypto-version: ${{ steps.npm-package.outputs.crypto-version }} + crypto-prerelease: ${{ steps.crypto-package.output.prerelease }} + crypto-type: ${{ steps.crypto-package.output.type }} + crypto-publish-required: ${{ steps.crypto-required.outputs.publish-required }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: ref: ${{ github.event.inputs.tag || '' }} fetch-depth: 0 @@ -55,28 +77,55 @@ jobs: with: version: 1.7 - - name: Extract NPM Package Information + - name: Extract NPM Package Versions id: npm-package - run: echo "version=$(jq -r '.version' package.json)" >>"${GITHUB_OUTPUT}" + run: | + SDK_PACKAGE_VERSION="$(jq -r '.version' package.json)" + PROTO_PACKAGE_VERSION="$(jq -r '.version' './packages/proto/package.json')" + CRYPTO_PACKAGE_VERSION="$(jq -r '.version' './packages/cryptography/package.json')" + + echo "sdk-version=${SDK_PACKAGE_VERSION}" >>"${GITHUB_OUTPUT}" + echo "proto-version=${PROTO_PACKAGE_VERSION}" >>"${GITHUB_OUTPUT}" + echo "crypto-version=${CRYPTO_PACKAGE_VERSION}" >>"${GITHUB_OUTPUT}" + + - name: Proto Subpackage Publish Required + id: proto-required + run: | + PUBLISH_REQUIRED="false" + if ! curl -sSLf "https://registry.npmjs.org/@hashgraph/proto/${{ steps.npm-package.outputs.proto-version }}" >/dev/null 2>&1; then + PUBLISH_REQUIRED="true" + fi + + echo "publish-required=${PUBLISH_REQUIRED}" >>"${GITHUB_OUTPUT}" + + - name: Crypto Subpackage Publish Required + id: crypto-required + run: | + PUBLISH_REQUIRED="false" + if ! curl -sSLf "https://registry.npmjs.org/@hashgraph/cryptography/${{ steps.npm-package.outputs.crypto-version }}" >/dev/null 2>&1; then + PUBLISH_REQUIRED="true" + fi + + echo "publish-required=${PUBLISH_REQUIRED}" >>"${GITHUB_OUTPUT}" - - name: Extract Tag Information + - name: Extract SDK Tag Information id: tag run: | REF_NAME="$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" IS_VALID_SEMVER="$(semver validate "${REF_NAME}")" - + if [[ "${IS_VALID_SEMVER}" != "valid" ]]; then echo "::error title=Invalid Tag::The tag '${REF_NAME}' is not a valid SemVer tag." exit 1 fi - + RELEASE_VERSION="$(semver get release "${REF_NAME}")" PREREL_VERSION="$(semver get prerel "${REF_NAME}")" PREREL_VERSION_LC="$(printf "%s" "${PREREL_VERSION}" | tr '[:upper:]' '[:lower:]')" IS_PRERELEASE="false" [[ -n "${PREREL_VERSION}" ]] && IS_PRERELEASE="true" - + PREREL_TYPE="unknown" if [[ "${IS_PRERELEASE}" == "true" ]]; then if [[ "${PREREL_VERSION_LC}" =~ "beta" ]]; then @@ -87,25 +136,85 @@ jobs: else PREREL_TYPE="production" fi - + FINAL_VERSION="${RELEASE_VERSION}" [[ -n "${PREREL_VERSION}" ]] && FINAL_VERSION="${RELEASE_VERSION}-${PREREL_VERSION}" - + TAG_NAME="v${FINAL_VERSION}" - + echo "name=${TAG_NAME}" >>"${GITHUB_OUTPUT}" echo "version=${FINAL_VERSION}" >>"${GITHUB_OUTPUT}" echo "prerelease=${IS_PRERELEASE}" >>"${GITHUB_OUTPUT}" echo "type=${PREREL_TYPE}" >>"${GITHUB_OUTPUT}" + - name: Extract Proto Subpackage Information + id: proto-package + run: | + IS_VALID_SEMVER="$(semver validate "${{ steps.npm-package.outputs.proto-version }}")" + + if [[ "${IS_VALID_SEMVER}" != "valid" ]]; then + echo "::error title=Invalid Tag::The tag '${{ steps.npm-package.outputs.proto-version }}' is not a valid SemVer tag." + exit 1 + fi + + PREREL_VERSION="$(semver get prerel '${{ steps.npm-package.outputs.proto-version }}')" + PREREL_VERSION_LC="$(printf "%s" "${PREREL_VERSION}" | tr '[:upper:]' '[:lower:]')" + + IS_PRERELEASE="false" + [[ -n "${PREREL_VERSION}" ]] && IS_PRERELEASE="true" + + PREREL_TYPE="unknown" + if [[ "${IS_PRERELEASE}" == "true" ]]; then + if [[ "${PREREL_VERSION_LC}" =~ "beta" ]]; then + PREREL_TYPE="beta" + else + PREREL_TYPE="unknown" + fi + else + PREREL_TYPE="production" + fi + + echo "prerelease=${IS_PRERELEASE}" >>"${GITHUB_OUTPUT}" + echo "type=${PREREL_TYPE}" >>"${GITHUB_OUTPUT}" + + - name: Extract Crypto Subpackage Information + id: crypto-package + run: | + IS_VALID_SEMVER="$(semver validate '${{ steps.npm-package.outputs.crypto-version }}')" + + if [[ "${IS_VALID_SEMVER}" != "valid" ]]; then + echo "::error title=Invalid Tag::The tag '${{ steps.npm-package.outputs.crypto-version }}' is not a valid SemVer tag." + exit 1 + fi + + PREREL_VERSION="$(semver get prerel '${{ steps.npm-package.outputs.crypto-version }}')" + PREREL_VERSION_LC="$(printf "%s" "${PREREL_VERSION}" | tr '[:upper:]' '[:lower:]')" + + IS_PRERELEASE="false" + [[ -n "${PREREL_VERSION}" ]] && IS_PRERELEASE="true" + + PREREL_TYPE="unknown" + if [[ "${IS_PRERELEASE}" == "true" ]]; then + if [[ "${PREREL_VERSION_LC}" =~ "beta" ]]; then + PREREL_TYPE="beta" + else + PREREL_TYPE="unknown" + fi + else + PREREL_TYPE="production" + fi + + echo "prerelease=${IS_PRERELEASE}" >>"${GITHUB_OUTPUT}" + echo "type=${PREREL_TYPE}" >>"${GITHUB_OUTPUT}" + - name: Validate Tag and Package Versions run: | - COMPARISON_RESULT="$(semver compare "${{ steps.npm-package.outputs.version }}" "${{ steps.tag.outputs.version }}")" + COMPARISON_RESULT="$(semver compare "${{ steps.npm-package.outputs.sdk-version }}" "${{ steps.tag.outputs.version }}")" if [[ "${COMPARISON_RESULT}" -ne 0 ]]; then - echo "::error title=Version Mismatch::The version in package.json (${{ steps.npm-package.outputs.version }}) does not match the version in the tag (${{ steps.tag.outputs.version }})." + echo "::error title=Version Mismatch::The version in package.json (${{ steps.npm-package.outputs.sdk-version }}) does not match the version in the tag (${{ steps.tag.outputs.version }})." exit 1 fi - + if [[ "${{ steps.tag.outputs.type }}" != "production" && "${{ steps.tag.outputs.type }}" != "beta" ]]; then echo "::error title=Unsupported PreRelease::The tag '${{ steps.tag.outputs.name }}' is an unsupported prerelease tag. Only 'beta' prereleases are supported." exit 2 @@ -120,20 +229,25 @@ jobs: name: Safety Checks runs-on: [self-hosted, Linux, medium, ephemeral] steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: ref: ${{ github.event.inputs.tag || '' }} - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -141,7 +255,8 @@ jobs: node-version: 18 - name: Compile Code - run: task build + run: | + task -v build publish-release: name: Publish Release @@ -150,20 +265,25 @@ jobs: - validate-release - run-safety-checks steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: ref: ${{ github.event.inputs.tag || '' }} - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -172,32 +292,75 @@ jobs: cache: pnpm - name: Install NPM Dependencies - run: task install + run: task -v install - name: Install Playwright Dependencies run: sudo npx playwright install-deps - - name: Calculate Publish Arguments - id: publish + - name: Calculate Proto Subpackage Publish Arguments + id: proto-publish + working-directory: packages/proto + if: ${{ needs.validate-release.outputs.proto-publish-required == 'true' && !cancelled() && !failure() }} + run: | + PUBLISH_ARGS="--access public --no-git-checks" + [[ "${{ github.event.inputs.dry-run-enabled }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --dry-run" + [[ "${{ needs.validate-release.outputs.proto-prerelease }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --tag ${{ needs.validate-release.outputs.proto-type }}" + + echo "args=${PUBLISH_ARGS}" >>"${GITHUB_OUTPUT}" + + # Add the registry authentication stanza with variable substitution to the .npmrc configuration file. + echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' >>".npmrc" + + - name: Calculate Crypto Subpackage Publish Arguments + id: crypto-publish + working-directory: packages/cryptography + if: ${{ needs.validate-release.outputs.crypto-publish-required == 'true' && !cancelled() && !failure() }} run: | PUBLISH_ARGS="--access public --no-git-checks" [[ "${{ github.event.inputs.dry-run-enabled }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --dry-run" - [[ "${{ needs.validate-release.outputs.prerelease }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --tag ${{ needs.validate-release.outputs.type }}" + [[ "${{ needs.validate-release.outputs.crypto-prerelease }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --tag ${{ needs.validate-release.outputs.crypto-type }}" echo "args=${PUBLISH_ARGS}" >>"${GITHUB_OUTPUT}" + # Add the registry authentication stanza with variable substitution to the .npmrc configuration file. echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' >>".npmrc" - - name: Publish Release + - name: Calculate SDK Publish Arguments + id: sdk-publish + run: | + PUBLISH_ARGS="--access public --no-git-checks" + [[ "${{ github.event.inputs.dry-run-enabled }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --dry-run" + [[ "${{ needs.validate-release.outputs.sdk-prerelease }}" == "true" ]] && PUBLISH_ARGS="${PUBLISH_ARGS} --tag ${{ needs.validate-release.outputs.sdk-type }}" + + echo "args=${PUBLISH_ARGS}" >>"${GITHUB_OUTPUT}" + + # Add the registry authentication stanza with variable substitution to the .npmrc configuration file. + echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' >>".npmrc" + + - name: Publish Proto Release + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + working-directory: packages/proto + if: ${{ needs.validate-release.outputs.proto-publish-required == 'true' && !cancelled() && !failure() }} + run: task -v publish -- ${{ steps.proto-publish.outputs.args }} + + - name: Publish Cryptography Release + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + working-directory: packages/cryptography + if: ${{ needs.validate-release.outputs.crypto-publish-required == 'true' && !cancelled() && !failure() }} + run: task -v publish -- ${{ steps.crypto-publish.outputs.args }} + + - name: Publish SDK Release env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: task publish -- ${{ steps.publish.outputs.args }} + run: task -v publish -- ${{ steps.sdk-publish.outputs.args }} - name: Generate Github Release uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 - if: ${{ github.event.inputs.dry-run-enabled != 'true' }} + if: ${{ github.event.inputs.dry-run-enabled != 'true' && !cancelled() && !failure() }} with: - tag: ${{ steps.validate-release.outputs.tag }} + tag: ${{ needs.validate-release.outputs.tag }} prerelease: ${{ needs.validate-release.outputs.prerelease == 'true' }} draft: false generateReleaseNotes: true diff --git a/.github/workflows/react_native.yml b/.github/workflows/react_native.yml index 21c4e6ab3..38c358e64 100644 --- a/.github/workflows/react_native.yml +++ b/.github/workflows/react_native.yml @@ -13,6 +13,10 @@ on: - develop - release/* +defaults: + run: + shell: bash + permissions: contents: read @@ -21,11 +25,16 @@ jobs: name: Android runs-on: macos-latest steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Java - uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0 + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: distribution: "zulu" java-version: "8" @@ -33,7 +42,7 @@ jobs: - name: Install Task uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 with: - version: 3.7.0 + version: 3.35.1 - name: Setup Node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -41,9 +50,9 @@ jobs: node-version: "16" - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Install Yarn run: npm install -g yarn @@ -77,11 +86,16 @@ jobs: name: iOS runs-on: macos-latest steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Java - uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0 + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: distribution: "zulu" java-version: "8" @@ -97,9 +111,9 @@ jobs: node-version: "16" - name: Install PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 with: - version: 8.10.0 + version: 8.15.4 - name: Install Yarn run: npm install -g yarn diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml index 86ea2ba45..f68b7d530 100644 --- a/.github/workflows/renovate.yml +++ b/.github/workflows/renovate.yml @@ -4,6 +4,7 @@ on: # The "*" (#42, asterisk) character has special semantics in YAML, so this # string has to be quoted. - cron: '0 0 * * 0' + workflow_dispatch: defaults: run: @@ -14,15 +15,20 @@ permissions: jobs: renovate: - runs-on: ubuntu-latest + runs-on: [ self-hosted, Linux, medium, ephemeral ] steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + egress-policy: audit + - name: Checkout Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: recursive - name: Self-hosted Renovate - uses: renovatebot/github-action@2d90417499f45ff78a09586f7b9874b19817dba3 # v40.1.0 + uses: renovatebot/github-action@21d88b0bf0183abcee15f990011cca090dfc47dd # v40.1.12 with: configurationFile: .github/renovate.json token: ${{ secrets.RENOVATE_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 0594a62fc..adb81b8cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,89 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v2.49.2 + +## What's Changed + +* fix: update taskfile status check for submodules task by @isavov in https://github.com/hashgraph/hedera-sdk-js/pull/2435 +* chore: fix token permissions for deploy to github pages by @isavov in https://github.com/hashgraph/hedera-sdk-js/pull/2418 +* fix: reconnect to working node by @0xivanov in https://github.com/hashgraph/hedera-sdk-js/pull/2417 +* release: proto v2.15.0-beta.3 by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2415 +* update: add node id to the precheck error by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2414 +* feat: Implement TokenRejectTransaction by @ivaylonikolov7 in https://github.com/hashgraph/hedera-sdk-js/pull/2411 +* update: handle PLATFORM_NOT_ACTIVE error gracefully by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2401 +* feat: pull protobuf changes from latest tag by @isavov in https://github.com/hashgraph/hedera-sdk-js/pull/2435 +* chore: fix token permissions for deploy to github pages by @isavov in https://github.com/hashgraph/hedera-sdk-js/pull/2389 +* update: release all skipped tests by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2395 +* test: add maxAutomaticTokenAssociations tests by @ivaylonikolov7 in https://github.com/hashgraph/hedera-sdk-js/pull/2390 + +## v2.48.1 + +## What's Changed + +* release: proto v2.15.0-beta.2 by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2367 +* ci: update publishing workflow to use appropriate pre release and stable versions by @rbarkerSL in https://github.com/hashgraph/hedera-sdk-js/pull/2364 +* feature: custom derivation paths in Mnemonic ECDSA private key derivation by @bguiz in https://github.com/hashgraph/hedera-sdk-js/pull/2341 + +## v2.48.0 + +## What's Changed + +* release: proto v2.15.0-beta.2 by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2367 +* ci: update publishing workflow to use appropriate pre release and stable versions by @rbarkerSL in https://github.com/hashgraph/hedera-sdk-js/pull/2364 +* feature: custom derivation paths in Mnemonic ECDSA private key derivation by @bguiz in https://github.com/hashgraph/hedera-sdk-js/pull/2341 + +## v2.47.0 + +## What's Changed + +* revert: mirror node queries changes by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2328 +* feature: change or remove existing keys from a token [HIP-540] by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2299 +* release: proto v2.15.0-beta.1 by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2351 + +## v2.46.0 + +## What's Changed + +* fix: naming convention by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2303 +* fix: enhanced Logger to accept a log file location by @jeromy-cannon in https://github.com/hashgraph/hedera-sdk-js/pull/2298 +* fix: typo in SDK query file by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2293 +* feature: solution to query the mirror node for the account balance, account info, and contract info data by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2289 + +## v2.45.0 + +## What's Changed + +* fix: solve backward compatibility issues between the SDK and hedera-services v0.49 (modularized code) by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2240 +* feature: update metadata field of fungible and non-fungible tokens, and dynamic NFTs (HIP-646, HIP-765 and HIP-657) by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2210 + +## v2.45.0-beta.1 + +## What's Changed + +* fix: solve backward compatibility issues between the SDK and hedera-services v0.49 (modularized code) by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2240 +* feature: update metadata field of fungible and non-fungible tokens, and dynamic NFTs (HIP-646, HIP-765 and HIP-657) by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2210 + +## v2.44.0 + +## What's Changed + +* fix: set correct autoRenrewAccountId by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2217 +* update: add a new getter to the TransferTransaction class by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2214 +* fix: integer overflow isuue for defaultMaxQueryPayment field by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2213 +* chore(ci): update the publish workflow to release the cryptography and proto artifacts if needed by @nathanklick in https://github.com/hashgraph/hedera-sdk-js/pull/2198 + +## v2.43.0 + +## What's Changed + +* fix: do not instantiate logger inside a method by @leninmehedy in https://github.com/hashgraph/hedera-sdk-js/pull/2195 +* release: proto beta version 2.14.0-beta.5 by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2196 +* fix: hbar presentation after serialization/deserialization by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2189 +* fix: inability to create and execute freeze transaction by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2171 +* update: update steps for release process in RELEASE.md file by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2201 +* update: add section in README.md file on how to start the tests by @svetoslav-nikol0v in https://github.com/hashgraph/hedera-sdk-js/pull/2201 + ## v2.43.0-beta.1 ## What's Changed diff --git a/RELEASE.md b/RELEASE.md index eb74090df..0689521a0 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,17 +1,18 @@ ## Release process of JS SDK - **Create** a new release branch following the naming convention for: - - stable release - `release/v*.*.*` - - beta release - `release/v*.*.*-beta.*` + - stable release - `release-v*.*.*` + - beta release - `release-v*.*.*-beta.*` - **Run** the [local node](https://github.com/hashgraph/hedera-local-node) - **Run** `task test:integration:node` - **Stop** the [local node](https://github.com/hashgraph/hedera-local-node) - **Run** `task test:release` (Note: the local node should not be running) - **Update** the SDK version in `package.json` file - **Update** the CHANGELOG file -- **Create** a new tag from the branch that you are releasing (release/vX.Y.Z) with the same version of the branch as running following command for: +- **Merge** release branch into main +- **Create** a new tag as running following command for: - stable release - `git tag -a "v*.*.*" -m "[message]"` - - bet release - `git tag -a "v*.*.*-beta.*" -m "[message]"` + - beta release - `git tag -a "v*.*.*-beta.*" -m "[message]"` - **Run** the following command for: - stable release - `git push origin v*.*.*` - beta release - `git push origin v*.*.*-beta.*` diff --git a/Taskfile.yml b/Taskfile.yml index f1660890b..877a505bc 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -42,38 +42,28 @@ tasks: --validation.invalidLink --entryPoints src/index.js src/browser.js src/native.js - "install:deps": - run: once + "yalc:install": cmds: - - pnpm i --dev > /dev/null + - npm install -g yalc > /dev/null - "install:cryptography:local": + "yalc:add": cmds: - - task: "install:deps" - - task: "cryptography:build" - - npx yalc add @hashgraph/cryptography > /dev/null - status: - - ./check_yalc_dep.sh @hashgraph/cryptography - - "install:proto:local": - cmds: - - task: "install:deps" - - task: "proto:build" - npx yalc add @hashgraph/proto > /dev/null - status: - - ./check_yalc_dep.sh @hashgraph/proto + - npx yalc add @hashgraph/cryptography > /dev/null - "build:proto": + "yalc:remove": cmds: - - task: "proto:build" + - npx yalc remove @hashgraph/proto > /dev/null + - npx yalc remove @hashgraph/cryptography > /dev/null install: deps: - - "build:proto" - - "install:cryptography:local" - - "install:proto:local" + - "yalc:install" + - "proto:build" + - "cryptography:build" cmds: - - pnpm i > /dev/null + - task: "yalc:add" + - pnpm i --no-frozen-lockfile > /dev/null build: cmds: @@ -142,7 +132,7 @@ tasks: "test:unit:node": cmds: - - npx mocha --inline-diffs -r @babel/register -r chai/register-expect.js "test/unit/*.js" {{.CLI_ARGS}} + - npx mocha --inline-diffs --exit -r @babel/register -r chai/register-expect.js "test/unit/*.js" {{.CLI_ARGS}} "test:unit:codecov": cmds: @@ -187,8 +177,14 @@ tasks: - task: build publish: + deps: + - "test:release" + cmds: + - task: "yalc:remove" + - task: "publish:release" + + "publish:release": preconditions: - "! grep '\".*\": \"\\(link\\|file\\):.*\"' package.json > /dev/null" cmds: - - task: "test:release" - pnpm publish {{.CLI_ARGS}} diff --git a/examples/change-or-remove-token-keys.js b/examples/change-or-remove-token-keys.js new file mode 100644 index 000000000..a8b9d4f00 --- /dev/null +++ b/examples/change-or-remove-token-keys.js @@ -0,0 +1,205 @@ +import { + AccountId, + Client, + PrivateKey, + Logger, + LogLevel, + PublicKey, + KeyList, + TokenUpdateTransaction, + TokenKeyValidation, + TokenCreateTransaction, + TokenType, + TokenInfoQuery, +} from "@hashgraph/sdk"; +import dotenv from "dotenv"; + +/** + * @description Change ot remove token keys + */ + +async function main() { + // Ensure required environment variables are available + dotenv.config(); + if ( + !process.env.OPERATOR_KEY || + !process.env.OPERATOR_ID || + !process.env.HEDERA_NETWORK + ) { + throw new Error("Please set required keys in .env file."); + } + + const network = process.env.HEDERA_NETWORK; + + // Configure client using environment variables + const operatorId = AccountId.fromString(process.env.OPERATOR_ID); + const operatorKey = PrivateKey.fromStringED25519(process.env.OPERATOR_KEY); + + const client = Client.forName(network).setOperator(operatorId, operatorKey); + + // Set logger + const infoLogger = new Logger(LogLevel.Info); + client.setLogger(infoLogger); + + const adminKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const newSupplyKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + // This HIP introduces ability to remove lower-privilege keys (Wipe, KYC, Freeze, Pause, Supply, Fee Schedule, Metadata) from a Token: + // - using an update with the empty KeyList; + const emptyKeyList = KeyList.of(); + // - updating with an “invalid” key such as an Ed25519 0x0000000000000000000000000000000000000000000000000000000000000000 public key, + // since it is (presumably) impossible to find the 32-byte string whose SHA-512 hash begins with 32 bytes of zeros. + const unusableKey = PublicKey.unusableKey(); + + console.log("====================================================="); + console.log("Initializing token keys..."); + console.log("-----------------------------------------------------"); + console.log("Admin key:", adminKey.publicKey.toString()); + console.log("Supply key:", supplyKey.publicKey.toString()); + console.log("New supply key:", newSupplyKey.publicKey.toString()); + console.log("Wipe key:", wipeKey.publicKey.toString()); + console.log("Freeze key:", freezeKey.publicKey.toString()); + console.log("Pause key:", pauseKey.publicKey.toString()); + console.log("Fee schedule key:", feeScheduleKey.publicKey.toString()); + console.log("Metadata key:", metadataKey.publicKey.toString()); + console.log("Unusable key:", unusableKey.toString()); + console.log("====================================================="); + console.log("\n"); + + let token, tokenInfo, response, receipt, update; + + console.log("====================================================="); + console.log("Creating token..."); + console.log("-----------------------------------------------------"); + token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(client); + + response = await (await token.sign(adminKey)).execute(client); + + receipt = await response.getReceipt(client); + console.log("Token create transction status:", receipt.status.toString()); + + const tokenId = receipt.tokenId; + console.log("Token id:", tokenId.toString()); + console.log("====================================================="); + console.log("\n"); + console.log("====================================================="); + console.log("Token keys:"); + console.log("-----------------------------------------------------"); + tokenInfo = await new TokenInfoQuery().setTokenId(tokenId).execute(client); + + console.log("Token admin key:", tokenInfo.adminKey.toString()); + console.log("Token pause key:", tokenInfo.pauseKey.toString()); + console.log("Token freeze key:", tokenInfo.freezeKey.toString()); + console.log("Token wipe key:", tokenInfo.wipeKey.toString()); + console.log("Token supply key:", tokenInfo.supplyKey.toString()); + console.log("Token fee schedule key:", tokenInfo.feeScheduleKey.toString()); + console.log("Token metadata key:", tokenInfo.metadataKey.toString()); + console.log("====================================================="); + console.log("\n"); + console.log("====================================================="); + console.log("Removing Wipe Key..."); + console.log("-----------------------------------------------------"); + update = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setWipeKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.FullValidation) + .freezeWith(client); + response = await (await update.sign(adminKey)).execute(client); + + receipt = await response.getReceipt(client); + console.log("Token update transaction status:", receipt.status.toString()); + + tokenInfo = await new TokenInfoQuery().setTokenId(tokenId).execute(client); + console.log("Token wipeKey is", tokenInfo.wipeKey); + console.log("====================================================="); + console.log("\n"); + console.log("====================================================="); + console.log("Removing Admin Key..."); + console.log("-----------------------------------------------------"); + update = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setAdminKey(emptyKeyList) + .setKeyVerificationMode(TokenKeyValidation.NoValidation) + .freezeWith(client); + response = await (await update.sign(adminKey)).execute(client); + + receipt = await response.getReceipt(client); + console.log("Token update transaction status:", receipt.status.toString()); + + tokenInfo = await new TokenInfoQuery().setTokenId(tokenId).execute(client); + console.log("Token adminKey is", tokenInfo.adminKey); + console.log("====================================================="); + console.log("\n"); + console.log("====================================================="); + console.log("Updating Supply Key..."); + console.log("-----------------------------------------------------"); + console.log( + "Token supplyKey (before update) is", + tokenInfo.supplyKey.toString(), + ); + update = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(newSupplyKey) + .setKeyVerificationMode(TokenKeyValidation.FullValidation) + .freezeWith(client); + response = await ( + await (await update.sign(supplyKey)).sign(newSupplyKey) + ).execute(client); + + receipt = await response.getReceipt(client); + console.log("Token update transaction status:", receipt.status.toString()); + + tokenInfo = await new TokenInfoQuery().setTokenId(tokenId).execute(client); + console.log( + "Token suppleyKey (after update) is", + tokenInfo.supplyKey.toString(), + ); + console.log("====================================================="); + console.log("\n"); + console.log("====================================================="); + console.log("Updating Supply Key to unusable format..."); + console.log("-----------------------------------------------------"); + console.log( + "Token supplyKey (before update) is", + tokenInfo.supplyKey.toString(), + ); + update = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setSupplyKey(unusableKey) + .setKeyVerificationMode(TokenKeyValidation.NoValidation) + .freezeWith(client); + response = await (await update.sign(newSupplyKey)).execute(client); + + receipt = await response.getReceipt(client); + console.log("Token update transaction status:", receipt.status.toString()); + + tokenInfo = await new TokenInfoQuery().setTokenId(tokenId).execute(client); + + console.log( + "Token suppleyKey (after update) is", + tokenInfo.supplyKey.toString(), + ); + console.log("====================================================="); + + client.close(); +} + +void main(); diff --git a/examples/hts-nftP1-fee-create-mint-burn-associate-transfer.js b/examples/hts-nftP1-fee-create-mint-burn-associate-transfer.js index dcfecf1ab..0fe23e73a 100644 --- a/examples/hts-nftP1-fee-create-mint-burn-associate-transfer.js +++ b/examples/hts-nftP1-fee-create-mint-burn-associate-transfer.js @@ -77,7 +77,6 @@ async function main() { .setCustomFees([nftCustomFee]) .setAdminKey(adminKey) .setSupplyKey(supplyKey) - // .setPauseKey(pauseKey) .setFreezeKey(freezeKey) .setWipeKey(wipeKey) .freezeWith(client) diff --git a/examples/react-native-example/yarn.lock b/examples/react-native-example/yarn.lock index b4aa8926a..f1281c5a8 100644 --- a/examples/react-native-example/yarn.lock +++ b/examples/react-native-example/yarn.lock @@ -3779,9 +3779,9 @@ fast-glob@^3.2.5, fast-glob@^3.2.9: micromatch "^4.0.4" fast-loops@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz" - integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.4.tgz#61bc77d518c0af5073a638c6d9d5c7683f069ce2" + integrity sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg== fast-redact@^3.1.1: version "3.3.0" diff --git a/examples/token-reject.js b/examples/token-reject.js new file mode 100644 index 000000000..cad9db09c --- /dev/null +++ b/examples/token-reject.js @@ -0,0 +1,241 @@ +import { + AccountCreateTransaction, + PrivateKey, + TokenCreateTransaction, + TransferTransaction, + AccountId, + Client, + TokenType, + TokenMintTransaction, + TokenRejectTransaction, + TokenRejectFlow, + NftId, + AccountBalanceQuery, + TokenSupplyType, +} from "@hashgraph/sdk"; +import dotenv from "dotenv"; + +dotenv.config(); + +async function main() { + if ( + process.env.OPERATOR_ID == null || + process.env.OPERATOR_KEY == null || + process.env.HEDERA_NETWORK == null + ) { + throw new Error( + "Environment variables OPERATOR_ID, HEDERA_NETWORK, and OPERATOR_KEY are required.", + ); + } + const CID = [ + "QmNPCiNA3Dsu3K5FxDPMG5Q3fZRwVTg14EXA92uqEeSRXn", + "QmZ4dgAgt8owvnULxnKxNe8YqpavtVCXmc1Lt2XajFpJs9", + "QmPzY5GxevjyfMUF5vEAjtyRoigzWp47MiKAtLBduLMC1T", + ]; + const operatorId = AccountId.fromString(process.env.OPERATOR_ID); + const operatorKey = PrivateKey.fromStringED25519(process.env.OPERATOR_KEY); + const network = process.env.HEDERA_NETWORK; + const client = Client.forName(network).setOperator(operatorId, operatorKey); + + // create a treasury account + const treasuryPrivateKey = PrivateKey.generateED25519(); + const treasuryAccountId = ( + await ( + await new AccountCreateTransaction() + .setKey(treasuryPrivateKey) + .setMaxAutomaticTokenAssociations(100) + .execute(client) + ).getReceipt(client) + ).accountId; + + // create a receiver account with unlimited max auto associations + const receiverPrivateKey = PrivateKey.generateED25519(); + const receiverAccountId = ( + await ( + await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setMaxAutomaticTokenAssociations(-1) + .execute(client) + ).getReceipt(client) + ).accountId; + + // create a nft collection + const nftCreationTx = await ( + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("Example Fungible Token") + .setTokenSymbol("EFT") + .setMaxSupply(CID.length) + .setSupplyType(TokenSupplyType.Finite) + .setSupplyKey(operatorKey) + .setAdminKey(operatorKey) + .setTreasuryAccountId(treasuryAccountId) + .freezeWith(client) + .sign(treasuryPrivateKey) + ).execute(client); + + const nftId = (await nftCreationTx.getReceipt(client)).tokenId; + console.log("NFT ID: ", nftId.toString()); + + // create a fungible token + const ftCreationTx = await ( + await new TokenCreateTransaction() + .setTokenName("Example Fungible Token") + .setTokenSymbol("EFT") + .setInitialSupply(100000000) + .setSupplyKey(operatorKey) + .setAdminKey(operatorKey) + .setTreasuryAccountId(treasuryAccountId) + .freezeWith(client) + .sign(treasuryPrivateKey) + ).execute(client); + + const ftId = (await ftCreationTx.getReceipt(client)).tokenId; + console.log("FT ID: ", ftId.toString()); + + // mint 3 NFTs to treasury + const nftSerialIds = []; + for (let i = 0; i < CID.length; i++) { + const { serials } = await ( + await new TokenMintTransaction() + .setTokenId(nftId) + .addMetadata(Buffer.from(CID[i])) + .execute(client) + ).getReceipt(client); + const [serial] = serials; + nftSerialIds.push(new NftId(nftId, serial)); + } + + // transfer nfts to receiver + await ( + await ( + await new TransferTransaction() + .addNftTransfer( + nftSerialIds[0], + treasuryAccountId, + receiverAccountId, + ) + .addNftTransfer( + nftSerialIds[1], + treasuryAccountId, + receiverAccountId, + ) + .addNftTransfer( + nftSerialIds[2], + treasuryAccountId, + receiverAccountId, + ) + .freezeWith(client) + .sign(treasuryPrivateKey) + ).execute(client) + ).getReceipt(client); + + // transfer fungible tokens to receiver + await ( + await ( + await new TransferTransaction() + .addTokenTransfer(ftId, treasuryAccountId, -1) + .addTokenTransfer(ftId, receiverAccountId, 1) + .freezeWith(client) + .sign(treasuryPrivateKey) + ).execute(client) + ).getReceipt(client); + + console.log("======================="); + console.log("Before Token Reject"); + console.log("======================="); + const receiverFTBalanceBefore = ( + await new AccountBalanceQuery() + .setAccountId(receiverAccountId) + .execute(client) + ).tokens.get(ftId); + const treasuryFTBalanceBefore = ( + await new AccountBalanceQuery() + .setAccountId(treasuryAccountId) + .execute(client) + ).tokens.get(ftId); + const receiverNFTBalanceBefore = ( + await new AccountBalanceQuery() + .setAccountId(receiverAccountId) + .execute(client) + ).tokens.get(nftId); + const treasuryNFTBalanceBefore = ( + await new AccountBalanceQuery() + .setAccountId(treasuryAccountId) + .execute(client) + ).tokens.get(nftId); + console.log("Receiver FT balance: ", receiverFTBalanceBefore.toInt()); + console.log("Treasury FT balance: ", treasuryFTBalanceBefore.toInt()); + console.log( + "Receiver NFT balance: ", + receiverNFTBalanceBefore ? receiverNFTBalanceBefore.toInt() : 0, + ); + console.log("Treasury NFT balance: ", treasuryNFTBalanceBefore.toInt()); + + // reject fungible tokens back to treasury + const tokenRejectResponse = await ( + await ( + await new TokenRejectTransaction() + .setOwnerId(receiverAccountId) + .addTokenId(ftId) + .freezeWith(client) + .sign(receiverPrivateKey) + ).execute(client) + ).getReceipt(client); + + // reject NFTs back to treasury + const rejectFlowResponse = await ( + await ( + new TokenRejectFlow() + .setOwnerId(receiverAccountId) + .setNftIds(nftSerialIds) + .freezeWith(client) + .sign(receiverPrivateKey) + ).execute(client) + ).getReceipt(client); + + const tokenRejectStatus = tokenRejectResponse.status.toString(); + const tokenRejectFlowStatus = rejectFlowResponse.status.toString(); + + console.log("======================="); + console.log("After Token Reject Transaction and flow"); + console.log("======================="); + + const receiverFTBalanceAfter = ( + await new AccountBalanceQuery() + .setAccountId(receiverAccountId) + .execute(client) + ).tokens.get(ftId); + + const treasuryFTBalanceAfter = ( + await new AccountBalanceQuery() + .setAccountId(treasuryAccountId) + .execute(client) + ).tokens.get(ftId); + + const receiverNFTBalanceAfter = ( + await new AccountBalanceQuery() + .setAccountId(receiverAccountId) + .execute(client) + ).tokens.get(nftId); + + const treasuryNFTBalanceAfter = ( + await new AccountBalanceQuery() + .setAccountId(treasuryAccountId) + .execute(client) + ).tokens.get(nftId); + + console.log("TokenReject response:", tokenRejectStatus); + console.log("TokenRejectFlow response:", tokenRejectFlowStatus); + console.log("Receiver FT balance: ", receiverFTBalanceAfter.toInt()); + console.log("Treasury FT balance: ", treasuryFTBalanceAfter.toInt()); + console.log( + "Receiver NFT balance: ", + receiverNFTBalanceAfter ? receiverNFTBalanceAfter.toInt() : 0, + ); + console.log("Treasury NFT balance: ", treasuryNFTBalanceAfter.toInt()); + + client.close(); +} + +void main(); diff --git a/examples/update-fungible-token-metadata-with-admin-key.js b/examples/update-fungible-token-metadata-with-admin-key.js new file mode 100644 index 000000000..c9031ea43 --- /dev/null +++ b/examples/update-fungible-token-metadata-with-admin-key.js @@ -0,0 +1,104 @@ +import { + TokenCreateTransaction, + TokenInfoQuery, + TokenType, + PrivateKey, + Client, + AccountId, + TokenUpdateTransaction, +} from "@hashgraph/sdk"; +import dotenv from "dotenv"; + +dotenv.config(); + +/** + * @summary E2E-HIP-646 https://hips.hedera.com/hip/hip-646 + * @description Update fungible token metadata with admin key + */ +async function main() { + if ( + !process.env.OPERATOR_KEY || + !process.env.OPERATOR_ID || + !process.env.HEDERA_NETWORK + ) { + throw new Error("Please set required keys in .env file."); + } + + const operatorId = AccountId.fromString(process.env.OPERATOR_ID); + const operatorKey = PrivateKey.fromStringDer(process.env.OPERATOR_KEY); + const network = process.env.HEDERA_NETWORK; + const client = Client.forName(network).setOperator(operatorId, operatorKey); + + // Generate a admin key + const adminKey = PrivateKey.generateED25519(); + // Initial metadata + const metadata = new Uint8Array([1]); + // New metadata + const newMetadata = new Uint8Array([1, 2]); + + let tokenInfo; + + try { + // Create a non fungible token + let createTokenTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setMetadata(metadata) + .setTokenType(TokenType.FungibleCommon) // The same flow can be executed with a TokenType.NON_FUNGIBLE_UNIQUE (i.e. HIP-765) + .setDecimals(3) + .setInitialSupply(10000) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .freezeWith(client); + + // Sign and execute create token transaction + const tokenCreateTxResponse = await ( + await createTokenTx.sign(adminKey) + ).execute(client); + + // Get receipt for create token transaction + const tokenCreateTxReceipt = + await tokenCreateTxResponse.getReceipt(client); + console.log( + `Status of token create transction: ${tokenCreateTxReceipt.status.toString()}`, + ); + + // Get token id + const tokenId = tokenCreateTxReceipt.tokenId; + console.log(`Token id: ${tokenId.toString()}`); + + // Get token info + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(client); + console.log(`Token metadata:`, tokenInfo.metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(client); + + // Sign transactions with admin key and execute it + const tokenUpdateTxResponse = await ( + await tokenUpdateTx.sign(adminKey) + ).execute(client); + + // Get receipt for token update transaction + const tokenUpdateTxReceipt = + await tokenUpdateTxResponse.getReceipt(client); + console.log( + `Status of token update transction: ${tokenUpdateTxReceipt.status.toString()}`, + ); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(client); + console.log(`Token updated metadata:`, tokenInfo.metadata); + } catch (error) { + console.log(error); + } + + client.close(); +} + +void main(); diff --git a/examples/update-fungible-token-metadata-with-metadata-key.js b/examples/update-fungible-token-metadata-with-metadata-key.js new file mode 100644 index 000000000..99227d7d6 --- /dev/null +++ b/examples/update-fungible-token-metadata-with-metadata-key.js @@ -0,0 +1,104 @@ +import { + TokenCreateTransaction, + TokenInfoQuery, + TokenType, + PrivateKey, + Client, + AccountId, + TokenUpdateTransaction, +} from "@hashgraph/sdk"; +import dotenv from "dotenv"; + +dotenv.config(); + +/** + * @summary E2E-HIP-646 https://hips.hedera.com/hip/hip-646 + * @description Update fungible token metadata with metadata key + */ +async function main() { + if ( + !process.env.OPERATOR_KEY || + !process.env.OPERATOR_ID || + !process.env.HEDERA_NETWORK + ) { + throw new Error("Please set required keys in .env file."); + } + + const operatorId = AccountId.fromString(process.env.OPERATOR_ID); + const operatorKey = PrivateKey.fromStringDer(process.env.OPERATOR_KEY); + const network = process.env.HEDERA_NETWORK; + const client = Client.forName(network).setOperator(operatorId, operatorKey); + + // Generate a metadata key + const metadataKey = PrivateKey.generateED25519(); + // Initial metadata + const metadata = new Uint8Array([1]); + // New metadata + const newMetadata = new Uint8Array([1, 2]); + + let tokenInfo; + + try { + // Create a non fungible token + let createTokenTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setMetadata(metadata) + .setTokenType(TokenType.FungibleCommon) // The same flow can be executed with a TokenType.NON_FUNGIBLE_UNIQUE (i.e. HIP-765) + .setDecimals(3) + .setInitialSupply(10000) + .setTreasuryAccountId(operatorId) + .setMetadataKey(metadataKey) + .freezeWith(client); + + // Sign and execute create token transaction + const tokenCreateTxResponse = await ( + await createTokenTx.sign(operatorKey) + ).execute(client); + + // Get receipt for create token transaction + const tokenCreateTxReceipt = + await tokenCreateTxResponse.getReceipt(client); + console.log( + `Status of token create transction: ${tokenCreateTxReceipt.status.toString()}`, + ); + + // Get token id + const tokenId = tokenCreateTxReceipt.tokenId; + console.log(`Token id: ${tokenId.toString()}`); + + // Get token info + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(client); + console.log(`Token metadata:`, tokenInfo.metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(client); + + // Sign transactions with metadata key and execute it + const tokenUpdateTxResponse = await ( + await tokenUpdateTx.sign(metadataKey) + ).execute(client); + + // Get receipt for token update transaction + const tokenUpdateTxReceipt = + await tokenUpdateTxResponse.getReceipt(client); + console.log( + `Status of token update transction: ${tokenUpdateTxReceipt.status.toString()}`, + ); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(client); + console.log(`Token updated metadata:`, tokenInfo.metadata); + } catch (error) { + console.log(error); + } + + client.close(); +} + +void main(); diff --git a/examples/update-nfts-metadata-of-non-fungible-token.js b/examples/update-nfts-metadata-of-non-fungible-token.js new file mode 100644 index 000000000..e39ea6455 --- /dev/null +++ b/examples/update-nfts-metadata-of-non-fungible-token.js @@ -0,0 +1,178 @@ +import { + TokenCreateTransaction, + TokenInfoQuery, + TokenType, + PrivateKey, + Client, + AccountId, + TokenMintTransaction, + TokenUpdateNftsTransaction, + TokenNftInfoQuery, + NftId, + AccountCreateTransaction, + Hbar, + TransferTransaction, + TokenAssociateTransaction, +} from "@hashgraph/sdk"; +import dotenv from "dotenv"; + +dotenv.config(); + +/** + * @summary E2E-HIP-657 https://hips.hedera.com/hip/hip-657 + * @description Update nfts metadata of fungible token with metadata key + */ +async function main() { + if ( + !process.env.OPERATOR_KEY || + !process.env.OPERATOR_ID || + !process.env.HEDERA_NETWORK + ) { + throw new Error("Please set required keys in .env file."); + } + + const operatorId = AccountId.fromString(process.env.OPERATOR_ID); + const operatorKey = PrivateKey.fromStringDer(process.env.OPERATOR_KEY); + const network = process.env.HEDERA_NETWORK; + const client = Client.forName(network).setOperator(operatorId, operatorKey); + + // Generate a metadata key + const metadataKey = PrivateKey.generateED25519(); + // Generate a supply key + const supplyKey = PrivateKey.generateED25519(); + // Initial metadata + const metadata = new Uint8Array([1]); + // New metadata + const newMetadata = new Uint8Array([1, 2]); + + let tokenNftsInfo, nftInfo; + + try { + // Create a non fungible token + let createTokenTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setSupplyKey(supplyKey) + .setMetadataKey(metadataKey) + .freezeWith(client); + + // Sign and execute create token transaction + const tokenCreateTxResponse = await ( + await createTokenTx.sign(operatorKey) + ).execute(client); + + // Get receipt for create token transaction + const tokenCreateTxReceipt = + await tokenCreateTxResponse.getReceipt(client); + console.log( + `Status of token create transction: ${tokenCreateTxReceipt.status.toString()}`, + ); + + // Get token id + const tokenId = tokenCreateTxReceipt.tokenId; + console.log(`Token id: ${tokenId.toString()}`); + + // Get token info + const tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(client); + console.log(`Token metadata key: ${tokenInfo.metadataKey?.toString()}`); + + // Mint token + const tokenMintTx = new TokenMintTransaction() + .setMetadata([metadata]) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintTxResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintTxReceipt = await tokenMintTxResponse.getReceipt(client); + console.log( + `Status of token mint transction: ${tokenMintTxReceipt.status.toString()}`, + ); + + const nftSerial = tokenMintTxReceipt.serials[0]; + + // Get TokenNftInfo to show the metadata on the NFT created + tokenNftsInfo = await new TokenNftInfoQuery() + .setNftId(new NftId(tokenId, nftSerial)) + .execute(client); + nftInfo = tokenNftsInfo[0]; + console.log(`Set token NFT metadata:`, nftInfo.metadata); + + // Create an account to transfer the NFT to + const accountCreateTx = new AccountCreateTransaction() + .setKey(operatorKey) + .setMaxAutomaticTokenAssociations(10) + .setInitialBalance(new Hbar(100)) + .freezeWith(client); + + const accountCreateTxResponse = await ( + await accountCreateTx.sign(operatorKey) + ).execute(client); + const accountCreateTxReceipt = + await accountCreateTxResponse.getReceipt(client); + const newAccountId = accountCreateTxReceipt.accountId; + console.log(`New account id: ${newAccountId.toString()}`); + + const tokenAssociateTx = new TokenAssociateTransaction() + .setAccountId(newAccountId) + .setTokenIds([tokenId]) + .freezeWith(client); + + const tokenAssociateTxResponse = await ( + await tokenAssociateTx.sign(operatorKey) + ).execute(client); + const tokenAssociateTxReceipt = + await tokenAssociateTxResponse.getReceipt(client); + console.log( + `Status of token associate transaction: ${tokenAssociateTxReceipt.status.toString()}`, + ); + + // Transfer nft to the new account + const transferNftTx = new TransferTransaction() + .addNftTransfer(tokenId, nftSerial, operatorId, newAccountId) + .freezeWith(client); + + const transferNftTxResponse = await ( + await transferNftTx.sign(operatorKey) + ).execute(client); + const transferNftTxReceipt = + await transferNftTxResponse.getReceipt(client); + console.log( + `Status of transfer NFT transaction: ${transferNftTxReceipt.status.toString()}`, + ); + + // Update nfts metadata + const tokenUpdateNftsTx = new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerialNumbers([nftSerial]) + .setMetadata(newMetadata) + .freezeWith(client); + + const tokenUpdateNftsTxResponse = await ( + await tokenUpdateNftsTx.sign(metadataKey) + ).execute(client); + const tokenUpdateNftsTxReceipt = + await tokenUpdateNftsTxResponse.getReceipt(client); + console.log( + `Status of token update nfts transction: ${tokenUpdateNftsTxReceipt.status.toString()}`, + ); + + // Get token nfts info in order to show the metadata on the NFT created + tokenNftsInfo = await new TokenNftInfoQuery() + .setNftId(new NftId(tokenId, nftSerial)) + .execute(client); + nftInfo = tokenNftsInfo[0]; + console.log(`Updated token NFT metadata:`, nftInfo.metadata); + } catch (error) { + console.log(error); + } + + client.close(); +} + +void main(); diff --git a/package.json b/package.json index fc254f1d9..d7efe9bc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hashgraph/sdk", - "version": "2.43.0-beta.1", + "version": "2.49.2", "description": "Hedera™ Hashgraph SDK", "types": "./lib/index.d.ts", "main": "./lib/index.cjs", @@ -58,7 +58,7 @@ "@ethersproject/rlp": "^5.7.0", "@grpc/grpc-js": "1.8.2", "@hashgraph/cryptography": "1.4.8-beta.5", - "@hashgraph/proto": "2.14.0-beta.5", + "@hashgraph/proto": "2.15.0-beta.3", "axios": "^1.6.4", "bignumber.js": "^9.1.1", "bn.js": "^5.1.1", @@ -68,8 +68,8 @@ "pino": "^8.14.1", "pino-pretty": "^10.0.0", "protobufjs": "^7.2.5", - "utf8": "^3.0.0", - "rfc4648": "^1.5.3" + "rfc4648": "^1.5.3", + "utf8": "^3.0.0" }, "devDependencies": { "@babel/cli": "^7.23.4", @@ -116,6 +116,7 @@ "npx": "^10.2.2", "nyc": "^15.1.0", "prettier": "^3.0.3", + "sinon": "^18.0.0", "typedoc": "^0.25.1", "typescript": "^5.1.6", "vite": "^4.4.9", @@ -129,4 +130,4 @@ "optional": true } } -} +} \ No newline at end of file diff --git a/packages/cryptography/Taskfile.yml b/packages/cryptography/Taskfile.yml index 65cf6febe..8cf64b443 100644 --- a/packages/cryptography/Taskfile.yml +++ b/packages/cryptography/Taskfile.yml @@ -22,8 +22,9 @@ tasks: - pnpm i > /dev/null 2>&1 build: + deps: + - install cmds: - - task: install - task: format - task: lint - npx babel src -d lib --out-file-extension .cjs > /dev/null diff --git a/packages/cryptography/src/PrivateKey.js b/packages/cryptography/src/PrivateKey.js index aeaf883b1..44cd16acc 100644 --- a/packages/cryptography/src/PrivateKey.js +++ b/packages/cryptography/src/PrivateKey.js @@ -138,7 +138,7 @@ export default class PrivateKey extends Key { if (data.length == 32) { console.warn( - "WARNING: Consider using fromStringECDSA() or fromStringED2551() on a HEX-encoded string and fromStringDer() on a HEX-encoded string with DER prefix instead.", + "WARNING: Consider using fromStringECDSA() or fromStringED25519() on a HEX-encoded string and fromStringDer() on a HEX-encoded string with DER prefix instead.", ); } diff --git a/packages/proto/Taskfile.yml b/packages/proto/Taskfile.yml index 8c4cf4251..9a12e8f20 100644 --- a/packages/proto/Taskfile.yml +++ b/packages/proto/Taskfile.yml @@ -26,7 +26,7 @@ tasks: # using tag --remote in order to always apply the newest proto changes - git submodule update --init --remote status: - - test -d packages/proto/src/proto + - test -e src/proto/.git install: deps: @@ -46,7 +46,7 @@ tasks: - npx babel src -d lib - npx copyfiles -u 1 src/index.d.ts src/proto.d.ts lib/ > /dev/null # This is necessary to correctly run browser tests with an unpublished proto package - # - ../../node_modules/.bin/yalc publish > /dev/null + - npx yalc publish > /dev/null clean: cmds: @@ -79,9 +79,21 @@ tasks: - build update: + dir: src/proto + vars: + latest_tag: + sh: git -c versionsort.suffix=-alpha + -c versionsort.suffix=-beta + -c versionsort.suffix=-rc + tag -l --sort=version:refname|tail -1 + proto: '{{.proto | default .latest_tag}}' cmds: - - cd src/proto && git pull origin main && git checkout main + - echo "Protobuf version set to {{.proto}}" + - git fetch origin + - git checkout {{.proto}} + - git show-ref --verify -q refs/heads/{{.proto}} && git pull origin || exit 0 - task: build + - echo "Sucessfully updated protobufs to {{.proto}}" publish: preconditions: diff --git a/packages/proto/package.json b/packages/proto/package.json index 1b87cd3ab..ca407aaf2 100644 --- a/packages/proto/package.json +++ b/packages/proto/package.json @@ -1,6 +1,6 @@ { "name": "@hashgraph/proto", - "version": "2.14.0-beta.5", + "version": "2.15.0-beta.3", "description": "Protobufs for the Hedera™ Hashgraph SDK", "main": "lib/index.js", "browser": "src/index.js", @@ -67,4 +67,4 @@ "typescript": "^5.1.6", "protobufjs-cli": "^1.0.2" } -} +} \ No newline at end of file diff --git a/packages/proto/src/proto b/packages/proto/src/proto index 6d41db355..141302ce2 160000 --- a/packages/proto/src/proto +++ b/packages/proto/src/proto @@ -1 +1 @@ -Subproject commit 6d41db35567c63073fd32be70cdae601a9e19aaf +Subproject commit 141302ce26bd0c2023d4d031ed207d1e05917688 diff --git a/src/Executable.js b/src/Executable.js index 0171cea13..19205c1b9 100644 --- a/src/Executable.js +++ b/src/Executable.js @@ -23,10 +23,10 @@ import GrpcStatus from "./grpc/GrpcStatus.js"; import List from "./transaction/List.js"; import * as hex from "./encoding/hex.js"; import HttpError from "./http/HttpError.js"; +import Status from "./Status.js"; /** * @typedef {import("./account/AccountId.js").default} AccountId - * @typedef {import("./Status.js").default} Status * @typedef {import("./channel/Channel.js").default} Channel * @typedef {import("./channel/MirrorChannel.js").default} MirrorChannel * @typedef {import("./transaction/TransactionId.js").default} TransactionId @@ -46,6 +46,7 @@ export const ExecutionState = { }; export const RST_STREAM = /\brst[^0-9a-zA-Z]stream\b/i; +export const DEFAULT_MAX_ATTEMPTS = 10; /** * @abstract @@ -62,7 +63,7 @@ export default class Executable { * @internal * @type {number} */ - this._maxAttempts = 10; + this._maxAttempts = DEFAULT_MAX_ATTEMPTS; /** * List of node account IDs for each transaction that has been @@ -314,10 +315,11 @@ export default class Executable { * @internal * @param {RequestT} request * @param {ResponseT} response + * @param {AccountId} nodeId * @returns {Error} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - _mapStatusError(request, response) { + _mapStatusError(request, response, nodeId) { throw new Error("not implemented"); } @@ -610,7 +612,7 @@ export default class Executable { // If the node is unhealthy, wait for it to be healthy // FIXME: This is wrong, we should skip to the next node, and only perform // a request backoff after we've tried all nodes in the current list. - if (!node.isHealthy()) { + if (!node.isHealthy() && this._nodeAccountIds.length > 1) { if (this._logger) { this._logger.debug( `[${logId}] node is not healthy, skipping waiting ${node.getRemainingTime()}`, @@ -705,9 +707,12 @@ export default class Executable { // For transactions this would be as simple as checking the response status is `OK` // while for _most_ queries it would check if the response status is `SUCCESS` // The only odd balls are `TransactionReceiptQuery` and `TransactionRecordQuery` - const [err, shouldRetry] = this._shouldRetry(request, response); - if (err != null) { - persistentError = err; + const [status, shouldRetry] = this._shouldRetry(request, response); + if ( + status.toString() !== Status.Ok.toString() && + status.toString() !== Status.Success.toString() + ) { + persistentError = status; } // Determine by the executing state what we should do @@ -722,10 +727,14 @@ export default class Executable { case ExecutionState.Finished: return this._mapResponse(response, nodeAccountId, request); case ExecutionState.Error: - throw this._mapStatusError(request, response); + throw this._mapStatusError( + request, + response, + nodeAccountId, + ); default: throw new Error( - "(BUG) non-exhuastive switch statement for `ExecutionState`", + "(BUG) non-exhaustive switch statement for `ExecutionState`", ); } } @@ -742,7 +751,7 @@ export default class Executable { /** * The current purpose of this method is to easily support signature providers since * signature providers need to serialize _any_ request into bytes. `Query` and `Transaction` - * already implement `toBytes()` so it only made sense to make it avaiable here too. + * already implement `toBytes()` so it only made sense to make it available here too. * * @abstract * @returns {Uint8Array} diff --git a/src/Mnemonic.js b/src/Mnemonic.js index 49146e293..4827178e4 100644 --- a/src/Mnemonic.js +++ b/src/Mnemonic.js @@ -25,6 +25,8 @@ import CACHE from "./Cache.js"; * @typedef {import("./PrivateKey.js").default} PrivateKey */ +const HARDENED_BIT = 0x80000000; + /** * Multi-word mnemonic phrase (BIP-39). * @@ -136,6 +138,58 @@ export default class Mnemonic { ); } + /** + * Converts a derivation path from string to an array of integers. + * Note that this expects precisely 5 components in the derivation path, + * as per BIP-44: + * `m / purpose' / coin_type' / account' / change / address_index` + * Takes into account `'` for hardening as per BIP-32, + * and does not prescribe which components should be hardened. + * + * @param {string} derivationPath the derivation path in BIP-44 format, + * e.g. "m/44'/60'/0'/0/0" + * @returns {Array} to be used with PrivateKey#derive + */ + calculateDerivationPathValues(derivationPath) { + // Parse the derivation path from string into values + const pattern = /m\/(\d+'?)\/(\d+'?)\/(\d+'?)\/(\d+'?)\/(\d+'?)/; + const matches = pattern.exec(derivationPath); + const values = new Array(5); // as Array; + if (matches) { + // Extract numbers and use apostrophe to select if is hardened + for (let i = 1; i <= 5; i++) { + let value = matches[i]; + if (value.endsWith("'")) { + value = value.substring(0, value.length - 1); + values[i - 1] = parseInt(value, 10) | HARDENED_BIT; + } else { + values[i - 1] = parseInt(value, 10); + } + } + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return values; + } + + /** + * Common implementation for both `toStandardECDSAsecp256k1PrivateKey` + * functions. + * + * @param {string} passphrase the passphrase used to protect the + * mnemonic, use "" for none + * @param {Array} derivationPathValues derivation path as an + * integer array, + * see: `calculateDerivationPathValues` + * @returns {Promise} a private key + */ + async toStandardECDSAsecp256k1PrivateKeyImpl( + passphrase, + derivationPathValues, + ) { + // eslint-disable-next-line deprecation/deprecation + return await this.toEcdsaPrivateKey(passphrase, derivationPathValues); + } + /** * Recover an ECDSA private key from this mnemonic phrase, with an * optional passphrase. @@ -153,6 +207,28 @@ export default class Mnemonic { ); } + /** + * Recover an ECDSAsecp256k1 private key from this mnemonic phrase and + * derivation path, with an optional passphrase + * + * @param {string} passphrase the passphrase used to protect the mnemonic, + * use "" for none + * @param {string} derivationPath the derivation path in BIP-44 format, + * e.g. "m/44'/60'/0'/0/0" + * @returns {Promise} the private key + */ + async toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( + passphrase = "", + derivationPath, + ) { + const derivationPathValues = + this.calculateDerivationPathValues(derivationPath); + return await this.toStandardECDSAsecp256k1PrivateKeyImpl( + passphrase, + derivationPathValues, + ); + } + /** * Recover a mnemonic phrase from a string, splitting on spaces. Handles 12, 22 (legacy), and 24 words. * diff --git a/src/PrecheckStatusError.js b/src/PrecheckStatusError.js index 4e62e43fd..c7deee624 100644 --- a/src/PrecheckStatusError.js +++ b/src/PrecheckStatusError.js @@ -24,6 +24,7 @@ import StatusError from "./StatusError.js"; * @typedef {import("./Status.js").default} Status * @typedef {import("./transaction/TransactionId.js").default} TransactionId * @typedef {import("./contract/ContractFunctionResult.js").default} ContractFunctionResult + * @typedef {import("./account/AccountId.js").default} AccountId */ /** @@ -31,6 +32,7 @@ import StatusError from "./StatusError.js"; * @property {string} name * @property {string} status * @property {string} transactionId + * @property {?string | null} nodeId * @property {string} message * @property {?ContractFunctionResult} contractFunctionResult */ @@ -40,12 +42,13 @@ export default class PrecheckStatusError extends StatusError { * @param {object} props * @param {Status} props.status * @param {TransactionId} props.transactionId + * @param {AccountId} props.nodeId * @param {?ContractFunctionResult} props.contractFunctionResult */ constructor(props) { super( props, - `transaction ${props.transactionId.toString()} failed precheck with status ${props.status.toString()}`, + `transaction ${props.transactionId.toString()} failed precheck with status ${props.status.toString()} against node account id ${props.nodeId.toString()}`, ); /** @@ -53,6 +56,12 @@ export default class PrecheckStatusError extends StatusError { * @readonly */ this.contractFunctionResult = props.contractFunctionResult; + + /** + * @type {AccountId} + * @readonly + */ + this.nodeId = props.nodeId; } /** @@ -63,6 +72,7 @@ export default class PrecheckStatusError extends StatusError { name: this.name, status: this.status.toString(), transactionId: this.transactionId.toString(), + nodeId: this.nodeId.toString(), message: this.message, contractFunctionResult: this.contractFunctionResult, }; diff --git a/src/PublicKey.js b/src/PublicKey.js index bda8869ba..47905a76d 100644 --- a/src/PublicKey.js +++ b/src/PublicKey.js @@ -282,6 +282,19 @@ export default class PublicKey extends Key { toAccountId(shard, realm) { return CACHE.accountIdConstructor(shard, realm, this); } + + /** + * Returns an "unusable" public key. + * “Unusable” refers to a key such as an Ed25519 0x00000... public key, + * since it is (presumably) impossible to find the 32-byte string whose SHA-512 hash begins with 32 bytes of zeros. + * + * @returns {PublicKey} + */ + static unusableKey() { + return PublicKey.fromStringED25519( + "0000000000000000000000000000000000000000000000000000000000000000", + ); + } } CACHE.setPublicKeyED25519((key) => PublicKey.fromBytesED25519(key)); diff --git a/src/RequestType.js b/src/RequestType.js index c785cb2d9..9412ccf8e 100644 --- a/src/RequestType.js +++ b/src/RequestType.js @@ -191,6 +191,20 @@ export default class RequestType { return "TransactionGetFastRecord"; case RequestType.TokenUpdateNfts: return "TokenUpdateNfts"; + case RequestType.NodeCreate: + return "NodeCreate"; + case RequestType.NodeUpdate: + return "NodeUpdate"; + case RequestType.NodeDelete: + return "NodeDelete"; + case RequestType.TokenReject: + return "TokenReject"; + case RequestType.TokenAirdrop: + return "TokenAirdrop"; + case RequestType.TokenCancelAirdrop: + return "TokenCancelAirdrop"; + case RequestType.TokenClaimAirdrop: + return "TokenClaimAirdrop"; default: return `UNKNOWN (${this._code})`; } @@ -353,6 +367,20 @@ export default class RequestType { return RequestType.TransactionGetFastRecord; case 88: return RequestType.TokenUpdateNfts; + case 89: + return RequestType.NodeCreate; + case 90: + return RequestType.NodeUpdate; + case 91: + return RequestType.NodeDelete; + case 92: + return RequestType.TokenReject; + case 93: + return RequestType.TokenAirdrop; + case 94: + return RequestType.TokenCancelAirdrop; + case 95: + return RequestType.TokenClaimAirdrop; } throw new Error( @@ -740,3 +768,38 @@ RequestType.TransactionGetFastRecord = new RequestType(87); * Update the metadata of one or more NFT's of a specific token type. */ RequestType.TokenUpdateNfts = new RequestType(88); + +/** + * A transaction body for a `createNode` request. + */ +RequestType.NodeCreate = new RequestType(89); + +/** + * A transaction body for an `updateNode` request. + */ +RequestType.NodeUpdate = new RequestType(90); + +/** + * A transaction body for a `deleteNode` request. + */ +RequestType.NodeDelete = new RequestType(91); + +/** + * Transfer one or more token balances held by the requesting account to the treasury for each token type. + */ +RequestType.TokenReject = new RequestType(92); + +/** + * Airdrop one or more tokens to one or more accounts. + */ +RequestType.TokenAirdrop = new RequestType(93); + +/** + * Remove one or more pending airdrops from state on behalf of the sender(s) for each airdrop. + */ +RequestType.TokenCancelAirdrop = new RequestType(94); + +/** + * Claim one or more pending airdrops + */ +RequestType.TokenClaimAirdrop = new RequestType(95); diff --git a/src/Status.js b/src/Status.js index e03f1fca1..9a4d70cd7 100644 --- a/src/Status.js +++ b/src/Status.js @@ -631,6 +631,64 @@ export default class Status { return "MISSING_TOKEN_METADATA"; case Status.MissingSerialNumbers: return "MISSING_SERIAL_NUMBERS"; + case Status.TokenHasNoAdminKey: + return "TOKEN_HAS_NO_ADMIN_KEY"; + case Status.NodeDeleted: + return "NODE_DELETED"; + case Status.InvalidNodeId: + return "INVALID_NODE_ID"; + case Status.InvalidGossipEndpoint: + return "INVALID_GOSSIP_ENDPOINT"; + case Status.InvalidNodeAccountId: + return "INVALID_NODE_ACCOUNT_ID"; + case Status.InvalidNodeDescription: + return "INVALID_NODE_DESCRIPTION"; + case Status.InvalidServiceEndpoint: + return "INVALID_SERVICE_ENDPOINT"; + case Status.InvalidGossipCaCertificate: + return "INVALID_GOSSIP_CA_CERTIFICATE"; + case Status.InvalidGrpcCertificate: + return "INVALID_GRPC_CERTIFICATE"; + case Status.InvalidMaxAutoAssociations: + return "INVALID_MAX_AUTO_ASSOCIATIONS"; + case Status.MaxNodesCreated: + return "MAX_NODES_CREATED"; + case Status.IpFqdnCannotBeSetForSameEndpoint: + return "IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT"; + case Status.GossipEndpointCannotHaveFqdn: + return "GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN"; + case Status.FqdnSizeTooLarge: + return "FQDN_SIZE_TOO_LARGE"; + case Status.InvalidEndpoint: + return "INVALID_ENDPOINT"; + case Status.GossipEndpointsExceededLimit: + return "GOSSIP_ENDPOINTS_EXCEEDED_LIMIT"; + case Status.ServiceEndpointsExceededLimit: + return "SERVICE_ENDPOINTS_EXCEEDED_LIMIT"; + case Status.InvalidIpv4Address: + return "INVALID_IPV4_ADDRESS"; + case Status.TokenReferenceRepeated: + return "TOKEN_REFERENCE_REPEATED"; + case Status.InvalidOwnerId: + return "INVALID_OWNER_ID"; + case Status.TokenReferenceListSizeLimitExceeded: + return "TOKEN_REFERENCE_LIST_SIZE_LIMIT_EXCEEDED"; + case Status.EmptyTokenReferenceList: + return "EMPTY_TOKEN_REFERENCE_LIST"; + case Status.UpdateNodeAccountNotAllowed: + return "UPDATE_NODE_ACCOUNT_NOT_ALLOWED"; + case Status.TokenHasNoMetadataOrSupplyKey: + return "TOKEN_HAS_NO_METADATA_OR_SUPPLY_KEY"; + case Status.EmptyPendingAirdropIdList: + return "EMPTY_PENDING_AIRDROP_ID_LIST"; + case Status.PendingAirdropIdRepeated: + return "PENDING_AIRDROP_ID_REPEATED"; + case Status.MaxPendingAirdropIdExceeded: + return "MAX_PENDING_AIRDROP_ID_EXCEEDED"; + case Status.PendingNftAirdropAlreadyExists: + return "PENDING_NFT_AIRDROP_ALREADY_EXISTS"; + case Status.AccountHasPendingAirdrops: + return "ACCOUNT_HAS_PENDING_AIRDROPS"; default: return `UNKNOWN (${this._code})`; } @@ -1233,6 +1291,64 @@ export default class Status { return Status.MissingTokenMetadata; case 336: return Status.MissingSerialNumbers; + case 337: + return Status.TokenHasNoAdminKey; + case 338: + return Status.NodeDeleted; + case 339: + return Status.InvalidNodeId; + case 340: + return Status.InvalidGossipEndpoint; + case 341: + return Status.InvalidNodeAccountId; + case 342: + return Status.InvalidNodeDescription; + case 343: + return Status.InvalidServiceEndpoint; + case 344: + return Status.InvalidGossipCaCertificate; + case 345: + return Status.InvalidGrpcCertificate; + case 346: + return Status.InvalidMaxAutoAssociations; + case 347: + return Status.MaxNodesCreated; + case 348: + return Status.IpFqdnCannotBeSetForSameEndpoint; + case 349: + return Status.GossipEndpointCannotHaveFqdn; + case 350: + return Status.FqdnSizeTooLarge; + case 351: + return Status.InvalidEndpoint; + case 352: + return Status.GossipEndpointsExceededLimit; + case 353: + return Status.TokenReferenceRepeated; + case 354: + return Status.InvalidOwnerId; + case 355: + return Status.TokenReferenceListSizeLimitExceeded; + case 356: + return Status.ServiceEndpointsExceededLimit; + case 357: + return Status.InvalidIpv4Address; + case 358: + return Status.EmptyTokenReferenceList; + case 359: + return Status.UpdateNodeAccountNotAllowed; + case 360: + return Status.TokenHasNoMetadataOrSupplyKey; + case 361: + return Status.EmptyPendingAirdropIdList; + case 362: + return Status.PendingAirdropIdRepeated; + case 363: + return Status.MaxPendingAirdropIdExceeded; + case 364: + return Status.PendingNftAirdropAlreadyExists; + case 365: + return Status.AccountHasPendingAirdrops; default: throw new Error( `(BUG) Status.fromCode() does not handle code: ${code}`, @@ -2764,3 +2880,153 @@ Status.MissingTokenMetadata = new Status(335); * NFT serial numbers are missing in the TokenUpdateNftsTransactionBody */ Status.MissingSerialNumbers = new Status(336); + +/** + * Admin key is not set on token + */ +Status.TokenHasNoAdminKey = new Status(337); + +/** + * The node has been marked as deleted + */ +Status.NodeDeleted = new Status(338); + +/** + * A node is not found during update and delete node transaction + */ +Status.InvalidNodeId = new Status(339); + +/** + * gossip_endpoint has a fully qualified domain name instead of ip + */ +Status.InvalidGossipEndpoint = new Status(340); + +/** + * The node account_id is invalid + */ +Status.InvalidNodeAccountId = new Status(341); + +/** + * The node description is invalid + */ +Status.InvalidNodeDescription = new Status(342); + +/** + * service_endpoint is invalid + */ +Status.InvalidServiceEndpoint = new Status(343); + +/** + * gossip_ca_certificate is invalid + */ +Status.InvalidGossipCaCertificate = new Status(344); + +/** + * grpc_certificate_hash is invalid + */ +Status.InvalidGrpcCertificate = new Status(345); + +/** + * The maximum automatic associations value is not valid. + * The most common cause for this error is a value less than `-1`. + */ +Status.InvalidMaxAutoAssociations = new Status(346); + +/** + * The maximum number of nodes allowed in the address book have been created. + */ +Status.MaxNodesCreated = new Status(347); + +/** + * In ServiceEndpoint, domain_name and ipAddressV4 are mutually exclusive + */ +Status.IpFqdnCannotBeSetForSameEndpoint = new Status(348); + +/** + * Fully qualified domain name is not allowed in gossip_endpoint + */ +Status.GossipEndpointCannotHaveFqdn = new Status(349); + +/** + * In ServiceEndpoint, domain_name size too large + */ +Status.FqdnSizeTooLarge = new Status(350); + +/** + * ServiceEndpoint is invalid + */ +Status.InvalidEndpoint = new Status(351); + +/** + * The number of gossip endpoints exceeds the limit + */ +Status.GossipEndpointsExceededLimit = new Status(352); + +/** + * The transaction attempted to use duplicate `TokenReference`.
+ * This affects `TokenReject` attempting to reject same token reference more than once. + */ +Status.TokenReferenceRepeated = new Status(353); + +/** + * The account id specified as the owner in `TokenReject` is invalid or does not exist. + */ +Status.InvalidOwnerId = new Status(354); + +/** + * The transaction attempted to use more than the allowed number of `TokenReference`. + */ +Status.TokenReferenceListSizeLimitExceeded = new Status(355); + +/** + * The number of service endpoints exceeds the limit + */ +Status.ServiceEndpointsExceededLimit = new Status(356); + +/* + * The IPv4 address is invalid + */ +Status.InvalidIpv4Address = new Status(357); + +/** + * The transaction attempted to use empty `TokenReference` list. + */ +Status.EmptyTokenReferenceList = new Status(358); + +/* + * The node account is not allowed to be updated + */ +Status.UpdateNodeAccountNotAllowed = new Status(359); + +/* + * The token has no metadata or supply key + */ +Status.TokenHasNoMetadataOrSupplyKey = new Status(360); + +/** + * The transaction attempted to the use an empty List of `PendingAirdropId`. + */ +Status.EmptyPendingAirdropIdList = new Status(361); + +/** + * The transaction attempted to the same `PendingAirdropId` twice. + */ +Status.PendingAirdropIdRepeated = new Status(362); + +/** + * The transaction attempted to use more than the allowed number of `PendingAirdropId`. + */ +Status.MaxPendingAirdropIdExceeded = new Status(363); + +/* + * A pending airdrop already exists for the specified NFT. + */ +Status.PendingNftAirdropAlreadyExists = new Status(364); + +/* + * The identified account is sender for one or more pending airdrop(s) + * and cannot be deleted.
+ * Requester should cancel all pending airdrops before resending + * this transaction. + */ +Status.AccountHasPendingAirdrops = new Status(365); diff --git a/src/account/AccountBalance.js b/src/account/AccountBalance.js index 3a7ae31a2..d2e9d8a6c 100644 --- a/src/account/AccountBalance.js +++ b/src/account/AccountBalance.js @@ -48,24 +48,14 @@ export default class AccountBalance { */ constructor(props) { /** - * The account ID for which this balancermation applies. + * The Hbar balance of the account * * @readonly */ this.hbars = props.hbars; - /** - * @deprecated - Use the mirror node API https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#api-v1-accounts instead - * @readonly - */ - // eslint-disable-next-line deprecation/deprecation this.tokens = props.tokens; - /** - * @deprecated - Use the mirror node API https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#api-v1-accounts instead - * @readonly - */ - // eslint-disable-next-line deprecation/deprecation this.tokenDecimals = props.tokenDecimals; Object.freeze(this); diff --git a/src/account/TransferTransaction.js b/src/account/TransferTransaction.js index 008268408..7df47038f 100644 --- a/src/account/TransferTransaction.js +++ b/src/account/TransferTransaction.js @@ -368,6 +368,13 @@ export default class TransferTransaction extends Transaction { return map; } + /** + * @returns {Transfer[]} + */ + get hbarTransfersList() { + return this._hbarTransfers; + } + /** * @internal * @param {AccountId | string} accountId diff --git a/src/client/Client.js b/src/client/Client.js index e2d9b6105..3bceaf58c 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -29,6 +29,7 @@ import LedgerId from "../LedgerId.js"; import FileId from "../file/FileId.js"; import CACHE from "../Cache.js"; import Logger from "../logger/Logger.js"; // eslint-disable-line +import { convertToNumber } from "../util.js"; /** * @typedef {import("../channel/Channel.js").default} Channel @@ -147,7 +148,6 @@ export default class Client { this._isShutdown = false; if (props != null && props.scheduleNetworkUpdate !== false) { - this._initialNetworkUpdate(); this._scheduleNetworkUpdate(); } @@ -449,7 +449,9 @@ export default class Client { * @returns {Client} */ setDefaultMaxQueryPayment(defaultMaxQueryPayment) { - if (defaultMaxQueryPayment.toTinybars().toInt() < 0) { + const isMaxQueryPaymentNegative = + convertToNumber(defaultMaxQueryPayment.toTinybars()) < 0; + if (isMaxQueryPaymentNegative) { throw new Error("defaultMaxQueryPayment must be non-negative"); } this._defaultMaxQueryPayment = defaultMaxQueryPayment; @@ -764,30 +766,6 @@ export default class Client { }, this._networkUpdatePeriod); } - /** - * @private - */ - _initialNetworkUpdate() { - // This is the automatic network update promise that _eventually_ completes - // eslint-disable-next-line @typescript-eslint/no-floating-promises,@typescript-eslint/no-misused-promises - setTimeout(async () => { - try { - const addressBook = await CACHE.addressBookQueryConstructor() - .setFileId(FileId.ADDRESS_BOOK) - .execute(this); - this.setNetworkFromAddressBook(addressBook); - } catch (error) { - if (this._logger) { - this._logger.trace( - `failed to update client address book: ${ - /** @type {Error} */ (error).toString() - }`, - ); - } - } - }, 1000); - } - /** * @returns {boolean} */ diff --git a/src/client/addressbooks/mainnet.js b/src/client/addressbooks/mainnet.js index 7316ae9cf..a5acca1f6 100644 --- a/src/client/addressbooks/mainnet.js +++ b/src/client/addressbooks/mainnet.js @@ -1,2 +1,2 @@ export const addressBook = - "0a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039303938383635646566326632616233373663376630663733386331643837613237616330316166643030383632306333356362366562666362623063333330303331393361333838633334366433303233313732373031323139336262373666643330303462383634333132633638396566353231336362623930313130313530396465616239346632366137333265363337393239646134633463623332353137653361646262333831316435306163346337376331666365386236353136303632313566333437303766336537323635353435653538633839343630396532383337366264623737373566653330343339653065313539326664636230633365653163333035373733643037326136623839353765616663653161313162653936356564616666333834333336366362366134346563323561383930313036653632343735363766373662353530666461343832626165633633303764363938656338383834316664363666323366323130653437623861396463626136626134653166613731366462333363383065333038313934393664636235653536303966623665376336313533373962646465643432376539323331623932353463326261663934333630386138366436393861653961336338363339646638383764366636623561373133383564323433333864393131613231326266373166316532616363386231383662393665633865363963383662366430353832313737373661303963396336383935336564623539313635373862356132363362326634363965336230633037656164613731613434376565613766386663316262383037343235353536376237663062643165366166623033353837313863393862343239653234623232393835393666633736636636616633393663613934333464373932366563376433376434623932616635366434356665666638313936303935323234613931366331666665366236363765323535666333616338636363656639323064633034346232353030333133326238373830363734326630323033303130303031280032060800100018033a60333432343464353061386564346434636261646632353632306431616238386133323038313937376432663864373062613832626135326233333035613435363038323463663662303130366436333565643563333932373266666661626538420a0a0422ef520610a48803420a0a0423edc8b410a48803420a0a0423edc8b410a38803420a0a0422ef520610a388034a22486f7374656420666f72204c47207c2053656f756c2c20536f757468204b6f72656150da94a18185d0cedf010a9e0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039313331616133363866393334353232396639376236323539636363616666656132336530306364356561643032653366363936633165373134656533393339646164383630653338626639356132393734663965623438653933343366386161633430356561393535643035333233653131376233623163393438313361336166343266653830383263336434336261663162643464383336376539336462303061643639366536323761313033366165353334663031316561643565353666333761366666653434623662396530393934303131393261643536306130333436623431613831303039356635663264376664333264366565623635356261373538633662353236633132393338366166373139376337613533616536303364363232383332323534393631663136643065666138303739613736383536313838386265373333343932323137393536626263616661656262363133356335666262323438346435623461356664663033333661633032653236633136353263316264386561663330646165316436643365623030663762346661623864363437386665386439356562393131646639363661306465613465353232646237366238393636353730656363356166303935313634323466306166356638656536366533383664353635303731333939373136396163333735373362663532666430353864653935616232666636386536383131316162323334303565613936346232626238386430326330663163616564373165636464346534653430383539343837366664623835303062633535633762613032303636653035616239386439663765303436366439373032656235376565333732326638666363383561373535303566663332363231373032383862373838373233616462393765346465353632306363393065616431333832666364373537313838396665666231316536373731626333663666336665623139633761633534323837386430336139303237303532366333656564323439346566663534653135336361396636383930323033303130303031280132060800100018043a60303164313733373533383130633061616537393462613732643534343363323932653966663936326230313034363232306464393966353831363432323639366530353639633937376532663136396531653536383861666338663461613136420a0a04038234ec10a38803420a0a0423babff710a38803420a0a0423babff710a48803420a0a04038234ec10a488034a27486f7374656420627920537769726c6473207c204e6f727468204361726f6c696e612c205553415080cab7ab9da1f99d020adb0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062326363616336356164306663373634356138313762666162633438376164376534313331316537613331393862333766623834326438346333393562336636376436626438343866313063366630336332393065386637646161386430303161383434316463333532613139313630613331393365363862383265646631396165363736393361396133336434636238376537383961313037303731353531356561373732636161386238366135363962393163353435303833356439633335346630646163656339376665373730393162343562313437363938623766383630313432326463643232363165393238646534646163396334326463626166646639366330373233336261333032373037366633376339363965386564333062366235643866353033346265376439326335393666386265383631653531666363336132343262663964386265396532613965386530663135356562636666323365666661376364353763313035343238313164383037373663393538353532366664623065616133346565313935356435313131393339306665383733653463303464656464323931363538383462393862343633303837383861653766633464346161346138666339626332363734626133323134393362363234343535616434313063316465373162633935643164393166613066323031343138613739356533303965616632393762363939626632376339666132373633636435396365623032316531366238323030633130363066323831376664383363666337363731383334383934363165333539393239316233383064366539333962616134623139323332613661323732646465363531663830343666646333346462323736613737376436666232626563333235356232636332343462346166353636623130356633306336353036646461653065623364656464636639343762636239633630653030303938346633623461386336633465643462663930626331393332623766393464633361653662333630303038656239303230343066396230323033303130303031280232060800100018053a60653535633535393937356331633932383563353236326436633934323632323837653564353031633636613063373730663063396138386637323334653034333563353634336530333636346562396338636532643966393464653731376563420a0a04176fbafa10a38803420a0a044a32752310a48803420a0a046b9b406210a48803420a0a04031212fe10a48803420a0a046b9b406210a38803420a0a0423c0021910a38803420a0a044a32752310a38803420a0a0423c0021910a48803420a0a04176fbafa10a48803420a0a04031212fe10a388034a1c486f7374656420627920464953207c20466c6f726964612c2055534150bcd0f5c5f1dbdfad010aa00822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061336533376237366336636435663636323264363932343434346431326336373763333935663262353930326633626239386238613862353035356137303737303663613032386364373530363061326438373032643264386230343934376264636665306138633134316161323834346231653036653636313930303132653862363332366162306661333137393733626337636234643239343966323130386161303463346230633931626161353732386635623536323265633735616266353738613166376234316564653261363765626436396331386535383166646639633630323061633064653963613263333166306336343639303033333131666262356365376462343963373837653161376432376161343235656537623834646137653636393339663963383064306538326663653535653032646663386235633738343138613236616134333635303639383731396261666365636630626434393030306164646366613430353730386264626566626231393734396432326461623030376534346434356561323362313036663838333463313532653235303632643463663234666632353335366337656233373239313035333933666234396261623930346130326630663062623431376364393139643335323839303132386536626266663466616339663930646531313861393734663261366464303165303332613739623137386636306661316663626264303262353730346662343632393563313531393038313633373365646436363335633835363937386631623935303366316637336234623062653861626132656431666565616435393935336266383265666465393361333437316162643535636461336261386136373366626233373939373439666230303664303033663065363366363635633334363164326137623239646338623230346261353961363536363861343661653238373866303064316639343930646639653238306665626634333135656130346561613536386133613966643438633632633633623665636461363930323033303130303031280332060800100018063a60623837303764643839313632316231306663653032626436656132383737333435366630303862303662396461393835616532646131616436366265383233376366383331666335623862346665613534353935313739653937333564356432420a0a0423c7a16c10a38803420a0a0423c7a16c10a48803420a0a040d346cf310a38803420a0a040d346cf310a488034a29486f7374656420666f7220576970726f207c20416d7374657264616d2c204e65746865726c616e647350f0a6d59faa90869b010a9d0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063343536316533633237386364363530653830633431336361343434323363316333633133636631343735663666363937366435393761653433326234396162343230383662373962383431333236303534623862336463663537643866636437396266633035383138336361323463643463316362633537346564313131376532663562376233633633636537623036643962346566636637333735363337623431666536663533633831316239646536313433663361353239353763646639353637373531323062333337303366663537363231343037616239353735626332643335633064343466303938336663316566363361346666353230396630373063393261663130363239353630316339366263656430363465633139303139373031396336383131633463386464383063623466346163373166396164373665376163383934353666626634663031316639306162643264393035333665383233343635316636626566393237653364356438623762663435393035303938336265636133616265663261396439376166333435373732613737343065393639393237356230313865613064663238366164643663653932336566393038666265373632613735663231313136383632646234346433646361316434346234643265386463313036366335303036626235613764393534616432353564346236303332373334373565353131616562343835643036396130363763306162356332343533386339333363303662356136616566613934303035633239313532313365346363646165366339343266363237326639646435323832643662383930663166323065666432333939636436373439323466613537303436616336646133326537333935316137333131336539316663326237666632396534383531623833666633396638336261396563366630386365666462623663626262666661626664666161393164393330663732303064613438313337633339346362643133653730316563646332363136666432316261643638316161346630303130323033303130303031280432060800100018073a60623863336339613161363430336161353536633462393663383936343339323563393831643565383364323963616665643739303832653331306531656234663135623536396337396664626332343136306238393165633732316663613337420a0a0423cb52f010a38803420a0a0423cb52f010a48803420a0a040372360410a38803420a0a040372360410a488034a26486f7374656420666f72204e6f6d757261207c2056696c6e6975732c204c69746875616e696150f0a6d59faa90869b010a9b0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061316334303737313534333033636337326334666237363932633366393432353162646563313233396131663761383937326162653931613335333233666265636136323561376666616536343036633835356463326166323131303930306230646630653665366462373633363464666131666665383565646135363739333665323938356238353633346133326161353261363539396464366333306265316637613663356238663565656361663236323164386134353936383266636432646261616431353631643131663333666363623766353530306163353638643136356462656161636533323836643238393466363431323964373831643663373266643764353939633965316433616634616134333363323362393130666165346334383431363431663631353236616437383765626561353339383734313637653964336137336363306662313536343239643135656337363361366430663036313135613739623961663738336437376239386438333039366161343734336639373430386439653134626366346464666665343539313736383834376234306362386461376361333735323536643262393335643039356665323532666165383166663665333766383464376139306437653537306134663865663363376437363665656461343732663039323031393930313561383930383235396138373363353435346663626264636164326535323864653835343535623430383363376463346164633561393838653063646466646331353964356437313261626435343461613733656330323930383938313463393861343466323666633036343436353963313833653331383461613237326638643164633062666133653061353630343834636230353562613464626235636333333965633830626431316436343264633361373032653863373033616232313933303834643962643633663064666531326134333363323537366561663738316366616438363765663730626461363137363862326265663134663530633663336238623039366630323033303130303031280532060800100018083a60346134346462616664353064636539653139616536353935656663663237626164666537396462383866306532393163333262303363306666633936383330623339316331343331313436313132353566356137366534613333626130333139420a0a0423ec05db10a48803420a0a0423b7429610a38803420a0a0423ec05db10a38803420a0a0423b7429610a488034a24486f7374656420627920476f6f676c65207c2048656c73696e6b692c2046696e6c616e6450da94a18185d0cedf010aa10822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039336132313563633461376137323263616539633133616264363336646639396363656563366166396462343662363966613531363731366566353063653234393061393831653039616230313963613263623436383131623562363139643162643164356565366634366134326337373763626465653634326131343834656364663564646433373239363432633338633664343361383835383837343437356635383234343433363634633034646665643962383930343566623038356532356333656663623438343137333365666637633532396331333965363933353063326364373962326338643139363739613731326534653863616664333236373534316238333262336531306130313235356465663639646631653964336238643865616630333131646536376435653132623236646430316462626439643365343264333564396465323731333032653066316636396438376362633761636139653838363765396434323864336361623036363665623439306435666261623330626666336637383564303366323037326134336262396235653534363536613539326362363165616664356135656632383463376361656336366637663437333235636330643463316432376636363164386137343863613530373163303665663133346466663936663430383636383833363664343638613234373830303137653062353661626137666162343362336237633062373739303666616535343832663332383131633239326536623134343534653134623839343830316138366130336363343737393464643064373435323761373265343234656433616661303438393965636239613633663261396165373262653766613938396164663064363561333263383531643938303166633431303438646633333536346663376233313730376563386662383031343066653762376131666131323062613163623636303332346365666662346263633264396262376465306366353463383139663264643362636561646563396332356635653139646339623130323033303130303031280632060800100018093a60393238316639613166303537653964653636666163343433326362313362313032643930643563633565613738373438376637313430336362363261613830393937366636356363336235656461333861316333336137313962343662333033420a0a0423c5c0e110a38803420a0a0423b59efa10a38803420a0a0423b59efa10a48803420a0a0423c5c0e110a488034a2a486f7374656420666f72205a61696e2047726f7570207c205374726173626f7572672c204672616e636550f0a6d59faa90869b010ab90822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030633537656462396666323736653032336232383032316362316438376364663139363662363938636634386534656161613763363932303737636565653863636232333961346339323135393765386538396637636330356433663331333135373839373663346533313434303564346134653033613732343130633563303963613532376164356138356239393836333765373261333265316662633064353534366232343635653965383036633264643530396562303530616235666232373036336664393238313562316464323638396532313131636165623666353439653934613966303066303832316434636136633661363131376635613533336339323633626630373461333064356362656635306431633863323338376263613937326564656461303938336235643061366235376463623030323030303638323862343065343037366234383730623234626164383430353665653532623566343232653838343030323863323530303633383264386539633636313232356634663736656137326533343036303765396663366633633230343333303736613163613863623135656430336163383936366430353037626364653638316534653032333165653966383764313131653762343861633866393464326438343262353264663733663537336363353431343936343739376336323639363866666165373431386633623631303962356130663039653332323366346134643565333530396464323530313338663662633137626636636563653137353934343330646631383061333865393061646632616666626661643063366238633162383766313738613036316463666266663862393263393136366438373463316666356166346662636462666538653964303939333730646466363062653734373633336433366565346562356364353166366533633333396531353165343162646235613563653263386339376130613433623363643463633038313838346338373966396432663337343834323863383537336631376339306633636264303230333031303030312807320608001000180a3a60373462636363393430333338393661613435393435343538623838336239383134336633643236313032316264653834373637356335633762306430663639313664656664636438386632303739303836613231663664363335343730393165420a0a04b19a3eea10a38803420a0a04b19a3eea10a48803420a0a0403f81b3010a48803420a0a0423f2e99a10a38803420a0a0403f81b3010a38803420a0a0423f2e99a10a488034a2a486f7374656420666f72204d6167616c75207c204e61616c6477696a6b2c204e65746865726c616e64735096dcebc9c2e1b0d9010a990822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030396264643865383466616461613335333266633463653031613861313764346333623233326635306139373930653236323638346564633438323365383135613162643562323065636561376266353665323966366262376238333166623362663665666364313437356630623865643566666230623133383562393664313636623632396630333936613866656635663036653462636132356565346131333430656532363361346439626230323064386634373233303666336438383631333864653761303139653035396264306166633930326363626131613231336165326461613630633861303133373535666530613438653033346635623430323361326461646561613838633534383638333533616337613761336466313262326662363431383737346539623134626536656162386363323762383830313261643631363264613734653065656231363133353930356634333733373464616238353836643735306132366262643361633234616564383738633464353365363531303732633837316539346437616363353735633936373338313733346135336665616634643762613662636464323431636336343538633630383764383633303261613235316330346636643536623963333264376439363632343735306564303535373835643037373366343364633039396232386339323238313134386536633831663239376666396431363665303030616330346233313234313836373735666365663735663565626130633130333262663133306466366364376134363231316430646633653035383464393265613637333439643834393035303865623465663838663534633863336434383664653837313966313066613936666562383563633739363037366361373831333138656532643965643930336361313333363034306335396164393161346432663639386539313038616530656462396231636239356164333362313937666662313862643162613862353663626565326161653935383565636532303861316531346234383536343633303230333031303030312808320608001000180b3a60373033316631353431646264376236666537646137303234303236353832306433376637633532396139333334386635306437383432316231383739376537623135636465376430653566303537633838346238376430393365376433386635420a0a0423f0766010a38803420a0a040d3577b910a38803420a0a040d3577b910a48803420a0a0423f0766010a488034a22486f7374656420627920426f65696e67207c2057617368696e67746f6e2c2055534150f0a6d59faa90869b010a9e0822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030393032353966346533643966306633393432353635343865396337333038623130623733343033636339303934643937616431353162373730363137306239373732636562363464363632656365663930316138643764313564333139613539633862373130373161636364383935623763393336313064633639373666363763346531373239626138333733616237653532613366336338663236353439316464653639643665303939393437306537343435393831313331626439366333366536383635323033666232656264356435306561646166623732363339366465633164393137343839386234653962653034633734643330346665616464396362643332333463336237663333303663393963623063333339666332353936396234316435386132623763666331383332653232366438316331393633393933653232353561303837643136393863303364343231306264363435383036343464303935636137366161313739346564643430633163383762356638326138653339663630336539373131366261303435373865376538303334363439356437383564346566376366373731346239656236663566396530623961393466346237333838343631396239323734643461393565663135373534613839643937656635633161383862366436393365306138306562643533376663396366306361393164316336326439313564653765643831386239353265363463323030323933656538653238346134313661373261336531326663376434323362313538663962343936363063626332343636666265643066656432653234653130326664653934326562346366643934626563343664336439306663303863333966656362613033653063613234363461653636346239373935313562613239653166373032633366653730326265373933373936643865646231376161343863303932393062303234353439663036313166356165323365643765313634343264663764316461643232383663326262303964353532326464336564363938633266303230333031303030312809320608001000180c3a60323030636462653835346639383561613664366264313539613234613033346561626539306438333861383438306638666230653665393265623563353762653265636265353461333263373161653466393731653363333666326637306339420a0a0423b1a2b410a48803420a0a0423cc562010a38803420a0a0423b1a2b410a38803420a0a0423cc562010a488034a27486f7374656420627920444c41205069706572207c2048656c73696e6b692c2046696e6c616e6450f897c08eafedc1a7010aa30822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303038326465373330363566333466666332393334306435393439643232323062316534333636656435636637633665626436313663663934313661353365613030313766366262313136626664336633646566636331356237613464646630653434643032666536393536383830353365373961373730653230316263663731393333393030333965653866303836643466613734366337653035363931383330316639623565383465333932363238323830383561373962333232626361306235643835666539373232316132366262646532353863363230663064636561303261623165646431366363343961336632616239323838653364643166333764633462366136663731333366663932653534316337316237306432613266363664353537323561623138626638366430303965633364323466356431326530623565363830326431313531333732643462373634656265636234616638326636343934383565633537623561303164633637393538663561303363636161623763626139333534613137333732633133313662613437633935336161663934393031623366386332346536613361666436373538653766336231343363653264643363623037316232613734633932316365653934396134623561366265383739663163373930613662386436336231393264376565323961393439316664643638396139386330613763336436303332306631623461633264363232396466643934653432663361363034386137366265316562393538633861313837336265386433333861656339666335396162376633373632363738393430326331666435393566313930383735373565306265383237666334633061346662336433393361643734613934396363393836626662363463616264646165353339333566366463353630373464623933643737656133623831366264643662653533343439373237323238393835396666333463653531383630616666623632316431303438376463333834336631663836643534303334613633653438613161306430323033303130303031280a320608001000180d3a60303762373763653238346637656262356265623037623130353337356166353564323238613736356531613834353837656231643166316230363735633338613164313531323337306535366632316338656435656164656662333737396139420a0a0423ea846b10a38803420a0a0422d7c06810a38803420a0a0422d7c06810a48803420a0a0423ea846b10a488034a2c486f7374656420666f72205461746120436f6d6d756e69636174696f6e73207c204f7265676f6e2c2055534150f0a6d59faa90869b010a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039383735356134303862353332316532363330353230303064366437643461326333613535346435653133383461396362356562663437346165383832633633623438366264303864313434646466316139346365396137643632353139363330303661666461616334353838343666313736343031393566653235333961363536393330656661383534663231343865363865633161303863316334396432303063336633303435666537313437663036643533346334626432363231303063623164643339373339643736306438316130626432306638336632353564323530376434636362313130366235333631386336613934343039633838376361653236326434636565396338363233323134376365633134303465306335376262613733313731333065653339363433383838616633643539386564643832623863363165363561653831613465316135366263303664333937313433613938643431636138376433656634333365663061656162363830313139316233653338343830393638663636623665383836363261663435613965323132393934663638623238386562393637626562393834373863323433653231333663316131353931663036316635626330346232316666326261343862323966313834333130383838373362646665393966386135326539343038393731383536653830346465613630326133313137383663393835363532393633633361333737303332396234303966373466646663373436623232613566383431383931323037316334636538343663396234623332306665646636653962363465326362653338346639613832623661616164346232303930373433316466316133336636393230376135363536303062653831303730643038333239303039393538353961343439386435623539333135626365626566656538303765623061336139343266316364663333363764643434343466646232393838366566636464306265346162653961313838383033393533383735656461333364623732393839663736336230323033303130303031280b320608001000180e3a60353061666134343861336137386236313566343966656635373762643762363262313330383265623535326366383839353130396530653534333866373961633861656432663261343865326635373034393037343666346334333931303462420a0a0423ec021b10a38803420a0a0423ec021b10a48803420a0a043408158d10a38803420a0a043408158d10a488034a1a486f737465642062792049424d207c2054657861732c2055534150f0a6d59faa90869b010aa60822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061396462376638626161313236383938666162373839313135613362356438393734346631393765323830343161653039386633653838366336393837313732316531316262306164313166336365393132346161393631643661306463383435663439373635633366616231393935383430323637366635363434363262663238316462613535383837383066303365393035373938653138343236396161613630663761313437323333316532666231646561646438373763383463626362363431636139653563386164366534356263313539636230373966636230643434396364636438643932333963316130343765376234343864613063646361323636313061323566323936643936653734363962363736643461343434353136653761353965383532393361383038366638343063303532383534653032613863623230303264616433353832356265346438336235326661393165386337336666303439373436313438383632373837633131313866393234643331636261633162343466656666323264343336623339373965616466396234336134626661373265313562343735356663616232363065303661323739633362623733626337663136613036306434643532326664343930353830333838616135393564383034343733366535323266363432343931356637383033623735383365303935636466373863333235313936393764653831623839666235303035343735336231613137663961616662303634643834633939326639616231316363626338636231303831346463616635323634616134356632316264656661633832636361636161663335386533313337336565316261346537343032666438613730656130633238636135636337346463343235313063393639636432633435396231656333363838613031656133396139393237313063643232393763393861383462363334386135373738303466646332333464336665313930336532633231653137326461323862353961653665346337653865646438623731633439643730323033303130303031280c320608001000180f3a60633637313364383765336331663638353961336133363633656262316237653162643164613134666366303736636535316433363436346235363832613564663763666164643434303139373536346634393838343233313330393163326264420a0a040379ee1a10a48803420a0a0423e40b3510a38803420a0a0423e40b3510a48803420a0a040379ee1a10a388034a2f486f7374656420666f722044657574736368652054656c656b6f6d207c2048656c73696e6b692c2046696e6c616e64508089abfdb3cafdb3010a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061386365616333363765623166316465356630643965663365616630646639623938343438666532303830383437363536326130363063353163323839373730623463616366653932636236353536393832336539363263326132633966656435336264333663613361313232646531633532356135383266323561346437643632386331613364356264623839333661656365373531306537353534656537303333303235633039326338323865656235373338626530326564393633646138316135393230353633346365393435343537376162383266343066313366316565353565306165373237653233633330323834623166343462393961636534646463356639616337616438386439666132323535393335623234646362613834303036343265313663663235333263306230643638393239303436303837313563343037366634366438346130653066656433366537366363646339363335356537613236313630393435633262353461653236636330306664303832333236333436656565656137646437356639313931316539396462636239396561346163366261303536633333323238643838316438353833316439636338373935393364613137343664643065653935646332623936666539336261666366663263643764393239353864373864663333663230356437313135656439666163346462366634636336306535366135343431646135623562353566613539393939303265393538613662366334346438313064646335363138313234316238376632326630353961363838306538303231373336643031383937646236353434396365383137613233373564303335353163623064653530376336303961306338303330656366346266646562323133633033646161373634613138323162373234333334663731663736386437616563623237373035326137303333373635663037323138303536633738663261383761663138333836643866363161356366636233663262613464643539393135663133643338363334643136393537353730323033303130303031280d32060800100018103a60613533633865623730626464383965646236626535666563353063666162633038313030326134373761663437386565666133353565613665653537326534616535306538343938386638656132633036386166613738393637623263616630420a0a04129ddfe610a38803420a0a04225bb5b710a38803420a0a04225bb5b710a48803420a0a04129ddfe610a488034a1a486f737465642062792055434c207c204c6f6e646f6e2c20554b50f0a6d59faa90869b010aa40822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061663062393134323537626637613436353563346135306430636164356530613165343538316564363632336630653837333066373936623866323963353831373862636363363933326331666333316633396566343462383264336334336233393837333733373366656362313239353232386130346664353061313466333634366438346665316634363763616562393864343633653239373565393935623864326531653339663362663661646463323561653335643635643032363038653033343535333739363665326162636534396238313462656164336331623735373137346165333063303062306334336539396238303439366237326433633133316631633665346663646130356632383131376566396532386334333033626534643863376530343264353862383363633132313934356132633635653739363263616139313835393338663337353764663763636139356366303262356533313934346133613631396130616333663165333462396230313364346332323463346631653730666439666433363938336566383661646535313833363263633833323263306637623631613961633735666238326537623836643638626330663039396130396131346361633561316438643338663961386137306363333766663563633362626432373432666664313436323535633137316536613137383038333237316463653066646536383165643439326362353962303739366432373031373538333864633539303831303765336136656133663961343036623364313133306363656333623437393165343962626332333136303362343661623264306639336434336265373561623961346437313065613934306532383561376231353362306361376364646565366439646365306164383335306334316439306332313562393538383531356166613061633333363561653037653831663362626233366264626561633462333162636231616134653832353635623937376639646164383564363236656566396161613965663864376533666230323033303130303031280e32060800100018113a60623562323865366635373234303733306364613830326335366535336238383337613764363364616239623362336162313536613034643965326536653234623637393063333861356664333335626536376139353162373762656563373438420a0a042256d4f710a38803420a0a0412e8fb1310a48803420a0a042256d4f710a48803420a0a0412e8fb1310a388034a2d486f7374656420666f722041766572792044656e6e69736f6e207c2050656e6e73796c76616e69612c2055534150f0a6d59faa90869b010a940822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303038633037626533303561643630623930626132646162333962306565373736306531613232663835373532323534306437306230336233663965343837356133613239616230383038386631343466353765623235326534366261353933383564306536643432373031313764613061626331623362383036393463396135303538623836643631646661303665373136373039633838653866656163376333613065316432356663306165626636613866373666636239396638343566653138313436316361623638353862393763336134303237666233373132623134653663303738396465313764343137363435373765353131343137656231363236393265623037616531653733353532333565396262343339303437623663303136313337383265376464366636303464616134363734363631643533393631663436633366616136623765373637363264333733623562353432623739656139363365666266333361633638313938626232623636316366663637363931366566333732616434633236633231366334626334373837633834656333326431383464373763373531383663303963663364396639313433336361393835333131396261623331666136616432366634353365353936643962646563613638613537363962633866656537613533356438306338633666336566623164666232383861623661393739383534623763653833313234656330643130326166663934633362373466396333373839353863323565623933336464353363316538303561313836353464366439313836393930663635373034323966393630663334653862346637666439393732646362666539323430653037346461326433353561356637656639633161663632656635393832613831373435373862396331356334396563353636626461636233306363666365663039636466653730386164343837343234653963316265363533663965653736363065376439343263316566613564613238366531616464616230366139613333663964653934363739356230323033303130303031280f32060800100018123a60656239373639393565356432613964613639633434623036646633633239656363613565626135303038663035343037376330656538376230383831333331346334666439316563383334643362383638666237656537373934663463626562420a0a048d5eafbb10a38803420a0a048d5eafbb10a488034a35486f7374656420666f722044656e746f6e73207c2053696e6761706f72652c2052657075626c6963206f662053696e6761706f726550f0a6d59faa90869b010aac0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062653137633939363437636563363561343434303762353335383536623363336261656635623534663536356166353862383435366261386337636535333564356163373263363163343463373662336335376338653836343834313633376265313061383363666533396330393234373664306462653464366364636463643732306133306235626665623531613031613138663538326334356636633836393933666366376466313832393335646531643836393036303434646366333531383639333564396264376565613739353233353262656262346566396165306637363631653730613432333761666139383939363638376361343866636663356230306433383037663035346265306661386333626661343235303338626536656632393531363466323266373362376538386339346561396265386161346633613234356338396239643166643531393266376135306239353862326566383130346233366631626638666432636662323863313432313830306331633437653465663938616631353030373063633664363964313765386562393266313861366161316136353236366134393532333864313033663866363935623537656366333733363530613035323030383734353732316265613831353632373936376338303736333635646638633463376137643464643866326333383530633138666261373165623630653665386466626431393665303533376664373062333434656362636335333064666338336461366665646634396435316139303431393530326261396437306364333566316366336330363934653233353466393036346664626635333565623233633237633061343364306237386331663836376336316439383639356438646566376263326131306262363637346332326636366161623061393138313364646632376364623835326335396566373965316239653161303735666136656532376137653337373464626634623236343635343237653664356162393166653766306633613731373834656361313832623530323033303130303031281032060800100018133a60643563653666343337386436643534373233393438366566643661373032613734666361396539363537323366306561343365656661333262653938313739353038633432313835666132333735306432306538633066316361316435363361420a0a0412a8043b10a48803420a0a042259578a10a48803420a0a0412a8043b10a38803420a0a042259578a10a388034a35486f7374656420666f72205374616e646172642042616e6b207c204a6f68616e6e6573627572672c20536f7574682041667269636150f0a6d59faa90869b010aad0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061356164326237363433613034633035356432663863643235313162313531333966633435353735363231333838653439633131396232663339386163613131306636313339366230633836366465353036333532326262383534303237336531336636643934636531653630343338663661666230306161613634363132663731343565396263653862633161353362393431393133616137366339663361323833336661643763663238356337616332643337663939663363326364623439646534643135316536313637383536346632383166353431343234623431666137633531623261393630323238336337643332656530306562383338646131356333386166633936653036316439376365646532323136356666316161393539663163343237356232643039386334303538366135353739666262336362393030373237303431323061386136366135323730663466636664313038366339323336393061333565376664343435653333616330336631333963363836383535363537306364633461616632323130376136633161343432343536613763366337396565303430393065376535643466363662636136306361316634376236646662353433646163336362663139613737313961386635356236663833623461336238613636643630323536643061343635353166613730323462643035363331623861353538303837373235346332663266323638636463333364326462626366623733336539666265323333626239636235396162333161303134386232336538633432363830666631306166346337396134643038333436666237396139336439363239353438656166316262313234363938666165666134636464373234343263303361303462373333343332663734383930336133323563323833643435366162396165393231616537656433333931653564313738376566646332333534306137623835633639316165383730613037663930623131633133623332636534336561656431356233363936383563653439313737636339383530323033303130303031281132060800100018143a60623064373630623735333936626438303239326366373531646334333833326130313432366663373965323934306161396432383339353235666664613164623034626434376434653265643362343630383833373338303035303463653133420a0a043427a2d810a38803420a0a0422524eff10a48803420a0a0422524eff10a38803420a0a043427a2d810a488034a36486f73746564206279204175737472616c69616e205061796d656e747320506c7573207c205379646e65792c204175737472616c696150bcd0f5c5f1dbdfad010a940822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303038643435633231633063393565663635613032396435326339353766643066383566323031323364613033346536313637316464656535343735663037333832613636633636636234646335303530346464666433373538313038336466386431373537373330656438643666333634646634633336613236353135393139353564613230316132343037666138616239623233313338313132323561306461323330666265333830653039306161353665666134663230326563396234383233663635303164393661633639386562663236616163663365653264316633326137323163393437653130373663663335623337336461316438376133366131353265303065373130313137393232383265383235666631373163353833336238383537306266633664613834343965366639356638623132363561623535353139343033313535336431643537366639336334326330636136306161626163346338646431363264383131346632623231353131353833633732353339666535366334393961393239646533613430613064343563313763353839633264373938386365323665616663393261336433376237656130303432643433653033616661363237316232363235356136636363666165353337313832316438316530623035633235306235396630613930373431613065306538386130396564353663356239373830643039356630393036663062383164353132363339383261616530313133366330373264383434613131643664613462326136316336343465316162313766313666663438656532336665646538343532663165343265326433306130373930633235643432303630653164343461363731613265623233643131346636386337316533336631373664623538613638623433303035346263316432393833613233613332656136666639356661376334643865333830656232393665393862373936386563663834353464383137633733376565613564643932316562383663313663376232393330346134613765636265356133613130323033303130303031281232060800100018153a60633332623830343033666562653331633462613165623436636661393363626339313639663238353937613330636130313336356362336531373936373261373535613338343530636536616161626634306164396533363034386664386434420a0a04224c8c6d10a38803420a0a04224c8c6d10a48803420a0a040d247bd110a38803420a0a040d247bd110a488034a1d486f7374656420627920454446207c2050617269732c204672616e6365509ecdd6b8c7beece5010a9b0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062303561626532616230306664643036633935356538363731306230653036663161393236323461343861643163626338646663366632323132393632623063333066646264323834613337633561333736353862363363333665613831363235363161386534663934366362653537323263303238383031663066323831633730663864383863376330306132663265323966353937623739393836396564383335366466353763343762653939343461326161666636353066396234626261306462633533646338383066646262363965613435313930356432383032323032663865323963303461373664323761663265623763353438343835626633663436393463393063343138313038383838343337393238343838333566373831363730376433653864373666346536376635373830626366303838313363353565633633396139626436323431373866356562313437643530306166333531653965663162316533343234383463613236306462376363626165343836663133636632363562356231616236383830363630303830353362323063336465646365373731633961303861303332306161396365343531656239643938336137623439636161313039366638616463303938333138646333386530653763656630643865356435353761303637353638356131633965323536613262633964626133323262336262333137326366373134303737626333383066386130613433336138626661376662666335396636623039336563386266366539333937633039623138653138303430633162353636383634373337633866613765323937393566336134353838646461376332626162343935363635636334613962383336653265623930633632613366636166353931666235663831383034633736313830653632366661323634346137646533343531316436633436363764393839333765323737333366346431653931333838333335346535346664373335313732316537366637623536633334383333383866346136623837623238616562656230323033303130303031281332060800100018163a60373364383962353366376363316563363734616632386164353231666166303864376130313831363634363965666338343531353461633431313038613964383132396138383330303331613139613537353161326437396362633437353230420a0a0422408da610a48803420a0a04344eca2210a38803420a0a0422408da610a38803420a0a04344eca2210a488034a24486f7374656420666f72205368696e68616e2042616e6b207c204c6f6e646f6e2c20554b50b4df8ad7ecfea3a1010ab70822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039386564366232323133613364613839346238653833633435333235383930393764316639343536633730623966653264386333303864656633366461613837373036643433306632336361353362323764303334646566663563326530616333353161303732396234373630613139623135323562396432306131393631623936323235356334626232613563633035613035643762343736663665356230353437623461383833623530663764316339333734356261343033363636303831303664626630356237353565626335316431623832393164313037663563306439613234383365626365336430376338623762353835376436326234626535363337356366323331346637653030396534663139653863383038396563363966393664303236363139396366376561333336336231353761653935326232323833613864396637636562343537333862313532343836663534643430663632303062376561373535643333366531633333616435386666653033613863353635306162363262393362326236363435373639666530316634643231636661613463653939353130663737316465323262613961643165386335656364333430346363333138333137346337633834666264663130386234393733333836386537633966656362393062336262383562306333633133373830333266336237393865366662396637666433356664323566336332316537636661653831613031626261353062633466646638323232326131363836653932303061316233323362363138653434383939306532616566623330363139303438626533353939666663383838306564653362363765636338626264346466363433326635323162666233333762663262303935386532396536363232336165333564633039343036626530323132313332626532393538313639346137343032363034663163653638396334623537613562616662633164343662333432623531633331666632623536373563366331646636306431323864346136346336366662346631383330323033303130303031281432060800100018173a60393033333838666533346538626438636165323865656334633665386664643230333562316463623563396634356233326234623137643635386336383862383164323333303638393536346333373164343737663638623464326233393539420a0a0403125bb010a48803420a0a0423e8f49110a48803420a0a0445a7a9d010a48803420a0a0423e8f49110a38803420a0a0403125bb010a38803420a0a0445a7a9d010a388034a28486f7374656420627920436861696e6c696e6b204c616273207c204d6963686967616e2c2055534150f0a6d59faa90869b010a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039646364386330613533653930633335353935373466363632303431313764336235303365353061333664333039376661633834323965366365636433376262353430373138303866326565393832303335663835316130633962653231373633383361323265333863316162613136386633326639303537306362333233336366653632353938373636366166363762353134636165663231666238646636643066636433336366323630366239326464656135353336623630363864383637383265333962643563333834343539393164343139623764316563303835393934313263303934396431633234306233356331346463353532373464626137316666616539333631323561356638313966353431333265323433396434616335353937393936656365383565313364666633333631663931333166353663656163356239663535326234396366366639613961633665356463653264623336393436326639336166383065356235366236653862656661313632613036316234613736383932626463383436343733303663363030383538666464323730333237366332633730343430313938656664376665333534356366326162353830633734636664363434356161663762643766373435636332353265616264323635656162656538363234313731303465363934386135353735366664633232326466306131303135323464653163336330386363663034333031316563376665393634656464383435316131333031343763303733363361333566313166646565663866326132623736313735376234333538666638396237356134386436376264633630393036393365306262383637396563626239336666646233663365643936626563393365663436353665333731366162383763653436636138653132353963386665646465386632663165613066336562326334386539363535316465313233333033343537323566343565643639633835373562353136383361666134373236323138323664623232626232643163346631653336343634613930323033303130303031281532060800100018183a60343136663935626665653633666464343562666331303037636265633364353434666163366233303339666531643030333137616666346339633965363739313862323863343862646462323363346361313131333163643832363062336564420a0a04128707d310a48803420a0a042259672610a48803420a0a042259672610a38803420a0a04128707d310a388034a1a486f73746564206279204c5345207c204c6f6e646f6e2c20554b50adee9fe8e7a1e894010a9e0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061653761663963363038633162646232383966623831376666633663393537376564366434663664353766323734623231356631333930633765373965343930663964636237346366666530653837353764363766393066346232306464343435316237666636363331613662343563396534303362653561363632323439353535383364316161343233386261366639343662373163636138633136636337383838623266306433333363363335623865353437386661633362613366383134353761316131366230346532623232353236353162366334363838633535313064326564633231623033653061396232383330343162656566363738336330303839383435656363393665366432333563353636383564323438663833393166653061666366386565303366336234393836393663366439646563663266633939306332313965663664326362626132613639613236353238653630303936333264383265616433613538336566306333376332623739303638313138653033323062396663623331386635616534386330383737393535643038333162633935323161616138386239333431396665643966343632663930306663343236303130353665323463653434396564626463383439626237383263303961316133366164356534643462356437343636623737363339313366373934623237373162303761666236313734343466623662346237343834643634653139316235313366633865323530313034336637323534323163643537623037336265643231623030333134313835643638383766626332623534386439306262326133633931383461653934346333323664623866333761373335366161383832626164346337393437613830653136643066303265333832643537373166313938376331623736653838636465316364663264316139323231356563363864396232303465383062356463343637356666336161626632323366373738376562323431356466356533383963666636306663343063613235323030306234373638343130323033303130303031281632060800100018193a60383835326362386463646461623336396535373139636462616335653566353030393866383832313736343132616437313165326230336163366539666537303335383236666434376331326131323734326432633030643530373537353366420a0a040de8f0cf10a38803420a0a04225d700710a48803420a0a040de8f0cf10a48803420a0a04225d700710a388034a27486f7374656420666f7220494954204d6164726173207c204e6577204a65727365792c2055534150f897c08eafedc1a7010a940822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030623732383031303538636365666235323133396261396336383638613934393565333762613635373163356263306662376632623064303739303035646435636531326166373035646663356163653766653462303131653634393765316230363565356566336439333735353362316337633130353435616335633931333534656366616436613634373634363838383363633636613461356562306531633164316262663130333064393635306636376230373539303965353336393665613331616361643532343031313166373535333762313465366565663634353164376233333464313830346132613537383962356534616336626661323739353565353531373835313630316236376366643062643238363963376564316634636530393865333637663133336231393538373433633133633666363930313161386334373562323132366433626333353233383739383963383636666532313961313632393165343461393264353437316636666331363836326563376266653063616435613465656131633630383239383736666235613031326531646263353163306330383264383930623936333532363764643939623463383131313538323665383136653232653333363833346339333033633533336532303263666136613139313339323630316365656462613366396361346465363131373737356530666336313034333431653733353231313339643736633333363464653238616635386631343733613038343732633630393136613634316165393764646239626230373238333264666535623932323731666434376566383963393832386130633731373534313864316339656133323032653262613538383133306262663265333834656238343763396163326436383037613932653462613133336435373135663336323636363137663864613536633963386233393462633030323537356361323032306364613966393239376137643034356432353130323538313735353965326561353266353434323833323865633364623331323731303230333031303030312817320608001000181a3a60343365393462613234626662376665613934393138663338383162653632353239393035623831353631613764626337636434646363306165623032626561323062383936393830333434633365376533356133323865623163353236343430420a0a040de4670e10a38803420a0a04225796ae10a38803420a0a04225796ae10a48803420a0a040de4670e10a488034a1d486f7374656420666f7220444253207c2047656f726769612c2055534150f0a6d59faa90869b010a9e0822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030636137393562313636643833343362656437656531343663393535646432316238323865383330373862326136643331346236373736313066666437336562613239313138336136396235353865393363346530653663363861323533356231336161663631663961353665323930316637653162623939396665313738663463393335346634353666636435636338633938643235393032386166646432666231646636363632326433343361306435313331373637376665653165623734393136393333333461306636366238393636356265343061346165623337366163633061633032383363356635643330346231303330363936626664366330373330363366323166646665376239613263383339616266363337383238333033626632646561323864613031353932303435386639666439346561633632666431373465333765653038613433623739333062323232313461353636346465636138643334383239356530633339396161386238343630383231666236343464373730323432363765633934326564663264643063356461623237323634303433313333333838356634343265373363633139343134386637323863356137633962356132393636643365336232356639383336303066646130656133613462653937636637653866336230666236343866306233356661313364323732616537643433343261393239623539376234306431633534343433396361646332366462393962353435636165653630373431383838333133356539626631363066393737396635386430386435303266356562393531626631653130393137633830333961626466303961633336313465646534393838333966336461323736303031363834303534316665666338346163393363376566643931383532613434303136626664633638653338653530633361653363633534356333376438336130363734326161386165353331643562313662653332633662646630366463633233373330313338393266393265336165353336343164373663643261386464303230333031303030312818320608001000181b3a60396535316430316664323032663535303830653564646334633561393832323734376336366138353762306434303730666332306233663530386335613230323339383737613231396237363138323533343961663566333030313061383364420a0a04227dc86010a38803420a0a040d38046010a48803420a0a04227dc86010a48803420a0a040d38046010a388034a27486f7374656420666f7220536572766963654e6f77207c2057617368696e67746f6e2c2055534150f0a6d59faa90869b010a9a0822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030396636363165316236353236626432333132333932383035353564623134613930393430346364636339313838363432623664343763623266336539633931363633326532366134353034636666303862613730643134366462333137613038636330383230303431396231623936393435393834633139636633386436316362346165366631316136616135303765616263313261373239313733313336333739303831366466336136356662396635356232303231383432396336643234636431393330326231326264623635396164366262383137316564366636386439653936646465316161306166343563363963336265633036316335363135643831363030626432626437313064383133356135303065333765623734386664323936663763353638633236633064636265366166653861326663326263336137653533303532346338333531323864373661613534623431336566613033353236396664363838313439376435616233303061623465666461343535663432613830656633396336626334313933316634366238393132393930343062366430663364653839336636383235346265303834316161653466353131323039376530396462386666633830633835323363373965613831633663316164656364303431313561663964626263626333356130326462653839646238386566663666666638636436623166333335656634666137323030303866613062326135623134303232313933656239653263623766303337326661396237653838313433386566373436306665313738306563363533663261336365373839633066356362366637623237313631343861326531636233656463316336646161316531306134633062383637343162316266633863346432663335636262306230626331346366666264363737353132666331356230646639336637366232326333323433376332373531633833323963646464386135366563613735323030373265663364303161636566633137626233386161346434343262623536643362303664303230333031303030312819320608001000181c3a60623032633965383566343964613138313437666431333533373733626166306537316437326532326130303766316466393035316161386134373631616538376164346639306561346333343039383131613163343737376363326663363166420a0a04128b2f0510a38803420a0a0423c6dc4b10a38803420a0a04128b2f0510a48803420a0a0423c6dc4b10a488034a23486f7374656420666f7220556269736f6674207c205175656265632c2043616e61646150f0a6d59faa90869b010aab0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039623138393637633833383837376638356136343731636539663136346365663933396230313833306239656232326330326363366237326139303730323062336534633031346463373131656138653039356238386430623438353865633836623035613863336135396163313264326239666162636163323832636562363138646230306562353937313636313164363730366338316161333264396464646336613663376233393662613230326665646562333366323839613838373238346562666330376431363664303263326336656433326337333234633365633861653232313132383534653138616235656130376136313563356566383030346563363861633730646330333030336134376637656665313033656463653235376432386537393631663432386631636661326536636637316266343563353634623832636362646131346131383366333063326333643561376166626137613030343037396538373730326332343965393661376232666464353632666331363735396566653735616265366132336430643266393036613264663164346236346362323131376137333034343439633735333139613736323063323139613466666339383265383232623665316130376265316366393862653932363564303836646332373161613430363331306638613834366664333331323339666533303362643536313663383930383066623838363339623763306365623134303039333831383233653034333364623666393135366532626461313837336434616139613361363339363034626662643131613664643663653033623462306365656639353630316337643838613834303339376364626361336666323134666266353863396439646264373964333965613736376539616535663665616239666361303566633438303066353537363537633930633132633630653032313634383235643463333361663437333733373465613233356235303331336564306237356266383961366237393030316662613734636331333139653535313530323033303130303031281a320608001000181d3a60666666383030656134323830643632633963316666333333636634333031393465306638616632383262383133623435323131333238353333636637326639663134363434633036303461616366313262313635623561366230353961636333420a0a04228e478110a48803420a0a04228e478110a38803420a0a04505546c510a48803420a0a04364a3c7810a38803420a0a04364a3c7810a48803420a0a04505546c510a388034a1c486f7374656420627920616272646e207c204c6f6e646f6e2c20554b50f0a6d59faa90869b010a980822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061313365303030633633343962366238303565663538643036363132303131303762373534366135396432626434653064373633333363333834653361333361393436366338366665373866356462383062616663616263313737636637633961626536326163343838336539363336393735633665393538393738353465613163643366623830376237653630626539656231663238356339306661643230366265366661303163613134636666313161326264616539366630303030386261616234343361353338373463323539336337366230613362646462346132306164353762326635666534343430353965383539316636623533626266393233393634333666306137333934323364613833346537346333666366663332663464363937343334303838636365653737373331373236343064636234393432326666363638613334333131663730373838343937616662643064636536316439653362366434623833643533623331663237616664356337366365373763396566383062643764666139323861373466316131643233393437663266313439373832366661366638383034346565313731383538383032313334643965326461653735623765333937616563336138323666383930643730306138613233393666656639363733663737616530363333333439343130643937353837343765613866663238373866613733616331646561373962376365363830663138666239623830383135613735626531323431336435366261643733303131366530656136653930333863326432343765663065663935623164643033663632623866316131333636353538383937653433323461643036616139336663643935313662363430343266376265343038373861633738653761396334303434376665333838373866383231386135326665363431333262333939333665363630333039623034643037646337323266633439356263396263653831333864306536326338366563353363376639636339393763336436636438613931353230313839656230323033303130303031281b320608001000181e3a60393737333639316332393935353531393332653035326537646338303533326233316438343730656332646233373935366365383830333638313739363534333633303066303064393562613561373530633262373839316338623636613961420a0a0422c9b1d410a48803420a0a0423eaf99610a48803420a0a0422c9b1d410a38803420a0a0423eaf99610a388034a21486f7374656420666f722044656c6c207c204e6577204a65727365792c2055534150f0a6d59faa90869b010abc0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039386538633539623263633133386236366333386562383137306435663364353561616462633037633835666639346338653965626631326139343736616535343665656339343665356161633436373539326661653263626433633362306366656562363833366537616237633137373830333661376532653161363763323765326661373135353532663232623837663632303662333135303938613938303238343061383735343030306330353165393465343033336465333762383437613165316236643863356163616365383164633763343935396239373530383634326264376333323032346662316364616437343535366238386435373765376133326535633437303362343931353161373163666237303130613134383433663239636463383936636361656632656532613631363931306632326464363566323164393961633335313532356130643234366330363666346566623962643461363430333165663164326663336638363461633863386638313462623365633030373539363937313034663362386231663364306161646536313363373438663834323264623630663566616139366637656439653636646438306439623664316564666464636164643739316365663036306533393330383261363166656130623066393936646435346532383766373562646530316536666364643832363938636362393065336565326532333539366138373036626464303466353666633934666334313730363131323835333631313064643963353832356537326565396335326135376233333265646636656231666566316433343235666465646164343031626462386339653261303730363738376365373863303061373430303733393834326230336335376234383331656636363133353164363238326534613666326332343861376266303533643664393439363635613638323039313436356134643064663432643831663534343837346631373439373664613731373034376566363036636538333234306366353436666666623761363930323033303130303031281c320608001000181f3a60663733313362633038623964346263373934636231343034643166343832613733616335666530633032393362356337333634376465333834316230353931303636363234653939323231373264353432313262316239653661373930393038420a0a04d94c39a510a38803420a0a04034d5efe10a48803420a0a04226b4eb310a48803420a0a04d94c39a510a48803420a0a04226b4eb310a38803420a0a04034d5efe10a388034a2d486f7374656420627920434f46524120486f6c64696e6773207c204672616e6b667572742c204765726d616e7950f0a6d59faa90869b010a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063376630636364316436313465336236626139373536663766393438323030613134636235336639633333343862373431653364643064323332643135313362656231666236393531653338376264356266643935656433653961303166343930363631363330613061646437386131336131613232396533633630623238653830366639346465373266653435373634333132313566306664633161643937663830386336646133653961306164633431353937336563343862383935363634636434353261663230646161633432333232623037656433356136316362323730306364623639386131343865636662633635373935363039303433363661353534346639363563653530666539383234366664366466323537353938396564633631383565633339616432326239353233666231643261313136393430646136306164633064353564303033313565666665323130643162666235623535366230313561383335633132386434613333643566363933386363623733633165663266313230653662663030393737663765346562333137346134373434653232313561643438373337613039366264333965333266643939643533306365343261376261653535616265373362376430613338656639643062346139666432373532336236633864306461666537313631613264323864653832626362373464373531636236633238333431373638633065653036356134383136393633376239323466613463363763393164623434636166353735333838393034626464336465343062643364323935356261633135616361623937636163373335626134653764303436353433376434623732393464653430333234373431663763356634333334383962356330653538353263363535353038653135363539343530323666373363343039666162353865336163343662386637363636643534353630393534643538613465383765653562343233353463393135653332373361666662633161626461626534323565616539623739343630626437633462663330323033303130303031281d32060800100018203a60353636626538323639636636623133393964636138323038386436303735356430656463303561316231663438663366623664626233306338343235663335313462613563613062653363353436643131393930353836363138663366363635420a0a042256ba9710a48803420a0a04031451e610a38803420a0a042256ba9710a38803420a0a04031451e610a488034a22486f7374656420666f722048697461636869207c2044656c61776172652c205553415000"; + "0a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039303938383635646566326632616233373663376630663733386331643837613237616330316166643030383632306333356362366562666362623063333330303331393361333838633334366433303233313732373031323139336262373666643330303462383634333132633638396566353231336362623930313130313530396465616239346632366137333265363337393239646134633463623332353137653361646262333831316435306163346337376331666365386236353136303632313566333437303766336537323635353435653538633839343630396532383337366264623737373566653330343339653065313539326664636230633365653163333035373733643037326136623839353765616663653161313162653936356564616666333834333336366362366134346563323561383930313036653632343735363766373662353530666461343832626165633633303764363938656338383834316664363666323366323130653437623861396463626136626134653166613731366462333363383065333038313934393664636235653536303966623665376336313533373962646465643432376539323331623932353463326261663934333630386138366436393861653961336338363339646638383764366636623561373133383564323433333864393131613231326266373166316532616363386231383662393665633865363963383662366430353832313737373661303963396336383935336564623539313635373862356132363362326634363965336230633037656164613731613434376565613766386663316262383037343235353536376237663062643165366166623033353837313863393862343239653234623232393835393666633736636636616633393663613934333464373932366563376433376434623932616635366434356665666638313936303935323234613931366331666665366236363765323535666333616338636363656639323064633034346232353030333133326238373830363734326630323033303130303031280032060800100018033a60333432343464353061386564346434636261646632353632306431616238386133323038313937376432663864373062613832626135326233333035613435363038323463663662303130366436333565643563333932373266666661626538420a0a0423edc8b410a48803420a0a0422ef520610a38803420a0a0423edc8b410a38803420a0a0422ef520610a488034a22486f7374656420666f72204c47207c2053656f756c2c20536f757468204b6f72656150b6fbd796f3968ada010a940822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039313331616133363866393334353232396639376236323539636363616666656132336530306364356561643032653366363936633165373134656533393339646164383630653338626639356132393734663965623438653933343366386161633430356561393535643035333233653131376233623163393438313361336166343266653830383263336434336261663162643464383336376539336462303061643639366536323761313033366165353334663031316561643565353666333761366666653434623662396530393934303131393261643536306130333436623431613831303039356635663264376664333264366565623635356261373538633662353236633132393338366166373139376337613533616536303364363232383332323534393631663136643065666138303739613736383536313838386265373333343932323137393536626263616661656262363133356335666262323438346435623461356664663033333661633032653236633136353263316264386561663330646165316436643365623030663762346661623864363437386665386439356562393131646639363661306465613465353232646237366238393636353730656363356166303935313634323466306166356638656536366533383664353635303731333939373136396163333735373362663532666430353864653935616232666636386536383131316162323334303565613936346232626238386430326330663163616564373165636464346534653430383539343837366664623835303062633535633762613032303636653035616239386439663765303436366439373032656235376565333732326638666363383561373535303566663332363231373032383862373838373233616462393765346465353632306363393065616431333832666364373537313838396665666231316536373731626333663666336665623139633761633534323837386430336139303237303532366333656564323439346566663534653135336361396636383930323033303130303031280132060800100018043a60303164313733373533383130633061616537393462613732643534343363323932653966663936326230313034363232306464393966353831363432323639366530353639633937376532663136396531653536383861666338663461613136420a0a0423babff710a38803420a0a04038234ec10a48803420a0a0423babff710a48803420a0a04038234ec10a388034a1d486f7374656420627920537769726c6473207c20496f77612c205553415080c09e91c191c795020adb0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062326363616336356164306663373634356138313762666162633438376164376534313331316537613331393862333766623834326438346333393562336636376436626438343866313063366630336332393065386637646161386430303161383434316463333532613139313630613331393365363862383265646631396165363736393361396133336434636238376537383961313037303731353531356561373732636161386238366135363962393163353435303833356439633335346630646163656339376665373730393162343562313437363938623766383630313432326463643232363165393238646534646163396334326463626166646639366330373233336261333032373037366633376339363965386564333062366235643866353033346265376439326335393666386265383631653531666363336132343262663964386265396532613965386530663135356562636666323365666661376364353763313035343238313164383037373663393538353532366664623065616133346565313935356435313131393339306665383733653463303464656464323931363538383462393862343633303837383861653766633464346161346138666339626332363734626133323134393362363234343535616434313063316465373162633935643164393166613066323031343138613739356533303965616632393762363939626632376339666132373633636435396365623032316531366238323030633130363066323831376664383363666337363731383334383934363165333539393239316233383064366539333962616134623139323332613661323732646465363531663830343666646333346462323736613737376436666232626563333235356232636332343462346166353636623130356633306336353036646461653065623364656464636639343762636239633630653030303938346633623461386336633465643462663930626331393332623766393464633361653662333630303038656239303230343066396230323033303130303031280232060800100018053a60653535633535393937356331633932383563353236326436633934323632323837653564353031633636613063373730663063396138386637323334653034333563353634336530333636346562396338636532643966393464653731376563420a0a04176fbafa10a38803420a0a04176fbafa10a48803420a0a0423c0021910a48803420a0a04031212fe10a38803420a0a0423c0021910a38803420a0a046b9b406210a38803420a0a044a32752310a38803420a0a046b9b406210a48803420a0a04031212fe10a48803420a0a044a32752310a488034a1c486f7374656420627920464953207c20466c6f726964612c2055534150a4a2fcd0daaae18c010a980822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061336533376237366336636435663636323264363932343434346431326336373763333935663262353930326633626239386238613862353035356137303737303663613032386364373530363061326438373032643264386230343934376264636665306138633134316161323834346231653036653636313930303132653862363332366162306661333137393733626337636234643239343966323130386161303463346230633931626161353732386635623536323265633735616266353738613166376234316564653261363765626436396331386535383166646639633630323061633064653963613263333166306336343639303033333131666262356365376462343963373837653161376432376161343235656537623834646137653636393339663963383064306538326663653535653032646663386235633738343138613236616134333635303639383731396261666365636630626434393030306164646366613430353730386264626566626231393734396432326461623030376534346434356561323362313036663838333463313532653235303632643463663234666632353335366337656233373239313035333933666234396261623930346130326630663062623431376364393139643335323839303132386536626266663466616339663930646531313861393734663261366464303165303332613739623137386636306661316663626264303262353730346662343632393563313531393038313633373365646436363335633835363937386631623935303366316637336234623062653861626132656431666565616435393935336266383265666465393361333437316162643535636461336261386136373366626233373939373439666230303664303033663065363366363635633334363164326137623239646338623230346261353961363536363861343661653238373866303064316639343930646639653238306665626634333135656130346561613536386133613966643438633632633633623665636461363930323033303130303031280332060800100018063a60623837303764643839313632316231306663653032626436656132383737333435366630303862303662396461393835616532646131616436366265383233376366383331666335623862346665613534353935313739653937333564356432420a0a0423c7a16c10a48803420a0a040d346cf310a38803420a0a040d346cf310a48803420a0a0423c7a16c10a388034a29486f7374656420666f7220576970726f207c20416d7374657264616d2c204e65746865726c616e647350000a9d0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063343536316533633237386364363530653830633431336361343434323363316333633133636631343735663666363937366435393761653433326234396162343230383662373962383431333236303534623862336463663537643866636437396266633035383138336361323463643463316362633537346564313131376532663562376233633633636537623036643962346566636637333735363337623431666536663533633831316239646536313433663361353239353763646639353637373531323062333337303366663537363231343037616239353735626332643335633064343466303938336663316566363361346666353230396630373063393261663130363239353630316339366263656430363465633139303139373031396336383131633463386464383063623466346163373166396164373665376163383934353666626634663031316639306162643264393035333665383233343635316636626566393237653364356438623762663435393035303938336265636133616265663261396439376166333435373732613737343065393639393237356230313865613064663238366164643663653932336566393038666265373632613735663231313136383632646234346433646361316434346234643265386463313036366335303036626235613764393534616432353564346236303332373334373565353131616562343835643036396130363763306162356332343533386339333363303662356136616566613934303035633239313532313365346363646165366339343266363237326639646435323832643662383930663166323065666432333939636436373439323466613537303436616336646133326537333935316137333131336539316663326237666632396534383531623833666633396638336261396563366630386365666462623663626262666661626664666161393164393330663732303064613438313337633339346362643133653730316563646332363136666432316261643638316161346630303130323033303130303031280432060800100018073a60623863336339613161363430336161353536633462393663383936343339323563393831643565383364323963616665643739303832653331306531656234663135623536396337396664626332343136306238393165633732316663613337420a0a040372360410a38803420a0a0423cb52f010a38803420a0a040372360410a48803420a0a0423cb52f010a488034a26486f7374656420666f72204e6f6d757261207c2056696c6e6975732c204c69746875616e696150a4a2fcd0daaae18c010a9b0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061316334303737313534333033636337326334666237363932633366393432353162646563313233396131663761383937326162653931613335333233666265636136323561376666616536343036633835356463326166323131303930306230646630653665366462373633363464666131666665383565646135363739333665323938356238353633346133326161353261363539396464366333306265316637613663356238663565656361663236323164386134353936383266636432646261616431353631643131663333666363623766353530306163353638643136356462656161636533323836643238393466363431323964373831643663373266643764353939633965316433616634616134333363323362393130666165346334383431363431663631353236616437383765626561353339383734313637653964336137336363306662313536343239643135656337363361366430663036313135613739623961663738336437376239386438333039366161343734336639373430386439653134626366346464666665343539313736383834376234306362386461376361333735323536643262393335643039356665323532666165383166663665333766383464376139306437653537306134663865663363376437363665656461343732663039323031393930313561383930383235396138373363353435346663626264636164326535323864653835343535623430383363376463346164633561393838653063646466646331353964356437313261626435343461613733656330323930383938313463393861343466323666633036343436353963313833653331383461613237326638643164633062666133653061353630343834636230353562613464626235636333333965633830626431316436343264633361373032653863373033616232313933303834643962643633663064666531326134333363323537366561663738316366616438363765663730626461363137363862326265663134663530633663336238623039366630323033303130303031280532060800100018083a60346134346462616664353064636539653139616536353935656663663237626164666537396462383866306532393163333262303363306666633936383330623339316331343331313436313132353566356137366534613333626130333139420a0a0423b7429610a38803420a0a0423ec05db10a48803420a0a0423ec05db10a38803420a0a0423b7429610a488034a24486f7374656420627920476f6f676c65207c2048656c73696e6b692c2046696e6c616e6450c9f4d0fd84f190d4010aa10822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039336132313563633461376137323263616539633133616264363336646639396363656563366166396462343662363966613531363731366566353063653234393061393831653039616230313963613263623436383131623562363139643162643164356565366634366134326337373763626465653634326131343834656364663564646433373239363432633338633664343361383835383837343437356635383234343433363634633034646665643962383930343566623038356532356333656663623438343137333365666637633532396331333965363933353063326364373962326338643139363739613731326534653863616664333236373534316238333262336531306130313235356465663639646631653964336238643865616630333131646536376435653132623236646430316462626439643365343264333564396465323731333032653066316636396438376362633761636139653838363765396434323864336361623036363665623439306435666261623330626666336637383564303366323037326134336262396235653534363536613539326362363165616664356135656632383463376361656336366637663437333235636330643463316432376636363164386137343863613530373163303665663133346466663936663430383636383833363664343638613234373830303137653062353661626137666162343362336237633062373739303666616535343832663332383131633239326536623134343534653134623839343830316138366130336363343737393464643064373435323761373265343234656433616661303438393965636239613633663261396165373262653766613938396164663064363561333263383531643938303166633431303438646633333536346663376233313730376563386662383031343066653762376131666131323062613163623636303332346365666662346263633264396262376465306366353463383139663264643362636561646563396332356635653139646339623130323033303130303031280632060800100018093a60393238316639613166303537653964653636666163343433326362313362313032643930643563633565613738373438376637313430336362363261613830393937366636356363336235656461333861316333336137313962343662333033420a0a0423b59efa10a38803420a0a0423c5c0e110a38803420a0a0423b59efa10a48803420a0a0423c5c0e110a488034a2a486f73746564206279205a61696e2047726f7570207c204b757761697420436974792c204b757761697450a4a2fcd0daaae18c010ab90822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030633537656462396666323736653032336232383032316362316438376364663139363662363938636634386534656161613763363932303737636565653863636232333961346339323135393765386538396637636330356433663331333135373839373663346533313434303564346134653033613732343130633563303963613532376164356138356239393836333765373261333265316662633064353534366232343635653965383036633264643530396562303530616235666232373036336664393238313562316464323638396532313131636165623666353439653934613966303066303832316434636136633661363131376635613533336339323633626630373461333064356362656635306431633863323338376263613937326564656461303938336235643061366235376463623030323030303638323862343065343037366234383730623234626164383430353665653532623566343232653838343030323863323530303633383264386539633636313232356634663736656137326533343036303765396663366633633230343333303736613163613863623135656430336163383936366430353037626364653638316534653032333165653966383764313131653762343861633866393464326438343262353264663733663537336363353431343936343739376336323639363866666165373431386633623631303962356130663039653332323366346134643565333530396464323530313338663662633137626636636563653137353934343330646631383061333865393061646632616666626661643063366238633162383766313738613036316463666266663862393263393136366438373463316666356166346662636462666538653964303939333730646466363062653734373633336433366565346562356364353166366533633333396531353165343162646235613563653263386339376130613433623363643463633038313838346338373966396432663337343834323863383537336631376339306633636264303230333031303030312807320608001000180a3a60373462636363393430333338393661613435393435343538623838336239383134336633643236313032316264653834373637356335633762306430663639313664656664636438386632303739303836613231663664363335343730393165420a0a04b19a3eea10a38803420a0a0403f81b3010a38803420a0a0423f2e99a10a48803420a0a0403f81b3010a48803420a0a0423f2e99a10a38803420a0a04b19a3eea10a488034a2a486f7374656420666f72204d6167616c75207c204e61616c6477696a6b2c204e65746865726c616e647350c9f4d0fd84f190d4010a990822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030396264643865383466616461613335333266633463653031613861313764346333623233326635306139373930653236323638346564633438323365383135613162643562323065636561376266353665323966366262376238333166623362663665666364313437356630623865643566666230623133383562393664313636623632396630333936613866656635663036653462636132356565346131333430656532363361346439626230323064386634373233303666336438383631333864653761303139653035396264306166633930326363626131613231336165326461613630633861303133373535666530613438653033346635623430323361326461646561613838633534383638333533616337613761336466313262326662363431383737346539623134626536656162386363323762383830313261643631363264613734653065656231363133353930356634333733373464616238353836643735306132366262643361633234616564383738633464353365363531303732633837316539346437616363353735633936373338313733346135336665616634643762613662636464323431636336343538633630383764383633303261613235316330346636643536623963333264376439363632343735306564303535373835643037373366343364633039396232386339323238313134386536633831663239376666396431363665303030616330346233313234313836373735666365663735663565626130633130333262663133306466366364376134363231316430646633653035383464393265613637333439643834393035303865623465663838663534633863336434383664653837313966313066613936666562383563633739363037366361373831333138656532643965643930336361313333363034306335396164393161346432663639386539313038616530656462396231636239356164333362313937666662313862643162613862353663626565326161653935383565636532303861316531346234383536343633303230333031303030312808320608001000180b3a60373033316631353431646264376236666537646137303234303236353832306433376637633532396139333334386635306437383432316231383739376537623135636465376430653566303537633838346238376430393365376433386635420a0a0423f0766010a48803420a0a040d3577b910a38803420a0a0423f0766010a38803420a0a040d3577b910a488034a22486f7374656420627920426f65696e67207c2057617368696e67746f6e2c2055534150a4a2fcd0daaae18c010a9e0822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030393032353966346533643966306633393432353635343865396337333038623130623733343033636339303934643937616431353162373730363137306239373732636562363464363632656365663930316138643764313564333139613539633862373130373161636364383935623763393336313064633639373666363763346531373239626138333733616237653532613366336338663236353439316464653639643665303939393437306537343435393831313331626439366333366536383635323033666232656264356435306561646166623732363339366465633164393137343839386234653962653034633734643330346665616464396362643332333463336237663333303663393963623063333339666332353936396234316435386132623763666331383332653232366438316331393633393933653232353561303837643136393863303364343231306264363435383036343464303935636137366161313739346564643430633163383762356638326138653339663630336539373131366261303435373865376538303334363439356437383564346566376366373731346239656236663566396530623961393466346237333838343631396239323734643461393565663135373534613839643937656635633161383862366436393365306138306562643533376663396366306361393164316336326439313564653765643831386239353265363463323030323933656538653238346134313661373261336531326663376434323362313538663962343936363063626332343636666265643066656432653234653130326664653934326562346366643934626563343664336439306663303863333966656362613033653063613234363461653636346239373935313562613239653166373032633366653730326265373933373936643865646231376161343863303932393062303234353439663036313166356165323365643765313634343264663764316461643232383663326262303964353532326464336564363938633266303230333031303030312809320608001000180c3a60323030636462653835346639383561613664366264313539613234613033346561626539306438333861383438306638666230653665393265623563353762653265636265353461333263373161653466393731653363333666326637306339420a0a0423b1a2b410a48803420a0a0423cc562010a48803420a0a0423b1a2b410a38803420a0a0423cc562010a388034a27486f7374656420627920444c41205069706572207c2048656c73696e6b692c2046696e6c616e6450dbbd98b593c2c6a4010aa30822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303038326465373330363566333466666332393334306435393439643232323062316534333636656435636637633665626436313663663934313661353365613030313766366262313136626664336633646566636331356237613464646630653434643032666536393536383830353365373961373730653230316263663731393333393030333965653866303836643466613734366337653035363931383330316639623565383465333932363238323830383561373962333232626361306235643835666539373232316132366262646532353863363230663064636561303261623165646431366363343961336632616239323838653364643166333764633462366136663731333366663932653534316337316237306432613266363664353537323561623138626638366430303965633364323466356431326530623565363830326431313531333732643462373634656265636234616638326636343934383565633537623561303164633637393538663561303363636161623763626139333534613137333732633133313662613437633935336161663934393031623366386332346536613361666436373538653766336231343363653264643363623037316232613734633932316365653934396134623561366265383739663163373930613662386436336231393264376565323961393439316664643638396139386330613763336436303332306631623461633264363232396466643934653432663361363034386137366265316562393538633861313837336265386433333861656339666335396162376633373632363738393430326331666435393566313930383735373565306265383237666334633061346662336433393361643734613934396363393836626662363463616264646165353339333566366463353630373464623933643737656133623831366264643662653533343439373237323238393835396666333463653531383630616666623632316431303438376463333834336631663836643534303334613633653438613161306430323033303130303031280a320608001000180d3a60303762373763653238346637656262356265623037623130353337356166353564323238613736356531613834353837656231643166316230363735633338613164313531323337306535366632316338656435656164656662333737396139420a0a0423ea846b10a38803420a0a0423ea846b10a48803420a0a0422d7c06810a48803420a0a0422d7c06810a388034a2c486f7374656420666f72205461746120436f6d6d756e69636174696f6e73207c204f7265676f6e2c2055534150b6cba6e7ef8db9b0010a900822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039383735356134303862353332316532363330353230303064366437643461326333613535346435653133383461396362356562663437346165383832633633623438366264303864313434646466316139346365396137643632353139363330303661666461616334353838343666313736343031393566653235333961363536393330656661383534663231343865363865633161303863316334396432303063336633303435666537313437663036643533346334626432363231303063623164643339373339643736306438316130626432306638336632353564323530376434636362313130366235333631386336613934343039633838376361653236326434636565396338363233323134376365633134303465306335376262613733313731333065653339363433383838616633643539386564643832623863363165363561653831613465316135366263303664333937313433613938643431636138376433656634333365663061656162363830313139316233653338343830393638663636623665383836363261663435613965323132393934663638623238386562393637626562393834373863323433653231333663316131353931663036316635626330346232316666326261343862323966313834333130383838373362646665393966386135326539343038393731383536653830346465613630326133313137383663393835363532393633633361333737303332396234303966373466646663373436623232613566383431383931323037316334636538343663396234623332306665646636653962363465326362653338346639613832623661616164346232303930373433316466316133336636393230376135363536303062653831303730643038333239303039393538353961343439386435623539333135626365626566656538303765623061336139343266316364663333363764643434343466646232393838366566636464306265346162653961313838383033393533383735656461333364623732393839663736336230323033303130303031280b320608001000180e3a60353061666134343861336137386236313566343966656635373762643762363262313330383265623535326366383839353130396530653534333866373961633861656432663261343865326635373034393037343666346334333931303462420a0a0423ec021b10a38803420a0a043408158d10a48803420a0a043408158d10a38803420a0a0423ec021b10a488034a1a486f737465642062792049424d207c2054657861732c2055534150ed86e0eca193fc740aa60822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061396462376638626161313236383938666162373839313135613362356438393734346631393765323830343161653039386633653838366336393837313732316531316262306164313166336365393132346161393631643661306463383435663439373635633366616231393935383430323637366635363434363262663238316462613535383837383066303365393035373938653138343236396161613630663761313437323333316532666231646561646438373763383463626362363431636139653563386164366534356263313539636230373966636230643434396364636438643932333963316130343765376234343864613063646361323636313061323566323936643936653734363962363736643461343434353136653761353965383532393361383038366638343063303532383534653032613863623230303264616433353832356265346438336235326661393165386337336666303439373436313438383632373837633131313866393234643331636261633162343466656666323264343336623339373965616466396234336134626661373265313562343735356663616232363065303661323739633362623733626337663136613036306434643532326664343930353830333838616135393564383034343733366535323266363432343931356637383033623735383365303935636466373863333235313936393764653831623839666235303035343735336231613137663961616662303634643834633939326639616231316363626338636231303831346463616635323634616134356632316264656661633832636361636161663335386533313337336565316261346537343032666438613730656130633238636135636337346463343235313063393639636432633435396231656333363838613031656133396139393237313063643232393763393861383462363334386135373738303466646332333464336665313930336532633231653137326461323862353961653665346337653865646438623731633439643730323033303130303031280c320608001000180f3a60633637313364383765336331663638353961336133363633656262316237653162643164613134666366303736636535316433363436346235363832613564663763666164643434303139373536346634393838343233313330393163326264420a0a0423e40b3510a38803420a0a040379ee1a10a38803420a0a0423e40b3510a48803420a0a040379ee1a10a488034a2f486f7374656420666f722044657574736368652054656c656b6f6d207c2048656c73696e6b692c2046696e6c616e6450c994ee9efedeee80010a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061386365616333363765623166316465356630643965663365616630646639623938343438666532303830383437363536326130363063353163323839373730623463616366653932636236353536393832336539363263326132633966656435336264333663613361313232646531633532356135383266323561346437643632386331613364356264623839333661656365373531306537353534656537303333303235633039326338323865656235373338626530326564393633646138316135393230353633346365393435343537376162383266343066313366316565353565306165373237653233633330323834623166343462393961636534646463356639616337616438386439666132323535393335623234646362613834303036343265313663663235333263306230643638393239303436303837313563343037366634366438346130653066656433366537366363646339363335356537613236313630393435633262353461653236636330306664303832333236333436656565656137646437356639313931316539396462636239396561346163366261303536633333323238643838316438353833316439636338373935393364613137343664643065653935646332623936666539336261666366663263643764393239353864373864663333663230356437313135656439666163346462366634636336306535366135343431646135623562353566613539393939303265393538613662366334346438313064646335363138313234316238376632326630353961363838306538303231373336643031383937646236353434396365383137613233373564303335353163623064653530376336303961306338303330656366346266646562323133633033646161373634613138323162373234333334663731663736386437616563623237373035326137303333373635663037323138303536633738663261383761663138333836643866363161356366636233663262613464643539393135663133643338363334643136393537353730323033303130303031280d32060800100018103a60613533633865623730626464383965646236626535666563353063666162633038313030326134373761663437386565666133353565613665653537326534616535306538343938386638656132633036386166613738393637623263616630420a0a04129ddfe610a38803420a0a04225bb5b710a38803420a0a04129ddfe610a48803420a0a04225bb5b710a488034a1a486f737465642062792055434c207c204c6f6e646f6e2c20554b50a4a2fcd0daaae18c010aa40822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061663062393134323537626637613436353563346135306430636164356530613165343538316564363632336630653837333066373936623866323963353831373862636363363933326331666333316633396566343462383264336334336233393837333733373366656362313239353232386130346664353061313466333634366438346665316634363763616562393864343633653239373565393935623864326531653339663362663661646463323561653335643635643032363038653033343535333739363665326162636534396238313462656164336331623735373137346165333063303062306334336539396238303439366237326433633133316631633665346663646130356632383131376566396532386334333033626534643863376530343264353862383363633132313934356132633635653739363263616139313835393338663337353764663763636139356366303262356533313934346133613631396130616333663165333462396230313364346332323463346631653730666439666433363938336566383661646535313833363263633833323263306637623631613961633735666238326537623836643638626330663039396130396131346361633561316438643338663961386137306363333766663563633362626432373432666664313436323535633137316536613137383038333237316463653066646536383165643439326362353962303739366432373031373538333864633539303831303765336136656133663961343036623364313133306363656333623437393165343962626332333136303362343661623264306639336434336265373561623961346437313065613934306532383561376231353362306361376364646565366439646365306164383335306334316439306332313562393538383531356166613061633333363561653037653831663362626233366264626561633462333162636231616134653832353635623937376639646164383564363236656566396161613965663864376533666230323033303130303031280e32060800100018113a60623562323865366635373234303733306364613830326335366535336238383337613764363364616239623362336162313536613034643965326536653234623637393063333861356664333335626536376139353162373762656563373438420a0a0412e8fb1310a38803420a0a042256d4f710a38803420a0a042256d4f710a48803420a0a0412e8fb1310a488034a2d486f7374656420666f722041766572792044656e6e69736f6e207c2050656e6e73796c76616e69612c2055534150a4a2fcd0daaae18c010ac40822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303038633037626533303561643630623930626132646162333962306565373736306531613232663835373532323534306437306230336233663965343837356133613239616230383038386631343466353765623235326534366261353933383564306536643432373031313764613061626331623362383036393463396135303538623836643631646661303665373136373039633838653866656163376333613065316432356663306165626636613866373666636239396638343566653138313436316361623638353862393763336134303237666233373132623134653663303738396465313764343137363435373765353131343137656231363236393265623037616531653733353532333565396262343339303437623663303136313337383265376464366636303464616134363734363631643533393631663436633366616136623765373637363264333733623562353432623739656139363365666266333361633638313938626232623636316366663637363931366566333732616434633236633231366334626334373837633834656333326431383464373763373531383663303963663364396639313433336361393835333131396261623331666136616432366634353365353936643962646563613638613537363962633866656537613533356438306338633666336566623164666232383861623661393739383534623763653833313234656330643130326166663934633362373466396333373839353863323565623933336464353363316538303561313836353464366439313836393930663635373034323966393630663334653862346637666439393732646362666539323430653037346461326433353561356637656639633161663632656635393832613831373435373862396331356334396563353636626461636233306363666365663039636466653730386164343837343234653963316265363533663965653736363065376439343263316566613564613238366531616464616230366139613333663964653934363739356230323033303130303031280f32060800100018123a60656239373639393565356432613964613639633434623036646633633239656363613565626135303038663035343037376330656538376230383831333331346334666439316563383334643362383638666237656537373934663463626562420a0a0412888fb010a48803420a0a048d5eafbb10a38803420a0a04225756f210a38803420a0a048d5eafbb10a48803420a0a0412888fb010a38803420a0a04225756f210a488034a35486f7374656420666f722044656e746f6e73207c2053696e6761706f72652c2052657075626c6963206f662053696e6761706f72655080b08a83b7f6d398010aac0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062653137633939363437636563363561343434303762353335383536623363336261656635623534663536356166353862383435366261386337636535333564356163373263363163343463373662336335376338653836343834313633376265313061383363666533396330393234373664306462653464366364636463643732306133306235626665623531613031613138663538326334356636633836393933666366376466313832393335646531643836393036303434646366333531383639333564396264376565613739353233353262656262346566396165306637363631653730613432333761666139383939363638376361343866636663356230306433383037663035346265306661386333626661343235303338626536656632393531363466323266373362376538386339346561396265386161346633613234356338396239643166643531393266376135306239353862326566383130346233366631626638666432636662323863313432313830306331633437653465663938616631353030373063633664363964313765386562393266313861366161316136353236366134393532333864313033663866363935623537656366333733363530613035323030383734353732316265613831353632373936376338303736333635646638633463376137643464643866326333383530633138666261373165623630653665386466626431393665303533376664373062333434656362636335333064666338336461366665646634396435316139303431393530326261396437306364333566316366336330363934653233353466393036346664626635333565623233633237633061343364306237386331663836376336316439383639356438646566376263326131306262363637346332326636366161623061393138313364646632376364623835326335396566373965316239653161303735666136656532376137653337373464626634623236343635343237653664356162393166653766306633613731373834656361313832623530323033303130303031281032060800100018133a60643563653666343337386436643534373233393438366566643661373032613734666361396539363537323366306561343365656661333262653938313739353038633432313835666132333735306432306538633066316361316435363361420a0a0412a8043b10a38803420a0a042259578a10a48803420a0a0412a8043b10a48803420a0a042259578a10a388034a35486f7374656420666f72205374616e646172642042616e6b207c204a6f68616e6e6573627572672c20536f7574682041667269636150a4a2fcd0daaae18c010aad0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061356164326237363433613034633035356432663863643235313162313531333966633435353735363231333838653439633131396232663339386163613131306636313339366230633836366465353036333532326262383534303237336531336636643934636531653630343338663661666230306161613634363132663731343565396263653862633161353362393431393133616137366339663361323833336661643763663238356337616332643337663939663363326364623439646534643135316536313637383536346632383166353431343234623431666137633531623261393630323238336337643332656530306562383338646131356333386166633936653036316439376365646532323136356666316161393539663163343237356232643039386334303538366135353739666262336362393030373237303431323061386136366135323730663466636664313038366339323336393061333565376664343435653333616330336631333963363836383535363537306364633461616632323130376136633161343432343536613763366337396565303430393065376535643466363662636136306361316634376236646662353433646163336362663139613737313961386635356236663833623461336238613636643630323536643061343635353166613730323462643035363331623861353538303837373235346332663266323638636463333364326462626366623733336539666265323333626239636235396162333161303134386232336538633432363830666631306166346337396134643038333436666237396139336439363239353438656166316262313234363938666165666134636464373234343263303361303462373333343332663734383930336133323563323833643435366162396165393231616537656433333931653564313738376566646332333534306137623835633639316165383730613037663930623131633133623332636534336561656431356233363936383563653439313737636339383530323033303130303031281132060800100018143a60623064373630623735333936626438303239326366373531646334333833326130313432366663373965323934306161396432383339353235666664613164623034626434376434653265643362343630383833373338303035303463653133420a0a043427a2d810a48803420a0a0422524eff10a38803420a0a0422524eff10a48803420a0a043427a2d810a388034a36486f73746564206279204175737472616c69616e205061796d656e747320506c7573207c205379646e65792c204175737472616c696150dbbd98b593c2c6a4010a940822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303038643435633231633063393565663635613032396435326339353766643066383566323031323364613033346536313637316464656535343735663037333832613636633636636234646335303530346464666433373538313038336466386431373537373330656438643666333634646634633336613236353135393139353564613230316132343037666138616239623233313338313132323561306461323330666265333830653039306161353665666134663230326563396234383233663635303164393661633639386562663236616163663365653264316633326137323163393437653130373663663335623337336461316438376133366131353265303065373130313137393232383265383235666631373163353833336238383537306266633664613834343965366639356638623132363561623535353139343033313535336431643537366639336334326330636136306161626163346338646431363264383131346632623231353131353833633732353339666535366334393961393239646533613430613064343563313763353839633264373938386365323665616663393261336433376237656130303432643433653033616661363237316232363235356136636363666165353337313832316438316530623035633235306235396630613930373431613065306538386130396564353663356239373830643039356630393036663062383164353132363339383261616530313133366330373264383434613131643664613462326136316336343465316162313766313666663438656532336665646538343532663165343265326433306130373930633235643432303630653164343461363731613265623233643131346636386337316533336631373664623538613638623433303035346263316432393833613233613332656136666639356661376334643865333830656232393665393862373936386563663834353464383137633733376565613564643932316562383663313663376232393330346134613765636265356133613130323033303130303031281232060800100018153a60633332623830343033666562653331633462613165623436636661393363626339313639663238353937613330636130313336356362336531373936373261373535613338343530636536616161626634306164396533363034386664386434420a0a04224c8c6d10a48803420a0a040d247bd110a48803420a0a040d247bd110a38803420a0a04224c8c6d10a388034a1d486f7374656420627920454446207c2050617269732c204672616e636550c9f4d0fd84f190d4010a9b0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062303561626532616230306664643036633935356538363731306230653036663161393236323461343861643163626338646663366632323132393632623063333066646264323834613337633561333736353862363363333665613831363235363161386534663934366362653537323263303238383031663066323831633730663864383863376330306132663265323966353937623739393836396564383335366466353763343762653939343461326161666636353066396234626261306462633533646338383066646262363965613435313930356432383032323032663865323963303461373664323761663265623763353438343835626633663436393463393063343138313038383838343337393238343838333566373831363730376433653864373666346536376635373830626366303838313363353565633633396139626436323431373866356562313437643530306166333531653965663162316533343234383463613236306462376363626165343836663133636632363562356231616236383830363630303830353362323063336465646365373731633961303861303332306161396365343531656239643938336137623439636161313039366638616463303938333138646333386530653763656630643865356435353761303637353638356131633965323536613262633964626133323262336262333137326366373134303737626333383066386130613433336138626661376662666335396636623039336563386266366539333937633039623138653138303430633162353636383634373337633866613765323937393566336134353838646461376332626162343935363635636334613962383336653265623930633632613366636166353931666235663831383034633736313830653632366661323634346137646533343531316436633436363764393839333765323737333366346431653931333838333335346535346664373335313732316537366637623536633334383333383866346136623837623238616562656230323033303130303031281332060800100018163a60373364383962353366376363316563363734616632386164353231666166303864376130313831363634363965666338343531353461633431313038613964383132396138383330303331613139613537353161326437396362633437353230420a0a04344eca2210a38803420a0a0422408da610a48803420a0a0422408da610a38803420a0a04344eca2210a488034a24486f7374656420666f72205368696e68616e2042616e6b207c204c6f6e646f6e2c20554b50edb6919ca59ccd9e010ab70822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039386564366232323133613364613839346238653833633435333235383930393764316639343536633730623966653264386333303864656633366461613837373036643433306632336361353362323764303334646566663563326530616333353161303732396234373630613139623135323562396432306131393631623936323235356334626232613563633035613035643762343736663665356230353437623461383833623530663764316339333734356261343033363636303831303664626630356237353565626335316431623832393164313037663563306439613234383365626365336430376338623762353835376436326234626535363337356366323331346637653030396534663139653863383038396563363966393664303236363139396366376561333336336231353761653935326232323833613864396637636562343537333862313532343836663534643430663632303062376561373535643333366531633333616435386666653033613863353635306162363262393362326236363435373639666530316634643231636661613463653939353130663737316465323262613961643165386335656364333430346363333138333137346337633834666264663130386234393733333836386537633966656362393062336262383562306333633133373830333266336237393865366662396637666433356664323566336332316537636661653831613031626261353062633466646638323232326131363836653932303061316233323362363138653434383939306532616566623330363139303438626533353939666663383838306564653362363765636338626264346466363433326635323162666233333762663262303935386532396536363232336165333564633039343036626530323132313332626532393538313639346137343032363034663163653638396334623537613562616662633164343662333432623531633331666632623536373563366331646636306431323864346136346336366662346631383330323033303130303031281432060800100018173a60393033333838666533346538626438636165323865656334633665386664643230333562316463623563396634356233326234623137643635386336383862383164323333303638393536346333373164343737663638623464326233393539420a0a0423e8f49110a48803420a0a0445a7a9d010a48803420a0a0445a7a9d010a38803420a0a0403125bb010a48803420a0a0403125bb010a38803420a0a0423e8f49110a388034a28486f7374656420627920436861696e6c696e6b204c616273207c204d6963686967616e2c2055534150a4a2fcd0daaae18c010a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039646364386330613533653930633335353935373466363632303431313764336235303365353061333664333039376661633834323965366365636433376262353430373138303866326565393832303335663835316130633962653231373633383361323265333863316162613136386633326639303537306362333233336366653632353938373636366166363762353134636165663231666238646636643066636433336366323630366239326464656135353336623630363864383637383265333962643563333834343539393164343139623764316563303835393934313263303934396431633234306233356331346463353532373464626137316666616539333631323561356638313966353431333265323433396434616335353937393936656365383565313364666633333631663931333166353663656163356239663535326234396366366639613961633665356463653264623336393436326639336166383065356235366236653862656661313632613036316234613736383932626463383436343733303663363030383538666464323730333237366332633730343430313938656664376665333534356366326162353830633734636664363434356161663762643766373435636332353265616264323635656162656538363234313731303465363934386135353735366664633232326466306131303135323464653163336330386363663034333031316563376665393634656464383435316131333031343763303733363361333566313166646565663866326132623736313735376234333538666638396237356134386436376264633630393036393365306262383637396563626239336666646233663365643936626563393365663436353665333731366162383763653436636138653132353963386665646465386632663165613066336562326334386539363535316465313233333033343537323566343565643639633835373562353136383361666134373236323138323664623232626232643163346631653336343634613930323033303130303031281532060800100018183a60343136663935626665653633666464343562666331303037636265633364353434666163366233303339666531643030333137616666346339633965363739313862323863343862646462323363346361313131333163643832363062336564420a0a04128707d310a48803420a0a042259672610a38803420a0a04128707d310a38803420a0a042259672610a488034a1a486f73746564206279204c5345207c204c6f6e646f6e2c20554b5092a983eac8d0da92010a9e0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061653761663963363038633162646232383966623831376666633663393537376564366434663664353766323734623231356631333930633765373965343930663964636237346366666530653837353764363766393066346232306464343435316237666636363331613662343563396534303362653561363632323439353535383364316161343233386261366639343662373163636138633136636337383838623266306433333363363335623865353437386661633362613366383134353761316131366230346532623232353236353162366334363838633535313064326564633231623033653061396232383330343162656566363738336330303839383435656363393665366432333563353636383564323438663833393166653061666366386565303366336234393836393663366439646563663266633939306332313965663664326362626132613639613236353238653630303936333264383265616433613538336566306333376332623739303638313138653033323062396663623331386635616534386330383737393535643038333162633935323161616138386239333431396665643966343632663930306663343236303130353665323463653434396564626463383439626237383263303961316133366164356534643462356437343636623737363339313366373934623237373162303761666236313734343466623662346237343834643634653139316235313366633865323530313034336637323534323163643537623037336265643231623030333134313835643638383766626332623534386439306262326133633931383461653934346333323664623866333761373335366161383832626164346337393437613830653136643066303265333832643537373166313938376331623736653838636465316364663264316139323231356563363864396232303465383062356463343637356666336161626632323366373738376562323431356466356533383963666636306663343063613235323030306234373638343130323033303130303031281632060800100018193a60383835326362386463646461623336396535373139636462616335653566353030393866383832313736343132616437313165326230336163366539666537303335383236666434376331326131323734326432633030643530373537353366420a0a04225d700710a38803420a0a040de8f0cf10a48803420a0a040de8f0cf10a38803420a0a04225d700710a488034a27486f7374656420666f7220494954204d6164726173207c204e6577204a65727365792c205553415080b08a83b7f6d398010a880822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030623732383031303538636365666235323133396261396336383638613934393565333762613635373163356263306662376632623064303739303035646435636531326166373035646663356163653766653462303131653634393765316230363565356566336439333735353362316337633130353435616335633931333534656366616436613634373634363838383363633636613461356562306531633164316262663130333064393635306636376230373539303965353336393665613331616361643532343031313166373535333762313465366565663634353164376233333464313830346132613537383962356534616336626661323739353565353531373835313630316236376366643062643238363963376564316634636530393865333637663133336231393538373433633133633666363930313161386334373562323132366433626333353233383739383963383636666532313961313632393165343461393264353437316636666331363836326563376266653063616435613465656131633630383239383736666235613031326531646263353163306330383264383930623936333532363764643939623463383131313538323665383136653232653333363833346339333033633533336532303263666136613139313339323630316365656462613366396361346465363131373737356530666336313034333431653733353231313339643736633333363464653238616635386631343733613038343732633630393136613634316165393764646239626230373238333264666535623932323731666434376566383963393832386130633731373534313864316339656133323032653262613538383133306262663265333834656238343763396163326436383037613932653462613133336435373135663336323636363137663864613536633963386233393462633030323537356361323032306364613966393239376137643034356432353130323538313735353965326561353266353434323833323865633364623331323731303230333031303030312817320608001000181a3a60343365393462613234626662376665613934393138663338383162653632353239393035623831353631613764626337636434646363306165623032626561323062383936393830333434633365376533356133323865623163353236343430420a0a040de4670e10a48803420a0a04225796ae10a38803420a0a040de4670e10a38803420a0a04225796ae10a488034a19486f7374656420666f72207c2047656f726769612c2055534150000a9e0822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030636137393562313636643833343362656437656531343663393535646432316238323865383330373862326136643331346236373736313066666437336562613239313138336136396235353865393363346530653663363861323533356231336161663631663961353665323930316637653162623939396665313738663463393335346634353666636435636338633938643235393032386166646432666231646636363632326433343361306435313331373637376665653165623734393136393333333461306636366238393636356265343061346165623337366163633061633032383363356635643330346231303330363936626664366330373330363366323166646665376239613263383339616266363337383238333033626632646561323864613031353932303435386639666439346561633632666431373465333765653038613433623739333062323232313461353636346465636138643334383239356530633339396161386238343630383231666236343464373730323432363765633934326564663264643063356461623237323634303433313333333838356634343265373363633139343134386637323863356137633962356132393636643365336232356639383336303066646130656133613462653937636637653866336230666236343866306233356661313364323732616537643433343261393239623539376234306431633534343433396361646332366462393962353435636165653630373431383838333133356539626631363066393737396635386430386435303266356562393531626631653130393137633830333961626466303961633336313465646534393838333966336461323736303031363834303534316665666338346163393363376566643931383532613434303136626664633638653338653530633361653363633534356333376438336130363734326161386165353331643562313662653332633662646630366463633233373330313338393266393265336165353336343164373663643261386464303230333031303030312818320608001000181b3a60396535316430316664323032663535303830653564646334633561393832323734376336366138353762306434303730666332306233663530386335613230323339383737613231396237363138323533343961663566333030313061383364420a0a04227dc86010a38803420a0a040d38046010a38803420a0a040d38046010a48803420a0a04227dc86010a488034a27486f7374656420666f7220536572766963654e6f77207c2057617368696e67746f6e2c2055534150a4a2fcd0daaae18c010a9a0822cc06333038323031613233303064303630393261383634383836663730643031303130313035303030333832303138663030333038323031386130323832303138313030396636363165316236353236626432333132333932383035353564623134613930393430346364636339313838363432623664343763623266336539633931363633326532366134353034636666303862613730643134366462333137613038636330383230303431396231623936393435393834633139636633386436316362346165366631316136616135303765616263313261373239313733313336333739303831366466336136356662396635356232303231383432396336643234636431393330326231326264623635396164366262383137316564366636386439653936646465316161306166343563363963336265633036316335363135643831363030626432626437313064383133356135303065333765623734386664323936663763353638633236633064636265366166653861326663326263336137653533303532346338333531323864373661613534623431336566613033353236396664363838313439376435616233303061623465666461343535663432613830656633396336626334313933316634366238393132393930343062366430663364653839336636383235346265303834316161653466353131323039376530396462386666633830633835323363373965613831633663316164656364303431313561663964626263626333356130326462653839646238386566663666666638636436623166333335656634666137323030303866613062326135623134303232313933656239653263623766303337326661396237653838313433386566373436306665313738306563363533663261336365373839633066356362366637623237313631343861326531636233656463316336646161316531306134633062383637343162316266633863346432663335636262306230626331346366666264363737353132666331356230646639336637366232326333323433376332373531633833323963646464386135366563613735323030373265663364303161636566633137626233386161346434343262623536643362303664303230333031303030312819320608001000181c3a60623032633965383566343964613138313437666431333533373733626166306537316437326532326130303766316466393035316161386134373631616538376164346639306561346333343039383131613163343737376363326663363166420a0a04128b2f0510a38803420a0a0423c6dc4b10a48803420a0a0423c6dc4b10a38803420a0a04128b2f0510a488034a23486f7374656420666f7220556269736f6674207c205175656265632c2043616e61646150a4a2fcd0daaae18c010aab0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039623138393637633833383837376638356136343731636539663136346365663933396230313833306239656232326330326363366237326139303730323062336534633031346463373131656138653039356238386430623438353865633836623035613863336135396163313264326239666162636163323832636562363138646230306562353937313636313164363730366338316161333264396464646336613663376233393662613230326665646562333366323839613838373238346562666330376431363664303263326336656433326337333234633365633861653232313132383534653138616235656130376136313563356566383030346563363861633730646330333030336134376637656665313033656463653235376432386537393631663432386631636661326536636637316266343563353634623832636362646131346131383366333063326333643561376166626137613030343037396538373730326332343965393661376232666464353632666331363735396566653735616265366132336430643266393036613264663164346236346362323131376137333034343439633735333139613736323063323139613466666339383265383232623665316130376265316366393862653932363564303836646332373161613430363331306638613834366664333331323339666533303362643536313663383930383066623838363339623763306365623134303039333831383233653034333364623666393135366532626461313837336434616139613361363339363034626662643131613664643663653033623462306365656639353630316337643838613834303339376364626361336666323134666266353863396439646264373964333965613736376539616535663665616239666361303566633438303066353537363537633930633132633630653032313634383235643463333361663437333733373465613233356235303331336564306237356266383961366237393030316662613734636331333139653535313530323033303130303031281a320608001000181d3a60666666383030656134323830643632633963316666333333636634333031393465306638616632383262383133623435323131333238353333636637326639663134363434633036303461616366313262313635623561366230353961636333420a0a04228e478110a48803420a0a04364a3c7810a48803420a0a04505546c510a38803420a0a04228e478110a38803420a0a04364a3c7810a38803420a0a04505546c510a488034a1c486f7374656420627920616272646e207c204c6f6e646f6e2c20554b50a4a2fcd0daaae18c010a9b0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303061313365303030633633343962366238303565663538643036363132303131303762373534366135396432626434653064373633333363333834653361333361393436366338366665373866356462383062616663616263313737636637633961626536326163343838336539363336393735633665393538393738353465613163643366623830376237653630626539656231663238356339306661643230366265366661303163613134636666313161326264616539366630303030386261616234343361353338373463323539336337366230613362646462346132306164353762326635666534343430353965383539316636623533626266393233393634333666306137333934323364613833346537346333666366663332663464363937343334303838636365653737373331373236343064636234393432326666363638613334333131663730373838343937616662643064636536316439653362366434623833643533623331663237616664356337366365373763396566383062643764666139323861373466316131643233393437663266313439373832366661366638383034346565313731383538383032313334643965326461653735623765333937616563336138323666383930643730306138613233393666656639363733663737616530363333333439343130643937353837343765613866663238373866613733616331646561373962376365363830663138666239623830383135613735626531323431336435366261643733303131366530656136653930333863326432343765663065663935623164643033663632623866316131333636353538383937653433323461643036616139336663643935313662363430343266376265343038373861633738653761396334303434376665333838373866383231386135326665363431333262333939333665363630333039623034643037646337323266633439356263396263653831333864306536326338366563353363376639636339393763336436636438613931353230313839656230323033303130303031281b320608001000181e3a60393737333639316332393935353531393332653035326537646338303533326233316438343730656332646233373935366365383830333638313739363534333633303066303064393562613561373530633262373839316338623636613961420a0a0423eaf99610a48803420a0a0422c9b1d410a48803420a0a0422c9b1d410a38803420a0a0423eaf99610a388034a24486f7374656420666f722044656c6c207c204672616e6b667572742c204765726d616e7950a4a2fcd0daaae18c010abc0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039386538633539623263633133386236366333386562383137306435663364353561616462633037633835666639346338653965626631326139343736616535343665656339343665356161633436373539326661653263626433633362306366656562363833366537616237633137373830333661376532653161363763323765326661373135353532663232623837663632303662333135303938613938303238343061383735343030306330353165393465343033336465333762383437613165316236643863356163616365383164633763343935396239373530383634326264376333323032346662316364616437343535366238386435373765376133326535633437303362343931353161373163666237303130613134383433663239636463383936636361656632656532613631363931306632326464363566323164393961633335313532356130643234366330363666346566623962643461363430333165663164326663336638363461633863386638313462623365633030373539363937313034663362386231663364306161646536313363373438663834323264623630663566616139366637656439653636646438306439623664316564666464636164643739316365663036306533393330383261363166656130623066393936646435346532383766373562646530316536666364643832363938636362393065336565326532333539366138373036626464303466353666633934666334313730363131323835333631313064643963353832356537326565396335326135376233333265646636656231666566316433343235666465646164343031626462386339653261303730363738376365373863303061373430303733393834326230336335376234383331656636363133353164363238326534613666326332343861376266303533643664393439363635613638323039313436356134643064663432643831663534343837346631373439373664613731373034376566363036636538333234306366353436666666623761363930323033303130303031281c320608001000181f3a60663733313362633038623964346263373934636231343034643166343832613733616335666530633032393362356337333634376465333834316230353931303636363234653939323231373264353432313262316239653661373930393038420a0a04d94c39a510a48803420a0a04034d5efe10a38803420a0a04226b4eb310a48803420a0a04d94c39a510a38803420a0a04226b4eb310a38803420a0a04034d5efe10a488034a2d486f7374656420627920434f46524120486f6c64696e6773207c204672616e6b667572742c204765726d616e7950a4a2fcd0daaae18c010a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063376630636364316436313465336236626139373536663766393438323030613134636235336639633333343862373431653364643064323332643135313362656231666236393531653338376264356266643935656433653961303166343930363631363330613061646437386131336131613232396533633630623238653830366639346465373266653435373634333132313566306664633161643937663830386336646133653961306164633431353937336563343862383935363634636434353261663230646161633432333232623037656433356136316362323730306364623639386131343865636662633635373935363039303433363661353534346639363563653530666539383234366664366466323537353938396564633631383565633339616432326239353233666231643261313136393430646136306164633064353564303033313565666665323130643162666235623535366230313561383335633132386434613333643566363933386363623733633165663266313230653662663030393737663765346562333137346134373434653232313561643438373337613039366264333965333266643939643533306365343261376261653535616265373362376430613338656639643062346139666432373532336236633864306461666537313631613264323864653832626362373464373531636236633238333431373638633065653036356134383136393633376239323466613463363763393164623434636166353735333838393034626464336465343062643364323935356261633135616361623937636163373335626134653764303436353433376434623732393464653430333234373431663763356634333334383962356330653538353263363535353038653135363539343530323666373363343039666162353865336163343662386637363636643534353630393534643538613465383765653562343233353463393135653332373361666662633161626461626534323565616539623739343630626437633462663330323033303130303031281d32060800100018203a60353636626538323639636636623133393964636138323038386436303735356430656463303561316231663438663366623664626233306338343235663335313462613563613062653363353436643131393930353836363138663366363635420a0a042256ba9710a48803420a0a04031451e610a48803420a0a042256ba9710a38803420a0a04031451e610a388034a22486f7374656420666f722048697461636869207c2056697267696e69612c205553415092a983eac8d0da92010abc0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303062646339343462333836373836343062653631653165653835303062303734343764313264633762313663353762636261363362633134666638386534376433363938383765303739343939373331333563386438633562363531353561613064303830653832396333663231613037633237343665333831363365663863366237313433313831306362323865616138643735646238343035356163663962353663393663386234376335313632633661613638616639653164623638336434363337333839643963383835383064383637336338663933303562363764326236356562316135626431316535376239333065396534663235653566633431376362646133346433323164326636646137663930393765326539653364623834313435653038633638323563633133633538356436393436363230633566336165353461356562393666616230323338313536623135386332613631643435313138306137343865666362343764306632323264376338393563333735656233636563616562313165343966653337383139623939666536373533623463643831383939353038653830666531306666353435346231663364643864376236613033633232653664333034613862343366653033636338386237613836323439656564343362363238386562336239356531383131663430356362333963386333336332303865643638656239613530333937333032313330636133393735656564393937363762663964623664373630323936323463336531303462363566646237323733336532393436386533323964373063623162323536316261633436643264656261393035333963663834623662616162313735303634643036633730623939376231313035363039663038613333323136336536313939613762303432303433623664383334623164306666663438643837633639313730616162313864386435363435323766356362363836356136386163356135633633313064376531666630376437646363343339663839356335666661646463626430323033303130303031281e32060800100018213a60336533313735656138616132656337353562643334363237353331376566626633363330666666663331633665623038633763393131643464323438333435313866643862636434373463663963663532323562346332363664346630653065420a0a041288411610a38803420a0a04228eace410a38803420a0a04228eace410a48803420a0a041288411610a488034a45486f7374656420666f72204d6f6e64656cc4937a20496e7465726e6174696f6e616c207c2053696e6761706f72652c2052657075626c6963206f662053696e6761706f726550a4a2fcd0daaae18c010a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303065306563336265386535646230626330316536383832353138633265623536373366336532613131353666353436383662633839613934333334363836336534373938366162613766333537366264343738323063356565616564633332383438343736646533333365396339383435373733383230373934303036366632643534653864343165393032373932333364633363656266303735303866616262363836393261663566316334363737333462356131313866393534313361353263383939313561363939663339643266396330376665623930346331366537393635633337666165303237653733363435376565333531336263656631613662333034343261333130663233356361373331366536316135336263393166333131373862383530386164383661333737366365383262373565353032353038636239313435353839646632653365346237323663373961386532326533396438323432626233353662656264326339363636666430653433396636383031363134663632313363373139646562366539353966646166393632316365373936373862306533656166303361626465663261633663646535613837353238643731653639363031656331373330393136663138643864346132386566373464313134323562656362626661353439313166323632333933316532303337623664396131303662343762633630633961363239366563383537653035383264613063393739663931373337646363303239616635626265333464653663323363373962333237613639663532653337633661353862616130653438376463653634323163616535343066363230393766356631323232653461373065323638353935353237363032616634313361353764383864363765373461636262326432383631323633613335396133343632333936346633346566646261363733323566333133336339613064356632306333303961653237616131623765633866376539383133363433663338306163336264663164636332363435646262613433646630323033303130303031281f32060800100018223a60656164643732666366363066616233343232386337323961366432353834616436353432633265346537383561333531643331656338333235306434383264343064656136313737666364626364366530316163623365383063396230636138420a0a0422108bf810a38803420a0a04239bd45a10a48803420a0a04239bd45a10a38803420a0a0422108bf810a488034a22486f7374656420666f7220426974476f207c2043616c69666f726e69612c205553415092a983eac8d0da9201"; diff --git a/src/client/addressbooks/previewnet.js b/src/client/addressbooks/previewnet.js index 57efdacb4..29736ad03 100644 --- a/src/client/addressbooks/previewnet.js +++ b/src/client/addressbooks/previewnet.js @@ -1,2 +1,2 @@ export const addressBook = - "0a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039663166386131323163326664366337366664353038643365343239663063363462636234346338326137303537333535326161646361643037313536396537323139353866356135643039663935383766666166636662653533343161326630313134616361653334366566336339303231336433343336656262323766343335306339393063356338633366386531653336373037626330386434323536303832336533663234653039613033616430393535613530393830313936323964643034623237623235316463653035356633646463623061343164363666303934316230623837636466653334393864343630333861623564663036663632613561646530383539383537336138386338663538363064633134393261366531383634383561396231333235306536643137623830636433396335633831393130396537336361373332646232336566386261613737366563383563653030393162656362326564656662616135656433653564626662643166383835613466613838316166336631343461386135363538353335333364383933393335393230383662326431643336326534356266653166623435363833616261366336343039373961643662343638373731383437323663366562643538623265616538356337636665336662616265663566366363656438353030333462333834373230366332643637386333363138373630323662386433353165303032616635653066666536663562316632393566646332663436396361613264323338316561306234386361393837636332633865363335653862313963653565313732613933373631613864343930613961343531386437323535383830613134643737623762613737343839326239326134306262383133363265333466633664353137386439623330313132393334323035636237376662396132383234323733393435363461383535346561343732383661343766383632333965373563393437383963653938633939383434373832343632393434663631333136376437623530323033303130303031280032060800100018033a60666664366164613734613361333461393034626561343736303330383666386265663362366265313861626564343463346434306531326662313330623937626436623835356165633564306239306230623863373335346435663362306534420a0a0403d3f8ac10a38803420a0a0403d3f8ac10a48803420a0a0423e7d09410a48803420a0a0423e7d09410a388034a22486f7374656420627920486564657261207c204561737420436f6173742c2055534150000a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063353537616635373966613833353031626538393962323839303737363562666466636435326162343332623031393561316631656364383666633030616236633535303962306664643937656464336362356365613536613239356633313261626235353038333164626639363366343530313138623466636336653232636634363736323030636539636338656466626266353538646336396630323432363461643764336461623233626564323133336332373465363933343438393135356462313038376639303337303930356336343138356136323131646337343266623961363930396438323138363934376232373734363364666233666630616364343765666631326561643166363937326566326331323033373933633435653737353735626534666131313063376534306661386462396336313837643131336634373034303134313739303731616266353962653764326230646538326465343231356463323535303662316339633236653439313734303163393937353036653337376536626630336236383837323765373934306661643639633565306461336364356362643262653737373335306165613264306434376539376134343863383462653663653133346436346265653039383563323931363266346331653536376363613933643036613363316265386162636533356235353766623737663466653637316136366465633739303735366430653838313831363566326261636161383931616165376163373433376663373137356236656236646562373437323337383735316262366266396230653134383366393636386539666462643536303463333962313464396532626564656563383436613938306437303464313731653762613462376663643161333064393435636131326634376133323564393339386161313866393730363630353464346431356663383939346532646562653733653932373164353438363833663631656134346662323530373165333531386137386564336562333765373161303639316632363730323033303130303031280132060800100018043a60663064393461636366366466663337323837346339646264386437393932656233313761663530303163613431393661626132363538303963623364323030626139363161353433386333613565643035633833626466396364313135643232420a0a0423c70fb110a48803420a0a040385d59210a48803420a0a040385d59210a38803420a0a0423c70fb110a388034a22486f7374656420627920486564657261207c204561737420436f6173742c2055534150000a8e0822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039626134353762373333303566303461393163633436623162393635633465383431373531616263386231343135613062616466643166333263323438323338366132323732356562376563373464656132316535303631376436343865613561633339333734316162303162386566623332313233396238643466646231646662656239653366333961613436353830646430343564313863613434643030326333376464623532376363653464646333326266633733343139363731663463613434363461336632613834666338356337316163663065356138393632366466363961383134373465643136353239663830316138616661393765343335633465303461393634613335373532373238383834336535386630613035636635313533656534353037623263363862336437666235346165366139356139353963383761313266363330653935633762316233633336393565383538363632343137393236643736633136393833666166363132323530333837343539303765396366313364363763326163643530336361343531633835393333616334313138616363323739383031636239363833343939303331343563656432373632396464303839313633313730393335383761373763323230356366613532353433623533633362366561313562383465336432633330633165643735326134363333633336623235623938393365613032616435363265623962373836386233623466343766346132356533353630363439363261633762323565353832393434663030643330373938613236326639323134643863356537346430613833373663633264366261363465313866356534613430616661633632353036326432636132336364323830303730383332316433383334333134663065353834343835393233323637336133326537306165306437313165333130353831626364623134653837313334363934633665303933306634366233376239366434396136343537333934373333316537653530376439653536646535653631343666326630323033303130303031280232060800100018053a60636136373865626362643364633836343866376564303366623539663065323161663637353133656165653531333138653662353439626535616365393036656463316666613236643933613537616365633962653737663430656165656437420a0a0423e1c9c310a48803420a0a04340f698210a38803420a0a0423e1c9c310a38803420a0a04340f698210a488034a1f486f7374656420627920486564657261207c2043656e7472616c2c2055534150000a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063343263636163356662633639316662626562646138376666643165373562646364383932323439346366343466646263636565343937383835323163333738626637376462303933346563306432313833643763353164623636663836346331316162376465316163336334636664633166303933613264366633376532623334636265346338313331663936383361643432383738633833643335353463363435616131363762636662303634613833646334356335623131353834393966396439323538376666663761626364356632323163643831353035343834313330303066613665353635393038396231646664363537363665613738656165646663613662343534353566643861623539383464626533356535373935643263363335656137393734643433653865616534666562666665343932653730376234386231623066633634383161653965303964333931333330303962376432363430326536653532653565393162326233383064383866306265376662346233303365373032313937383530353761613934636539323463343932366539313635363932383665383662336261363531636132613061363364663466363930376665666533343833643933623463653164346430336337313432313131333735623263326335316434656238333965333761663533306232636264366635306434636233366532373933373137306439636464616330616365326363323462383034623061323733353163663833306237363532356532366466623964626634396130353636323461373638363234393465373236336430643730636562616539353239343365353538343266356361643133666366363061326536646366376131643533336633613562623534656332313931386337366535323562613239313436363735383331653137653336633631666538353439383832386430396237363230313534313262326535323738343962616563316366666337376465346332393463353530383131653539386666323464613135613334353639646430323033303130303031280332060800100018063a60323437316633666538313430363831666539313931336432636330363366303635653434393061653632666635643534386135616265313331643261663936636265336163323562626532343336366361346638663065373663663934356633420a0a0423f76d8710a48803420a0a0436f1260110a48803420a0a0423f76d8710a38803420a0a0436f1260110a388034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150000a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039303266303439306139623766356432636431633064393663366136393930663537336235663065623562646262613339363631656630323330393234313933343436363939363961363861346337303731643332393939306662313739326539303031636235353938656137316332643636373638323433323065653463616266316464333537616537663261646265646331623162306139643935363233373739623463346337623437633437383761313665653731383863373231373137373632346139323634616233396334316637666630623435613839626461343063346164303763346435393664356630396437303536626362356133356634346639356135396332363665303938393264636265343661643531663264326233653939316138663636353865316632636239346337373365623434633434653839326431653535633130373666313630383331396565363537653430663139323936373534336162343261623232323338366431373538366532353337343864616264303235653530623530616536303530373230653233396436346565366662343530376330363134646434626537616664623133333038393066663361366531373635323763333131366166313239613961633565333336643966363031653731323761366437643832306164326639303264616339623234383636386131626162303864313033343265613639613730393731333266663731323063633634666364653738343063363536626131373332626139356539633336373531313735653465633364383461376530643238383432623431626262626436663238653436633361363633336531383237393635633535383230643530646165326230343635636330643432653139356239643135333265363232356562393938643661343930373961386131636434643031373564653363383766393736313438343762336362623137616133346265383230623762336164393861633366616566393933613637373839373437383263306334616533666162626363343330323033303130303031280432060800100018073a60663335373837336434313134613161656630336164633662613639656661663236393065323237616263313661366663366535303439613633666264393638383030346231346534363363323065333834333661336132346433313832646438420a0a0423eb413310a48803420a0a0436b1337f10a48803420a0a0423eb413310a38803420a0a0436b1337f10a388034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150000a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039316437646666663738663465666265353839303435306335626339653335333462666661646164393366623761666231356263376263663637643364336234313362643939393430646438323536346164613034616232653465646630613163306238666237653161383039326539313338653936306265326363363862356239376635376432383163353837326539376134373966633834383336333136306533383633623537623333653438363962313835616365356533366264343361653566613637386339656236366631663430313437383638323662326638666137653030363066343430356330613866396461373230356666343638336132343366613066333135663161666262346134643134306430323233346534343733666239326663623338663365623238633630636637636266623634653036396331383038366534646436313933383932306165306664376331393365366531303465363562383137656439333938653233323233376664663038333232633963656330396434303939323732613763303135643232623464636339363966366561316635313839303231303564663630303932623535613431623466333262393537623537643834653562323233393035653836393839353137333365613966326532343631656330643635323265653831366435383530666163666562343132636666396239393934336138376463306430343634343763653933623937653136643733623936623432363339363266383166636639343538653537353737633738306136663136313561613761313233323637333865323639626237333166383965383931363232653537376561353434323062663063613436626536666334663731636632363831616330323532616138383565313362653637326364323834353930343237646364313337636633313136323565386265653362303866646361616634363562333837636537636233333831366632633134613662393961633764373334333138636663353962376564393339626166656638373930323033303130303031280532060800100018083a60343933316137383230326435356631306233313537353738356333663433396462363831396264313130303364663762633263653932653239613531376237633231383830646562346330313739353734346235373663643433623834393864420a0a04226af74110a48803420a0a04235359ab10a48803420a0a04226af74110a38803420a0a04235359ab10a388034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150000a910822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063366531386338666266346364346562313034353432636232306161616132353264393566303532663130383664353831633434616437333762663636373663306333663738396166353236356238616662373962353039313264613834653061666366373534376362316666663038643035323730313765623664633563646638336235313936396434343333366136333837636437306239346266346339626166323032393834306535663466383633643730383166306661383165303836336164656462386238396135646163326262353532643665376239666261323232616332386335373037353533386663393537393932393432643334316661323837366536623530376539636537656435373265386366646135646566613336346664663864386532333832396134636362623437386631316565653362333261623835653037323935316335643934323031313566626133323730373334393466343362356636626562663834313532653335366537623136626137363462376133623532636232373334363430313633626531343635653664316661346336653666363636383461363335633961353536616137313030646265363435646638663463343233616534356130386362333562346263313837383836653232393962356330323130613566626133623934343966343833656639346564393232653165393863313133626531363662383963373335383232343331333564343432333036616265356137316237373031386666333335643664643739353432363937623136383233386239363732376664313333396235663832613362366135393764393736303337616532353036343536633862333465396662663362633332343130343431633462666338656261353835393732353465666562666161373838303961356338383534373239613562613738656365313966633834303764643838393461366263373834343033376438373863616365366331353263326538396538613634623036386136633233376530393939336265383036383930323033303130303031280632060800100018093a60363465303938363135626634303566376564356134303133343436623839633438386366636436626232356134613637366463373765656131316433336437303236383266306136396138303330653863353737376430653432323033373939420a0a04227d173110a38803420a0a043212115d10a38803420a0a043212115d10a48803420a0a04227d173110a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415000"; + "0a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039663166386131323163326664366337366664353038643365343239663063363462636234346338326137303537333535326161646361643037313536396537323139353866356135643039663935383766666166636662653533343161326630313134616361653334366566336339303231336433343336656262323766343335306339393063356338633366386531653336373037626330386434323536303832336533663234653039613033616430393535613530393830313936323964643034623237623235316463653035356633646463623061343164363666303934316230623837636466653334393864343630333861623564663036663632613561646530383539383537336138386338663538363064633134393261366531383634383561396231333235306536643137623830636433396335633831393130396537336361373332646232336566386261613737366563383563653030393162656362326564656662616135656433653564626662643166383835613466613838316166336631343461386135363538353335333364383933393335393230383662326431643336326534356266653166623435363833616261366336343039373961643662343638373731383437323663366562643538623265616538356337636665336662616265663566366363656438353030333462333834373230366332643637386333363138373630323662386433353165303032616635653066666536663562316632393566646332663436396361613264323338316561306234386361393837636332633865363335653862313963653565313732613933373631613864343930613961343531386437323535383830613134643737623762613737343839326239326134306262383133363265333466633664353137386439623330313132393334323035636237376662396132383234323733393435363461383535346561343732383661343766383632333965373563393437383963653938633939383434373832343632393434663631333136376437623530323033303130303031280032060800100018033a60666664366164613734613361333461393034626561343736303330383666386265663362366265313861626564343463346434306531326662313330623937626436623835356165633564306239306230623863373335346435663362306534420a0a0423e7d09410a38803420a0a0403d3f8ac10a38803420a0a0423e7d09410a48803420a0a0403d3f8ac10a488034a22486f7374656420627920486564657261207c204561737420436f6173742c2055534150809a9b96cab89abd020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063353537616635373966613833353031626538393962323839303737363562666466636435326162343332623031393561316631656364383666633030616236633535303962306664643937656464336362356365613536613239356633313261626235353038333164626639363366343530313138623466636336653232636634363736323030636539636338656466626266353538646336396630323432363461643764336461623233626564323133336332373465363933343438393135356462313038376639303337303930356336343138356136323131646337343266623961363930396438323138363934376232373734363364666233666630616364343765666631326561643166363937326566326331323033373933633435653737353735626534666131313063376534306661386462396336313837643131336634373034303134313739303731616266353962653764326230646538326465343231356463323535303662316339633236653439313734303163393937353036653337376536626630336236383837323765373934306661643639633565306461336364356362643262653737373335306165613264306434376539376134343863383462653663653133346436346265653039383563323931363266346331653536376363613933643036613363316265386162636533356235353766623737663466653637316136366465633739303735366430653838313831363566326261636161383931616165376163373433376663373137356236656236646562373437323337383735316262366266396230653134383366393636386539666462643536303463333962313464396532626564656563383436613938306437303464313731653762613462376663643161333064393435636131326634376133323564393339386161313866393730363630353464346431356663383939346532646562653733653932373164353438363833663631656134346662323530373165333531386137386564336562333765373161303639316632363730323033303130303031280132060800100018043a60663064393461636366366466663337323837346339646264386437393932656233313761663530303163613431393661626132363538303963623364323030626139363161353433386333613565643035633833626466396364313135643232420a0a0423c70fb110a48803420a0a040385d59210a48803420a0a0423c70fb110a38803420a0a040385d59210a388034a22486f7374656420627920486564657261207c204561737420436f6173742c2055534150809a9b96cab89abd020a960822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039626134353762373333303566303461393163633436623162393635633465383431373531616263386231343135613062616466643166333263323438323338366132323732356562376563373464656132316535303631376436343865613561633339333734316162303162386566623332313233396238643466646231646662656239653366333961613436353830646430343564313863613434643030326333376464623532376363653464646333326266633733343139363731663463613434363461336632613834666338356337316163663065356138393632366466363961383134373465643136353239663830316138616661393765343335633465303461393634613335373532373238383834336535386630613035636635313533656534353037623263363862336437666235346165366139356139353963383761313266363330653935633762316233633336393565383538363632343137393236643736633136393833666166363132323530333837343539303765396366313364363763326163643530336361343531633835393333616334313138616363323739383031636239363833343939303331343563656432373632396464303839313633313730393335383761373763323230356366613532353433623533633362366561313562383465336432633330633165643735326134363333633336623235623938393365613032616435363265623962373836386233623466343766346132356533353630363439363261633762323565353832393434663030643330373938613236326639323134643863356537346430613833373663633264366261363465313866356534613430616661633632353036326432636132336364323830303730383332316433383334333134663065353834343835393233323637336133326537306165306437313165333130353831626364623134653837313334363934633665303933306634366233376239366434396136343537333934373333316537653530376439653536646535653631343666326630323033303130303031280232060800100018053a60636136373865626362643364633836343866376564303366623539663065323161663637353133656165653531333138653662353439626535616365393036656463316666613236643933613537616365633962653737663430656165656437420a0a0423e1c9c310a38803420a0a04340f698210a38803420a0a04340f698210a48803420a0a0423e1c9c310a488034a1f486f7374656420627920486564657261207c2043656e7472616c2c2055534150809a9b96cab89abd020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063343263636163356662633639316662626562646138376666643165373562646364383932323439346366343466646263636565343937383835323163333738626637376462303933346563306432313833643763353164623636663836346331316162376465316163336334636664633166303933613264366633376532623334636265346338313331663936383361643432383738633833643335353463363435616131363762636662303634613833646334356335623131353834393966396439323538376666663761626364356632323163643831353035343834313330303066613665353635393038396231646664363537363665613738656165646663613662343534353566643861623539383464626533356535373935643263363335656137393734643433653865616534666562666665343932653730376234386231623066633634383161653965303964333931333330303962376432363430326536653532653565393162326233383064383866306265376662346233303365373032313937383530353761613934636539323463343932366539313635363932383665383662336261363531636132613061363364663466363930376665666533343833643933623463653164346430336337313432313131333735623263326335316434656238333965333761663533306232636264366635306434636233366532373933373137306439636464616330616365326363323462383034623061323733353163663833306237363532356532366466623964626634396130353636323461373638363234393465373236336430643730636562616539353239343365353538343266356361643133666366363061326536646366376131643533336633613562623534656332313931386337366535323562613239313436363735383331653137653336633631666538353439383832386430396237363230313534313262326535323738343962616563316366666337376465346332393463353530383131653539386666323464613135613334353639646430323033303130303031280332060800100018063a60323437316633666538313430363831666539313931336432636330363366303635653434393061653632666635643534386135616265313331643261663936636265336163323562626532343336366361346638663065373663663934356633420a0a0423f76d8710a38803420a0a0436f1260110a48803420a0a0423f76d8710a48803420a0a0436f1260110a388034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150809a9b96cab89abd020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039303266303439306139623766356432636431633064393663366136393930663537336235663065623562646262613339363631656630323330393234313933343436363939363961363861346337303731643332393939306662313739326539303031636235353938656137316332643636373638323433323065653463616266316464333537616537663261646265646331623162306139643935363233373739623463346337623437633437383761313665653731383863373231373137373632346139323634616233396334316637666630623435613839626461343063346164303763346435393664356630396437303536626362356133356634346639356135396332363665303938393264636265343661643531663264326233653939316138663636353865316632636239346337373365623434633434653839326431653535633130373666313630383331396565363537653430663139323936373534336162343261623232323338366431373538366532353337343864616264303235653530623530616536303530373230653233396436346565366662343530376330363134646434626537616664623133333038393066663361366531373635323763333131366166313239613961633565333336643966363031653731323761366437643832306164326639303264616339623234383636386131626162303864313033343265613639613730393731333266663731323063633634666364653738343063363536626131373332626139356539633336373531313735653465633364383461376530643238383432623431626262626436663238653436633361363633336531383237393635633535383230643530646165326230343635636330643432653139356239643135333265363232356562393938643661343930373961386131636434643031373564653363383766393736313438343762336362623137616133346265383230623762336164393861633366616566393933613637373839373437383263306334616533666162626363343330323033303130303031280432060800100018073a60663335373837336434313134613161656630336164633662613639656661663236393065323237616263313661366663366535303439613633666264393638383030346231346534363363323065333834333661336132346433313832646438420a0a0436b1337f10a38803420a0a0423eb413310a48803420a0a0436b1337f10a48803420a0a0423eb413310a388034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150809a9b96cab89abd020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039316437646666663738663465666265353839303435306335626339653335333462666661646164393366623761666231356263376263663637643364336234313362643939393430646438323536346164613034616232653465646630613163306238666237653161383039326539313338653936306265326363363862356239376635376432383163353837326539376134373966633834383336333136306533383633623537623333653438363962313835616365356533366264343361653566613637386339656236366631663430313437383638323662326638666137653030363066343430356330613866396461373230356666343638336132343366613066333135663161666262346134643134306430323233346534343733666239326663623338663365623238633630636637636266623634653036396331383038366534646436313933383932306165306664376331393365366531303465363562383137656439333938653233323233376664663038333232633963656330396434303939323732613763303135643232623464636339363966366561316635313839303231303564663630303932623535613431623466333262393537623537643834653562323233393035653836393839353137333365613966326532343631656330643635323265653831366435383530666163666562343132636666396239393934336138376463306430343634343763653933623937653136643733623936623432363339363266383166636639343538653537353737633738306136663136313561613761313233323637333865323639626237333166383965383931363232653537376561353434323062663063613436626536666334663731636632363831616330323532616138383565313362653637326364323834353930343237646364313337636633313136323565386265653362303866646361616634363562333837636537636233333831366632633134613662393961633764373334333138636663353962376564393339626166656638373930323033303130303031280532060800100018083a60343933316137383230326435356631306233313537353738356333663433396462363831396264313130303364663762633263653932653239613531376237633231383830646562346330313739353734346235373663643433623834393864420a0a04235359ab10a48803420a0a04226af74110a38803420a0a04226af74110a48803420a0a04235359ab10a388034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150809a9b96cab89abd020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063366531386338666266346364346562313034353432636232306161616132353264393566303532663130383664353831633434616437333762663636373663306333663738396166353236356238616662373962353039313264613834653061666366373534376362316666663038643035323730313765623664633563646638336235313936396434343333366136333837636437306239346266346339626166323032393834306535663466383633643730383166306661383165303836336164656462386238396135646163326262353532643665376239666261323232616332386335373037353533386663393537393932393432643334316661323837366536623530376539636537656435373265386366646135646566613336346664663864386532333832396134636362623437386631316565653362333261623835653037323935316335643934323031313566626133323730373334393466343362356636626562663834313532653335366537623136626137363462376133623532636232373334363430313633626531343635653664316661346336653666363636383461363335633961353536616137313030646265363435646638663463343233616534356130386362333562346263313837383836653232393962356330323130613566626133623934343966343833656639346564393232653165393863313133626531363662383963373335383232343331333564343432333036616265356137316237373031386666333335643664643739353432363937623136383233386239363732376664313333396235663832613362366135393764393736303337616532353036343536633862333465396662663362633332343130343431633462666338656261353835393732353465666562666161373838303961356338383534373239613562613738656365313966633834303764643838393461366263373834343033376438373863616365366331353263326538396538613634623036386136633233376530393939336265383036383930323033303130303031280632060800100018093a60363465303938363135626634303566376564356134303133343436623839633438386366636436626232356134613637366463373765656131316433336437303236383266306136396138303330653863353737376430653432323033373939420a0a04227d173110a38803420a0a043212115d10a48803420a0a043212115d10a38803420a0a04227d173110a488034a22486f7374656420627920486564657261207c205765737420436f6173742c2055534150809a9b96cab89abd02"; diff --git a/src/client/addressbooks/testnet.js b/src/client/addressbooks/testnet.js index 1d08de14a..b1f67e315 100644 --- a/src/client/addressbooks/testnet.js +++ b/src/client/addressbooks/testnet.js @@ -1,2 +1,2 @@ export const addressBook = - "0a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039663166386131323163326664366337366664353038643365343239663063363462636234346338326137303537333535326161646361643037313536396537323139353866356135643039663935383766666166636662653533343161326630313134616361653334366566336339303231336433343336656262323766343335306339393063356338633366386531653336373037626330386434323536303832336533663234653039613033616430393535613530393830313936323964643034623237623235316463653035356633646463623061343164363666303934316230623837636466653334393864343630333861623564663036663632613561646530383539383537336138386338663538363064633134393261366531383634383561396231333235306536643137623830636433396335633831393130396537336361373332646232336566386261613737366563383563653030393162656362326564656662616135656433653564626662643166383835613466613838316166336631343461386135363538353335333364383933393335393230383662326431643336326534356266653166623435363833616261366336343039373961643662343638373731383437323663366562643538623265616538356337636665336662616265663566366363656438353030333462333834373230366332643637386333363138373630323662386433353165303032616635653066666536663562316632393566646332663436396361613264323338316561306234386361393837636332633865363335653862313963653565313732613933373631613864343930613961343531386437323535383830613134643737623762613737343839326239326134306262383133363265333466633664353137386439623330313132393334323035636237376662396132383234323733393435363461383535346561343732383661343766383632333965373563393437383963653938633939383434373832343632393434663631333136376437623530323033303130303031280032060800100018033a60613137316533626138333437363734376165623265326163346430653131356361616162393138323033623064666531636465616234343334333866633238396162633862613861366166663833646235663162333334303436646138386338420a0a04321284d310a38803420a0a04225e6a3d10a38803420a0a04225e6a3d10a48803420a0a04321284d310a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080eed6cdcabbffbf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063353537616635373966613833353031626538393962323839303737363562666466636435326162343332623031393561316631656364383666633030616236633535303962306664643937656464336362356365613536613239356633313261626235353038333164626639363366343530313138623466636336653232636634363736323030636539636338656466626266353538646336396630323432363461643764336461623233626564323133336332373465363933343438393135356462313038376639303337303930356336343138356136323131646337343266623961363930396438323138363934376232373734363364666233666630616364343765666631326561643166363937326566326331323033373933633435653737353735626534666131313063376534306661386462396336313837643131336634373034303134313739303731616266353962653764326230646538326465343231356463323535303662316339633236653439313734303163393937353036653337376536626630336236383837323765373934306661643639633565306461336364356362643262653737373335306165613264306434376539376134343863383462653663653133346436346265653039383563323931363266346331653536376363613933643036613363316265386162636533356235353766623737663466653637316136366465633739303735366430653838313831363566326261636161383931616165376163373433376663373137356236656236646562373437323337383735316262366266396230653134383366393636386539666462643536303463333962313464396532626564656563383436613938306437303464313731653762613462376663643161333064393435636131326634376133323564393339386161313866393730363630353464346431356663383939346532646562653733653932373164353438363833663631656134346662323530373165333531386137386564336562333765373161303639316632363730323033303130303031280132060800100018043a60373430396465633265343934623632376565343963363962323934626531636561656263613366646361663336373839653838666337643562306565663535363166353262383264333531393161333963326662656436303237323637313636420a0a0403d4060d10a38803420a0a0403d4060d10a48803420a0a0423ed773710a38803420a0a0423ed773710a488034a22486f7374656420627920486564657261207c204561737420436f6173742c205553415080eed6cdcabbffbf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039626134353762373333303566303461393163633436623162393635633465383431373531616263386231343135613062616466643166333263323438323338366132323732356562376563373464656132316535303631376436343865613561633339333734316162303162386566623332313233396238643466646231646662656239653366333961613436353830646430343564313863613434643030326333376464623532376363653464646333326266633733343139363731663463613434363461336632613834666338356337316163663065356138393632366466363961383134373465643136353239663830316138616661393765343335633465303461393634613335373532373238383834336535386630613035636635313533656534353037623263363862336437666235346165366139356139353963383761313266363330653935633762316233633336393565383538363632343137393236643736633136393833666166363132323530333837343539303765396366313364363763326163643530336361343531633835393333616334313138616363323739383031636239363833343939303331343563656432373632396464303839313633313730393335383761373763323230356366613532353433623533633362366561313562383465336432633330633165643735326134363333633336623235623938393365613032616435363265623962373836386233623466343766346132356533353630363439363261633762323565353832393434663030643330373938613236326639323134643863356537346430613833373663633264366261363465313866356534613430616661633632353036326432636132336364323830303730383332316433383334333134663065353834343835393233323637336133326537306165306437313165333130353831626364623134653837313334363934633665303933306634366233376239366434396136343537333934373333316537653530376439653536646535653631343666326630323033303130303031280232060800100018053a60396231343136353834613461333830626238366136633764373230376438616564646263336236336561333035393938323535626365383335316261346235646361353263393238326135346136626564363064653633636530336161613234420a0a043414125610a38803420a0a043414125610a48803420a0a0423f51bc110a38803420a0a0423f51bc110a488034a22486f7374656420627920486564657261207c204561737420436f6173742c205553415080eed6cdcabbffbf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063343263636163356662633639316662626562646138376666643165373562646364383932323439346366343466646263636565343937383835323163333738626637376462303933346563306432313833643763353164623636663836346331316162376465316163336334636664633166303933613264366633376532623334636265346338313331663936383361643432383738633833643335353463363435616131363762636662303634613833646334356335623131353834393966396439323538376666663761626364356632323163643831353035343834313330303066613665353635393038396231646664363537363665613738656165646663613662343534353566643861623539383464626533356535373935643263363335656137393734643433653865616534666562666665343932653730376234386231623066633634383161653965303964333931333330303962376432363430326536653532653565393162326233383064383866306265376662346233303365373032313937383530353761613934636539323463343932366539313635363932383665383662336261363531636132613061363364663466363930376665666533343833643933623463653164346430336337313432313131333735623263326335316434656238333965333761663533306232636264366635306434636233366532373933373137306439636464616330616365326363323462383034623061323733353163663833306237363532356532366466623964626634396130353636323461373638363234393465373236336430643730636562616539353239343365353538343266356361643133666366363061326536646366376131643533336633613562623534656332313931386337366535323562613239313436363735383331653137653336633631666538353439383832386430396237363230313534313262326535323738343962616563316366666337376465346332393463353530383131653539386666323464613135613334353639646430323033303130303031280332060800100018063a60363438363638356234653665306362393633343732633031666539393933316664396534633434383837626138333432336165376665656432326436343834383463663861336263356363636136613337333837626639366433383637323830420a0a043646c02110a48803420a0a042253707410a38803420a0a043646c02110a38803420a0a042253707410a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080eed6cdcabbffbf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039303266303439306139623766356432636431633064393663366136393930663537336235663065623562646262613339363631656630323330393234313933343436363939363961363861346337303731643332393939306662313739326539303031636235353938656137316332643636373638323433323065653463616266316464333537616537663261646265646331623162306139643935363233373739623463346337623437633437383761313665653731383863373231373137373632346139323634616233396334316637666630623435613839626461343063346164303763346435393664356630396437303536626362356133356634346639356135396332363665303938393264636265343661643531663264326233653939316138663636353865316632636239346337373365623434633434653839326431653535633130373666313630383331396565363537653430663139323936373534336162343261623232323338366431373538366532353337343864616264303235653530623530616536303530373230653233396436346565366662343530376330363134646434626537616664623133333038393066663361366531373635323763333131366166313239613961633565333336643966363031653731323761366437643832306164326639303264616339623234383636386131626162303864313033343265613639613730393731333266663731323063633634666364653738343063363536626131373332626139356539633336373531313735653465633364383461376530643238383432623431626262626436663238653436633361363633336531383237393635633535383230643530646165326230343635636330643432653139356239643135333265363232356562393938643661343930373961386131636434643031373564653363383766393736313438343762336362623137616133346265383230623762336164393861633366616566393933613637373839373437383263306334616533666162626363343330323033303130303031280432060800100018073a60333965393039393135613835323830333031353461366337373039353063376234373737626134303133353763306536313837363534323135636332306161636364643865356666323965396334643935636634313031666136386265343563420a0a0436b0c76d10a38803420a0a04225ea00410a48803420a0a0436b0c76d10a48803420a0a04225ea00410a388034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080eed6cdcabbffbf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039316437646666663738663465666265353839303435306335626339653335333462666661646164393366623761666231356263376263663637643364336234313362643939393430646438323536346164613034616232653465646630613163306238666237653161383039326539313338653936306265326363363862356239376635376432383163353837326539376134373966633834383336333136306533383633623537623333653438363962313835616365356533366264343361653566613637386339656236366631663430313437383638323662326638666137653030363066343430356330613866396461373230356666343638336132343366613066333135663161666262346134643134306430323233346534343733666239326663623338663365623238633630636637636266623634653036396331383038366534646436313933383932306165306664376331393365366531303465363562383137656439333938653233323233376664663038333232633963656330396434303939323732613763303135643232623464636339363966366561316635313839303231303564663630303932623535613431623466333262393537623537643834653562323233393035653836393839353137333365613966326532343631656330643635323265653831366435383530666163666562343132636666396239393934336138376463306430343634343763653933623937653136643733623936623432363339363266383166636639343538653537353737633738306136663136313561613761313233323637333865323639626237333166383965383931363232653537376561353434323062663063613436626536666334663731636632363831616330323532616138383565313362653637326364323834353930343237646364313337636633313136323565386265653362303866646361616634363562333837636537636233333831366632633134613662393961633764373334333138636663353962376564393339626166656638373930323033303130303031280532060800100018083a60613434383734613761613162333737343161303731616461616537386662313532623639366431633538643864656662653164383233303435333261306330313965653936636331396437353638363537386433396131653663333161316565420a0a04226a66da10a38803420a0a04239b319310a38803420a0a04239b319310a48803420a0a04226a66da10a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080eed6cdcabbffbf020a960822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063366531386338666266346364346562313034353432636232306161616132353264393566303532663130383664353831633434616437333762663636373663306333663738396166353236356238616662373962353039313264613834653061666366373534376362316666663038643035323730313765623664633563646638336235313936396434343333366136333837636437306239346266346339626166323032393834306535663466383633643730383166306661383165303836336164656462386238396135646163326262353532643665376239666261323232616332386335373037353533386663393537393932393432643334316661323837366536623530376539636537656435373265386366646135646566613336346664663864386532333832396134636362623437386631316565653362333261623835653037323935316335643934323031313566626133323730373334393466343362356636626562663834313532653335366537623136626137363462376133623532636232373334363430313633626531343635653664316661346336653666363636383461363335633961353536616137313030646265363435646638663463343233616534356130386362333562346263313837383836653232393962356330323130613566626133623934343966343833656639346564393232653165393863313133626531363662383963373335383232343331333564343432333036616265356137316237373031386666333335643664643739353432363937623136383233386239363732376664313333396235663832613362366135393764393736303337616532353036343536633862333465396662663362633332343130343431633462666338656261353835393732353465666562666161373838303961356338383534373239613562613738656365313966633834303764643838393461366263373834343033376438373863616365366331353263326538396538613634623036386136633233376530393939336265383036383930323033303130303031280632060800100018093a60363938333261373361333630326538643166626535616435386431633236333761316236373264373165653837616631306462363438656239316166623232383235336231663437653537643364346134346666353437623333393461613232420a0a042285c5e610a48803420a0a04340efccf10a48803420a0a042285c5e610a38803420a0a04340efccf10a388034a1f486f7374656420627920486564657261207c2043656e7472616c2c205553415080eed6cdcabbffbf02"; + "0a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039663166386131323163326664366337366664353038643365343239663063363462636234346338326137303537333535326161646361643037313536396537323139353866356135643039663935383766666166636662653533343161326630313134616361653334366566336339303231336433343336656262323766343335306339393063356338633366386531653336373037626330386434323536303832336533663234653039613033616430393535613530393830313936323964643034623237623235316463653035356633646463623061343164363666303934316230623837636466653334393864343630333861623564663036663632613561646530383539383537336138386338663538363064633134393261366531383634383561396231333235306536643137623830636433396335633831393130396537336361373332646232336566386261613737366563383563653030393162656362326564656662616135656433653564626662643166383835613466613838316166336631343461386135363538353335333364383933393335393230383662326431643336326534356266653166623435363833616261366336343039373961643662343638373731383437323663366562643538623265616538356337636665336662616265663566366363656438353030333462333834373230366332643637386333363138373630323662386433353165303032616635653066666536663562316632393566646332663436396361613264323338316561306234386361393837636332633865363335653862313963653565313732613933373631613864343930613961343531386437323535383830613134643737623762613737343839326239326134306262383133363265333466633664353137386439623330313132393334323035636237376662396132383234323733393435363461383535346561343732383661343766383632333965373563393437383963653938633939383434373832343632393434663631333136376437623530323033303130303031280032060800100018033a60613137316533626138333437363734376165623265326163346430653131356361616162393138323033623064666531636465616234343334333866633238396162633862613861366166663833646235663162333334303436646138386338420a0a04321284d310a48803420a0a04225e6a3d10a38803420a0a04321284d310a38803420a0a04225e6a3d10a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080aefb83ebfdfebf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063353537616635373966613833353031626538393962323839303737363562666466636435326162343332623031393561316631656364383666633030616236633535303962306664643937656464336362356365613536613239356633313261626235353038333164626639363366343530313138623466636336653232636634363736323030636539636338656466626266353538646336396630323432363461643764336461623233626564323133336332373465363933343438393135356462313038376639303337303930356336343138356136323131646337343266623961363930396438323138363934376232373734363364666233666630616364343765666631326561643166363937326566326331323033373933633435653737353735626534666131313063376534306661386462396336313837643131336634373034303134313739303731616266353962653764326230646538326465343231356463323535303662316339633236653439313734303163393937353036653337376536626630336236383837323765373934306661643639633565306461336364356362643262653737373335306165613264306434376539376134343863383462653663653133346436346265653039383563323931363266346331653536376363613933643036613363316265386162636533356235353766623737663466653637316136366465633739303735366430653838313831363566326261636161383931616165376163373433376663373137356236656236646562373437323337383735316262366266396230653134383366393636386539666462643536303463333962313464396532626564656563383436613938306437303464313731653762613462376663643161333064393435636131326634376133323564393339386161313866393730363630353464346431356663383939346532646562653733653932373164353438363833663631656134346662323530373165333531386137386564336562333765373161303639316632363730323033303130303031280132060800100018043a60373430396465633265343934623632376565343963363962323934626531636561656263613366646361663336373839653838666337643562306565663535363166353262383264333531393161333963326662656436303237323637313636420a0a0423ed773710a38803420a0a0403d4060d10a38803420a0a0423ed773710a48803420a0a0403d4060d10a488034a22486f7374656420627920486564657261207c204561737420436f6173742c205553415080aefb83ebfdfebf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039626134353762373333303566303461393163633436623162393635633465383431373531616263386231343135613062616466643166333263323438323338366132323732356562376563373464656132316535303631376436343865613561633339333734316162303162386566623332313233396238643466646231646662656239653366333961613436353830646430343564313863613434643030326333376464623532376363653464646333326266633733343139363731663463613434363461336632613834666338356337316163663065356138393632366466363961383134373465643136353239663830316138616661393765343335633465303461393634613335373532373238383834336535386630613035636635313533656534353037623263363862336437666235346165366139356139353963383761313266363330653935633762316233633336393565383538363632343137393236643736633136393833666166363132323530333837343539303765396366313364363763326163643530336361343531633835393333616334313138616363323739383031636239363833343939303331343563656432373632396464303839313633313730393335383761373763323230356366613532353433623533633362366561313562383465336432633330633165643735326134363333633336623235623938393365613032616435363265623962373836386233623466343766346132356533353630363439363261633762323565353832393434663030643330373938613236326639323134643863356537346430613833373663633264366261363465313866356534613430616661633632353036326432636132336364323830303730383332316433383334333134663065353834343835393233323637336133326537306165306437313165333130353831626364623134653837313334363934633665303933306634366233376239366434396136343537333934373333316537653530376439653536646535653631343666326630323033303130303031280232060800100018053a60396231343136353834613461333830626238366136633764373230376438616564646263336236336561333035393938323535626365383335316261346235646361353263393238326135346136626564363064653633636530336161613234420a0a0423f51bc110a38803420a0a043414125610a48803420a0a0423f51bc110a48803420a0a043414125610a388034a22486f7374656420627920486564657261207c204561737420436f6173742c205553415080aefb83ebfdfebf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063343263636163356662633639316662626562646138376666643165373562646364383932323439346366343466646263636565343937383835323163333738626637376462303933346563306432313833643763353164623636663836346331316162376465316163336334636664633166303933613264366633376532623334636265346338313331663936383361643432383738633833643335353463363435616131363762636662303634613833646334356335623131353834393966396439323538376666663761626364356632323163643831353035343834313330303066613665353635393038396231646664363537363665613738656165646663613662343534353566643861623539383464626533356535373935643263363335656137393734643433653865616534666562666665343932653730376234386231623066633634383161653965303964333931333330303962376432363430326536653532653565393162326233383064383866306265376662346233303365373032313937383530353761613934636539323463343932366539313635363932383665383662336261363531636132613061363364663466363930376665666533343833643933623463653164346430336337313432313131333735623263326335316434656238333965333761663533306232636264366635306434636233366532373933373137306439636464616330616365326363323462383034623061323733353163663833306237363532356532366466623964626634396130353636323461373638363234393465373236336430643730636562616539353239343365353538343266356361643133666366363061326536646366376131643533336633613562623534656332313931386337366535323562613239313436363735383331653137653336633631666538353439383832386430396237363230313534313262326535323738343962616563316366666337376465346332393463353530383131653539386666323464613135613334353639646430323033303130303031280332060800100018063a60363438363638356234653665306362393633343732633031666539393933316664396534633434383837626138333432336165376665656432326436343834383463663861336263356363636136613337333837626639366433383637323830420a0a043646c02110a38803420a0a042253707410a38803420a0a043646c02110a48803420a0a042253707410a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080aefb83ebfdfebf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039303266303439306139623766356432636431633064393663366136393930663537336235663065623562646262613339363631656630323330393234313933343436363939363961363861346337303731643332393939306662313739326539303031636235353938656137316332643636373638323433323065653463616266316464333537616537663261646265646331623162306139643935363233373739623463346337623437633437383761313665653731383863373231373137373632346139323634616233396334316637666630623435613839626461343063346164303763346435393664356630396437303536626362356133356634346639356135396332363665303938393264636265343661643531663264326233653939316138663636353865316632636239346337373365623434633434653839326431653535633130373666313630383331396565363537653430663139323936373534336162343261623232323338366431373538366532353337343864616264303235653530623530616536303530373230653233396436346565366662343530376330363134646434626537616664623133333038393066663361366531373635323763333131366166313239613961633565333336643966363031653731323761366437643832306164326639303264616339623234383636386131626162303864313033343265613639613730393731333266663731323063633634666364653738343063363536626131373332626139356539633336373531313735653465633364383461376530643238383432623431626262626436663238653436633361363633336531383237393635633535383230643530646165326230343635636330643432653139356239643135333265363232356562393938643661343930373961386131636434643031373564653363383766393736313438343762336362623137616133346265383230623762336164393861633366616566393933613637373839373437383263306334616533666162626363343330323033303130303031280432060800100018073a60333965393039393135613835323830333031353461366337373039353063376234373737626134303133353763306536313837363534323135636332306161636364643865356666323965396334643935636634313031666136386265343563420a0a04225ea00410a48803420a0a0436b0c76d10a38803420a0a04225ea00410a38803420a0a0436b0c76d10a488034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080aefb83ebfdfebf020a990822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303039316437646666663738663465666265353839303435306335626339653335333462666661646164393366623761666231356263376263663637643364336234313362643939393430646438323536346164613034616232653465646630613163306238666237653161383039326539313338653936306265326363363862356239376635376432383163353837326539376134373966633834383336333136306533383633623537623333653438363962313835616365356533366264343361653566613637386339656236366631663430313437383638323662326638666137653030363066343430356330613866396461373230356666343638336132343366613066333135663161666262346134643134306430323233346534343733666239326663623338663365623238633630636637636266623634653036396331383038366534646436313933383932306165306664376331393365366531303465363562383137656439333938653233323233376664663038333232633963656330396434303939323732613763303135643232623464636339363966366561316635313839303231303564663630303932623535613431623466333262393537623537643834653562323233393035653836393839353137333365613966326532343631656330643635323265653831366435383530666163666562343132636666396239393934336138376463306430343634343763653933623937653136643733623936623432363339363266383166636639343538653537353737633738306136663136313561613761313233323637333865323639626237333166383965383931363232653537376561353434323062663063613436626536666334663731636632363831616330323532616138383565313362653637326364323834353930343237646364313337636633313136323565386265653362303866646361616634363562333837636537636233333831366632633134613662393961633764373334333138636663353962376564393339626166656638373930323033303130303031280532060800100018083a60613434383734613761613162333737343161303731616461616537386662313532623639366431633538643864656662653164383233303435333261306330313965653936636331396437353638363537386433396131653663333161316565420a0a04226a66da10a38803420a0a04239b319310a48803420a0a04226a66da10a48803420a0a04239b319310a388034a22486f7374656420627920486564657261207c205765737420436f6173742c205553415080aefb83ebfdfebf020a960822cc0633303832303161323330306430363039326138363438383666373064303130313031303530303033383230313866303033303832303138613032383230313831303063366531386338666266346364346562313034353432636232306161616132353264393566303532663130383664353831633434616437333762663636373663306333663738396166353236356238616662373962353039313264613834653061666366373534376362316666663038643035323730313765623664633563646638336235313936396434343333366136333837636437306239346266346339626166323032393834306535663466383633643730383166306661383165303836336164656462386238396135646163326262353532643665376239666261323232616332386335373037353533386663393537393932393432643334316661323837366536623530376539636537656435373265386366646135646566613336346664663864386532333832396134636362623437386631316565653362333261623835653037323935316335643934323031313566626133323730373334393466343362356636626562663834313532653335366537623136626137363462376133623532636232373334363430313633626531343635653664316661346336653666363636383461363335633961353536616137313030646265363435646638663463343233616534356130386362333562346263313837383836653232393962356330323130613566626133623934343966343833656639346564393232653165393863313133626531363662383963373335383232343331333564343432333036616265356137316237373031386666333335643664643739353432363937623136383233386239363732376664313333396235663832613362366135393764393736303337616532353036343536633862333465396662663362633332343130343431633462666338656261353835393732353465666562666161373838303961356338383534373239613562613738656365313966633834303764643838393461366263373834343033376438373863616365366331353263326538396538613634623036386136633233376530393939336265383036383930323033303130303031280632060800100018093a60363938333261373361333630326538643166626535616435386431633236333761316236373264373165653837616631306462363438656239316166623232383235336231663437653537643364346134346666353437623333393461613232420a0a04340efccf10a38803420a0a042285c5e610a38803420a0a042285c5e610a48803420a0a04340efccf10a488034a1f486f7374656420627920486564657261207c2043656e7472616c2c205553415080aefb83ebfdfebf02"; diff --git a/src/contract/ContractCallQuery.js b/src/contract/ContractCallQuery.js index 102ac2f99..d9a7af600 100644 --- a/src/contract/ContractCallQuery.js +++ b/src/contract/ContractCallQuery.js @@ -243,9 +243,10 @@ export default class ContractCallQuery extends Query { * @internal * @param {HashgraphProto.proto.IQuery} request * @param {HashgraphProto.proto.IResponse} response + * @param {AccountId} nodeId * @returns {Error} */ - _mapStatusError(request, response) { + _mapStatusError(request, response, nodeId) { const { nodeTransactionPrecheckCode } = this._mapResponseHeader(response); @@ -262,6 +263,7 @@ export default class ContractCallQuery extends Query { (response.contractCallLocal); if (!call.functionResult) { return new PrecheckStatusError({ + nodeId, status, transactionId: this._getTransactionId(), contractFunctionResult: null, @@ -271,6 +273,7 @@ export default class ContractCallQuery extends Query { const contractFunctionResult = this._mapResponseSync(response); return new PrecheckStatusError({ + nodeId, status, transactionId: this._getTransactionId(), contractFunctionResult, diff --git a/src/exports.js b/src/exports.js index 290940718..de6f28266 100644 --- a/src/exports.js +++ b/src/exports.js @@ -128,6 +128,8 @@ export { default as Timestamp } from "./Timestamp.js"; export { default as TokenAllowance } from "./account/TokenAllowance.js"; export { default as TokenAssociateTransaction } from "./token/TokenAssociateTransaction.js"; export { default as TokenBurnTransaction } from "./token/TokenBurnTransaction.js"; +export { default as TokenRejectTransaction } from "./token/TokenRejectTransaction.js"; +export { default as TokenRejectFlow } from "./token/TokenRejectFlow.js"; export { default as TokenCreateTransaction } from "./token/TokenCreateTransaction.js"; export { default as TokenDeleteTransaction } from "./token/TokenDeleteTransaction.js"; export { default as TokenDissociateTransaction } from "./token/TokenDissociateTransaction.js"; @@ -175,11 +177,13 @@ export { default as LogLevel } from "./logger/LogLevel.js"; export { EntityIdHelper }; export { default as Long } from "long"; export { default as FreezeType } from "./FreezeType.js"; +export { default as TokenKeyValidation } from "./token/TokenKeyValidation.js"; export { default as StatusError } from "./StatusError.js"; export { default as PrecheckStatusError } from "./PrecheckStatusError.js"; export { default as ReceiptStatusError } from "./ReceiptStatusError.js"; export { default as LedgerId } from "./LedgerId.js"; +export { default as TokenUpdateNftsTransaction } from "./token/TokenUpdateNftsTransaction.js"; /** * @typedef {import("./client/Client.js").NetworkName} ClientNetworkName diff --git a/src/grpc/GrpcServiceError.js b/src/grpc/GrpcServiceError.js index 5345043c4..5a80b0197 100644 --- a/src/grpc/GrpcServiceError.js +++ b/src/grpc/GrpcServiceError.js @@ -33,7 +33,9 @@ export default class GrpcServiceError extends Error { * @param {GrpcStatus} status */ constructor(status) { - super(`gRPC service failed with status: ${status.toString()}`); + super( + `gRPC service failed with: Status: ${status.toString()}, Code: ${status.valueOf()}`, + ); /** * @readonly @@ -55,10 +57,20 @@ export default class GrpcServiceError extends Error { if (obj.code != null && obj.details != null) { const status = GrpcStatus._fromValue(obj.code); const err = new GrpcServiceError(status); - err.message = obj.details; + err.stack += `\nCaused by: ${ + obj.stack ? obj.stack.toString() : "" + }`; + err.message += `: ${obj.details}`; return err; } else { return /** @type {Error} */ (obj); } } + + /** + * @returns {string} + */ + toString() { + return `${this.name}: ${this.message}`; + } } diff --git a/src/logger/Logger.js b/src/logger/Logger.js index 704d925d2..dd60158eb 100644 --- a/src/logger/Logger.js +++ b/src/logger/Logger.js @@ -23,22 +23,63 @@ import LogLevel from "./LogLevel.js"; export default class Logger { /** * @param {LogLevel} level + * @param {string} logFile the file to log to, if empty, logs to console + * @param {boolean} sync perform writes synchronously (similar to console.log) + * @param {boolean} fsync perform a fsyncSync every time a write is completed + * @param {boolean} mkdir ensure directory for dest file exists when true (default false) + * @param {number} minLength the minimum length of the internal buffer that is required to be full before flushing */ - constructor(level) { + constructor( + level, + logFile = "", + sync = true, + fsync = true, + mkdir = true, + minLength = 0, + ) { + const fileTransport = logFile + ? pino.destination({ + dest: logFile, + sync, + fsync, + mkdir, + minLength, + }) + : null; + + const loggerOptions = fileTransport + ? { + level: level.toString(), + timestamp: pino.stdTimeFunctions.isoTime, + formatters: { + bindings: () => { + return {}; + }, + // @ts-ignore + level: (label) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access + return { level: label.toUpperCase() }; + }, + }, + } + : { + level: level.toString(), + transport: { + target: "pino-pretty", + options: { + translateTime: "SYS:dd-mm-yyyy HH:MM:ss", + ignore: "pid,hostname", + }, + }, + }; + /** * @private * @type {import("pino").Logger} */ - this._logger = pino({ - level: level.toString(), - transport: { - target: "pino-pretty", - options: { - translateTime: "SYS:dd-mm-yyyy HH:MM:ss", - ignore: "pid,hostname", - }, - }, - }); + this._logger = fileTransport + ? pino(loggerOptions, fileTransport) + : pino(loggerOptions); /** * @private diff --git a/src/network/NetworkVersionInfo.js b/src/network/NetworkVersionInfo.js index 726939333..d5c76c2c1 100644 --- a/src/network/NetworkVersionInfo.js +++ b/src/network/NetworkVersionInfo.js @@ -29,7 +29,7 @@ export default class NetworkVersionInfo { * @private * @param {object} props * @param {SemanticVersion} props.protobufVersion - * @param {SemanticVersion} props.servicesVesion + * @param {SemanticVersion} props.servicesVersion */ constructor(props) { /** @@ -44,7 +44,7 @@ export default class NetworkVersionInfo { * * @readonly */ - this.servicesVesion = props.servicesVesion; + this.servicesVersion = props.servicesVersion; Object.freeze(this); } @@ -60,7 +60,7 @@ export default class NetworkVersionInfo { /** @type {HashgraphProto.proto.ISemanticVersion} */ (info.hapiProtoVersion), ), - servicesVesion: SemanticVersion._fromProtobuf( + servicesVersion: SemanticVersion._fromProtobuf( /** @type {HashgraphProto.proto.ISemanticVersion} */ (info.hederaServicesVersion), ), @@ -74,7 +74,7 @@ export default class NetworkVersionInfo { _toProtobuf() { return { hapiProtoVersion: this.protobufVersion._toProtobuf(), - hederaServicesVersion: this.servicesVesion._toProtobuf(), + hederaServicesVersion: this.servicesVersion._toProtobuf(), }; } diff --git a/src/query/CostQuery.js b/src/query/CostQuery.js index fe896ef7f..86031bd4b 100644 --- a/src/query/CostQuery.js +++ b/src/query/CostQuery.js @@ -153,11 +153,12 @@ export default class CostQuery extends Executable { * @internal * @param {HashgraphProto.proto.IQuery} request * @param {HashgraphProto.proto.IResponse} response + * @param {AccountId} nodeId * @returns {Error} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - _mapStatusError(request, response) { - return this._query._mapStatusError(request, response); + _mapStatusError(request, response, nodeId) { + return this._query._mapStatusError(request, response, nodeId); } /** diff --git a/src/query/Query.js b/src/query/Query.js index 660f2a9be..f600408ff 100644 --- a/src/query/Query.js +++ b/src/query/Query.js @@ -442,34 +442,26 @@ export default class Query extends Executable { responseType: HashgraphProto.proto.ResponseType.ANSWER_ONLY, }; - if (this._isPaymentRequired() && this._paymentTransactions != null) { - if (this._nodeAccountIds.locked) { - header.payment = - this._paymentTransactions[this._nodeAccountIds.index]; - } else { - const logId = this._getLogId(); - const nodeId = this._nodeAccountIds.current; - const paymentTransactionId = - /** @type {import("../transaction/TransactionId.js").default} */ ( - this._paymentTransactionId - ); - const paymentAmount = /** @type {Hbar} */ (this._queryPayment); - - if (this._logger) { - this._logger.debug( - `[${logId}] making a payment transaction for node ${nodeId.toString()} and transaction ID ${paymentTransactionId.toString()} with amount ${paymentAmount.toString()}`, - ); - } + const logId = this._getLogId(); + const nodeId = this._nodeAccountIds.current; + const paymentTransactionId = TransactionId.generate( + this._operator ? this._operator.accountId : new AccountId(0), + ); + const paymentAmount = /** @type {Hbar} */ (this._queryPayment); - header.payment = await _makePaymentTransaction( - paymentTransactionId, - nodeId, - this._isPaymentRequired() ? this._operator : null, - paymentAmount, - ); - } + if (this._logger) { + this._logger.debug( + `[${logId}] making a payment transaction for node ${nodeId.toString()} and transaction ID ${paymentTransactionId.toString()} with amount ${paymentAmount.toString()}`, + ); } + header.payment = await _makePaymentTransaction( + paymentTransactionId, + nodeId, + this._isPaymentRequired() ? this._operator : null, + paymentAmount, + ); + return this._onMakeRequest(header); } @@ -500,6 +492,7 @@ export default class Query extends Executable { case Status.Busy: case Status.Unknown: case Status.PlatformTransactionNotCreated: + case Status.PlatformNotActive: return [status, ExecutionState.Retry]; case Status.Ok: return [status, ExecutionState.Finished]; @@ -513,10 +506,11 @@ export default class Query extends Executable { * @internal * @param {HashgraphProto.proto.IQuery} request * @param {HashgraphProto.proto.IResponse} response + * @param {AccountId} nodeId * @returns {Error} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - _mapStatusError(request, response) { + _mapStatusError(request, response, nodeId) { const { nodeTransactionPrecheckCode } = this._mapResponseHeader(response); @@ -527,6 +521,7 @@ export default class Query extends Executable { ); return new PrecheckStatusError({ + nodeId, status, transactionId: this._getTransactionId(), contractFunctionResult: null, diff --git a/src/token/TokenCreateTransaction.js b/src/token/TokenCreateTransaction.js index 1c335166f..e00ee8650 100644 --- a/src/token/TokenCreateTransaction.js +++ b/src/token/TokenCreateTransaction.js @@ -80,6 +80,8 @@ export default class TokenCreateTransaction extends Transaction { * @param {TokenType} [props.tokenType] * @param {TokenSupplyType} [props.supplyType] * @param {Long | number} [props.maxSupply] + * @param {Key} [props.metadataKey] + * @param {Uint8Array} [props.metadata] */ constructor(props = {}) { super(); @@ -172,7 +174,12 @@ export default class TokenCreateTransaction extends Transaction { * @private * @type {?Timestamp} */ - this._expirationTime = null; + this._expirationTime = new Timestamp( + Math.floor( + Date.now() / 1000 + DEFAULT_AUTO_RENEW_PERIOD.toNumber(), + ), + 0, + ); /** * @private @@ -212,6 +219,19 @@ export default class TokenCreateTransaction extends Transaction { this._defaultMaxTransactionFee = new Hbar(30); + /** + * @private + * @type {?Key} + */ + this._metadataKey = null; + + /** + * @private + * @description Metadata of the created token definition. + * @type {?Uint8Array} + */ + this._metadata = null; + if (props.tokenName != null) { this.setTokenName(props.tokenName); } @@ -295,6 +315,14 @@ export default class TokenCreateTransaction extends Transaction { if (props.maxSupply != null) { this.setMaxSupply(props.maxSupply); } + + if (props.metadataKey != null) { + this.setMetadataKey(props.metadataKey); + } + + if (props.metadata != null) { + this.setMetadata(props.metadata); + } } /** @@ -399,6 +427,11 @@ export default class TokenCreateTransaction extends Transaction { : undefined, maxSupply: create.maxSupply != null ? create.maxSupply : undefined, + metadataKey: + create.metadataKey != null + ? Key._fromProtobufKey(create.metadataKey) + : undefined, + metadata: create.metadata != null ? create.metadata : undefined, }), transactions, signedTransactions, @@ -657,7 +690,6 @@ export default class TokenCreateTransaction extends Transaction { */ setExpirationTime(time) { this._requireNotFrozen(); - this._autoRenewPeriod = null; this._expirationTime = time instanceof Timestamp ? time : Timestamp.fromDate(time); @@ -792,27 +824,39 @@ export default class TokenCreateTransaction extends Transaction { } /** - * @override - * @param {AccountId} accountId + * @returns {?Key} */ - _freezeWithAccountId(accountId) { - super._freezeWithAccountId(accountId); + get metadataKey() { + return this._metadataKey; + } - if (this._autoRenewPeriod != null && accountId != null) { - this._autoRenewAccountId = accountId; - } + /** + * @param {Key} key + * @returns {this} + */ + setMetadataKey(key) { + this._requireNotFrozen(); + this._metadataKey = key; + + return this; } /** - * @param {?import("../client/Client.js").default} client + * @returns {?Uint8Array} + */ + get metadata() { + return this._metadata; + } + + /** + * @param {Uint8Array} metadata * @returns {this} */ - freezeWith(client) { - if (client != null && client.operatorAccountId != null) { - this._freezeWithAccountId(client.operatorAccountId); - } + setMetadata(metadata) { + this._requireNotFrozen(); + this._metadata = metadata; - return super.freezeWith(client); + return this; } /** @@ -901,6 +945,11 @@ export default class TokenCreateTransaction extends Transaction { supplyType: this._supplyType != null ? this._supplyType._code : null, maxSupply: this.maxSupply, + metadataKey: + this._metadataKey != null + ? this._metadataKey._toProtobufKey() + : null, + metadata: this._metadata != null ? this._metadata : undefined, }; } diff --git a/src/token/TokenInfo.js b/src/token/TokenInfo.js index 692ec57f7..516858420 100644 --- a/src/token/TokenInfo.js +++ b/src/token/TokenInfo.js @@ -68,7 +68,9 @@ export default class TokenInfo { * @param {TokenType | null} props.tokenType; * @param {TokenSupplyType | null} props.supplyType; * @param {Long | null} props.maxSupply; - * @param {LedgerId|null} props.ledgerId + * @param {LedgerId|null} props.ledgerId; + * @param {Key | null} props.metadataKey; + * @param {Uint8Array | null} props.metadata; */ constructor(props) { /** @@ -238,6 +240,19 @@ export default class TokenInfo { this.maxSupply = props.maxSupply; this.ledgerId = props.ledgerId; + + /** + * @description The key which can change the metadata of a token (token definition and individual NFTs). + * + * @readonly + */ + this.metadataKey = props.metadataKey; + + /** + * @description Metadata of the created token definition. + * @readonly + */ + this.metadata = props.metadata; } /** @@ -361,6 +376,11 @@ export default class TokenInfo { info.ledgerId != null ? LedgerId.fromBytes(info.ledgerId) : null, + metadataKey: + info.metadataKey != null + ? Key._fromProtobufKey(info.metadataKey) + : null, + metadata: info.metadata != null ? info.metadata : new Uint8Array(), }); } @@ -426,6 +446,11 @@ export default class TokenInfo { supplyType: this.supplyType != null ? this.supplyType._code : null, maxSupply: this.maxSupply, ledgerId: this.ledgerId != null ? this.ledgerId.toBytes() : null, + metadataKey: + this.metadataKey != null + ? this.metadataKey._toProtobufKey() + : null, + metadata: this.metadata != null ? this.metadata : null, }; } diff --git a/src/token/TokenKeyValidation.js b/src/token/TokenKeyValidation.js new file mode 100644 index 000000000..031567e5e --- /dev/null +++ b/src/token/TokenKeyValidation.js @@ -0,0 +1,88 @@ +/*- + * ‌ + * Hedera JavaScript SDK + * ​ + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +/** + * @namespace proto + * @typedef {import("@hashgraph/proto").proto.TokenKeyValidation} HashgraphProto.proto.TokenKeyValidation + */ + +/** Types of validation strategies for token keys. */ +export default class TokenKeyValidation { + /** + * @hideconstructor + * @internal + * @param {number} code + */ + constructor(code) { + /** @readonly */ + this._code = code; + + Object.freeze(this); + } + + /** + * @returns {string} + */ + toString() { + switch (this) { + case TokenKeyValidation.FullValidation: + return "FULL_VALIDATION"; + case TokenKeyValidation.NoValidation: + return "NO_VALIDATION"; + default: + return `UNKNOWN (${this._code})`; + } + } + + /** + * @internal + * @param {number} code + * @returns {TokenKeyValidation} + */ + static _fromCode(code) { + switch (code) { + case 0: + return TokenKeyValidation.FullValidation; + case 1: + return TokenKeyValidation.NoValidation; + } + + throw new Error( + `(BUG) TokenKeyValidation.fromCode() does not handle code: ${code}`, + ); + } + + /** + * @returns {HashgraphProto.proto.TokenKeyValidation} + */ + valueOf() { + return this._code; + } +} + +/** + * Currently the default behaviour. It will perform all token key validations. + */ +TokenKeyValidation.FullValidation = new TokenKeyValidation(0); + +/** + * Perform no validations at all for all passed token keys. + */ +TokenKeyValidation.NoValidation = new TokenKeyValidation(1); diff --git a/src/token/TokenNftsUpdateTransaction.js b/src/token/TokenNftsUpdateTransaction.js new file mode 100644 index 000000000..78db5e48d --- /dev/null +++ b/src/token/TokenNftsUpdateTransaction.js @@ -0,0 +1,238 @@ +/*- + * ‌ + * Hedera JavaScript SDK + * ​ + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +import TokenId from "./TokenId.js"; +import Transaction, { + TRANSACTION_REGISTRY, +} from "../transaction/Transaction.js"; + +/** + * @namespace proto + * @typedef {import("@hashgraph/proto").proto.ITransaction} HashgraphProto.proto.ITransaction + * @typedef {import("@hashgraph/proto").proto.ISignedTransaction} HashgraphProto.proto.ISignedTransaction + * @typedef {import("@hashgraph/proto").proto.TransactionBody} HashgraphProto.proto.TransactionBody + * @typedef {import("@hashgraph/proto").proto.ITransactionBody} HashgraphProto.proto.ITransactionBody + * @typedef {import("@hashgraph/proto").proto.ITransactionResponse} HashgraphProto.proto.ITransactionResponse + * @typedef {import("@hashgraph/proto").proto.ITokenUpdateNftsTransactionBody} HashgraphProto.proto.ITokenUpdateNftsTransactionBody + * @typedef {import("@hashgraph/proto").proto.ITokenID} HashgraphProto.proto.ITokenID + */ + +/** + * @typedef {import("../channel/Channel.js").default} Channel + * @typedef {import("../client/Client.js").default<*, *>} Client + * @typedef {import("../transaction/TransactionId.js").default} TransactionId + * @typedef {import("../account/AccountId.js").default} AccountId + */ + +/** + * @deprecated - Use TokenUpdateNftsTransaction instead + */ +export default class TokenNftsUpdateTransaction extends Transaction { + /** + * @param {object} [props] + * @param {TokenId | string} [props.tokenId] + * @param {Long[]} [props.serialNumbers] + * @param {Uint8Array} [props.metadata] + */ + constructor(props = {}) { + super(); + + /** + * @private + * @type {?TokenId} + */ + this._tokenId = null; + + /** + * @private + * @type {?Long[]} + */ + this._serialNumbers = []; + + /** + * @private + * @type {?Uint8Array} + */ + this._metadata = null; + + if (props.tokenId != null) { + this.setTokenId(props.tokenId); + } + + if (props.serialNumbers != null) { + this.setSerialNumbers(props.serialNumbers); + } + + if (props.metadata != null) { + this.setMetadata(props.metadata); + } + } + + /** + * @internal + * @param {HashgraphProto.proto.ITransaction[]} transactions + * @param {HashgraphProto.proto.ISignedTransaction[]} signedTransactions + * @param {TransactionId[]} transactionIds + * @param {AccountId[]} nodeIds + * @param {HashgraphProto.proto.ITransactionBody[]} bodies + * @returns {TokenNftsUpdateTransaction} + */ + static _fromProtobuf( + transactions, + signedTransactions, + transactionIds, + nodeIds, + bodies, + ) { + const body = bodies[0]; + const tokenUpdate = + /** @type {HashgraphProto.proto.ITokenUpdateNftsTransactionBody} */ ( + body.tokenUpdate + ); + + return Transaction._fromProtobufTransactions( + // eslint-disable-next-line deprecation/deprecation + new TokenNftsUpdateTransaction({ + tokenId: + tokenUpdate.token != null + ? TokenId._fromProtobuf(tokenUpdate.token) + : undefined, + serialNumbers: + tokenUpdate.serialNumbers != null + ? tokenUpdate.serialNumbers + : [], + metadata: + tokenUpdate.metadata != null + ? tokenUpdate.metadata.value != null + ? tokenUpdate.metadata.value + : undefined + : undefined, + }), + transactions, + signedTransactions, + transactionIds, + nodeIds, + bodies, + ); + } + + /** + * @description Assign the token id. + * @param {TokenId | string} tokenId + * @returns {this} + */ + setTokenId(tokenId) { + this._requireNotFrozen(); + this._tokenId = + typeof tokenId === "string" + ? TokenId.fromString(tokenId) + : tokenId.clone(); + + return this; + } + + /** + * @description Assign the list of serial numbers. + * @param {Long[]} serialNumbers + * @returns {this} + */ + setSerialNumbers(serialNumbers) { + this._requireNotFrozen(); + this._serialNumbers = serialNumbers; + + return this; + } + + /** + * @param {Uint8Array} metadata + * @returns {this} + */ + setMetadata(metadata) { + this._requireNotFrozen(); + this._metadata = metadata; + + return this; + } + + /** + * @param {Client} client + */ + _validateChecksums(client) { + if (this._tokenId != null) { + this._tokenId.validateChecksum(client); + } + } + + /** + * @override + * @internal + * @param {Channel} channel + * @param {HashgraphProto.proto.ITransaction} request + * @returns {Promise} + */ + _execute(channel, request) { + return channel.token.pauseToken(request); + } + + /** + * @override + * @protected + * @returns {NonNullable} + */ + _getTransactionDataCase() { + return "tokenUpdateNfts"; + } + + /** + * @override + * @protected + * @returns {HashgraphProto.proto.ITokenUpdateNftsTransactionBody} + */ + _makeTransactionData() { + return { + token: this._tokenId != null ? this._tokenId._toProtobuf() : null, + serialNumbers: + this._serialNumbers != null ? this._serialNumbers : [], + ...(this._metadata != null + ? { + metadata: { + value: this._metadata, + }, + } + : null), + }; + } + + /** + * @returns {string} + */ + _getLogId() { + const timestamp = /** @type {import("../Timestamp.js").default} */ ( + this._transactionIds.current.validStart + ); + return `TokenNftsUpdateTransaction:${timestamp.toString()}`; + } +} + +TRANSACTION_REGISTRY.set( + "tokenUpdateNfts", + // eslint-disable-next-line deprecation/deprecation, @typescript-eslint/unbound-method + TokenNftsUpdateTransaction._fromProtobuf, +); diff --git a/src/token/TokenReference.js b/src/token/TokenReference.js new file mode 100644 index 000000000..8955acc09 --- /dev/null +++ b/src/token/TokenReference.js @@ -0,0 +1,40 @@ +import NftId from "./NftId.js"; +import TokenId from "./TokenId.js"; + +/** + * @namespace proto + * @typedef {import("@hashgraph/proto").proto.TokenReference} HashgraphProto.proto.TokenReference + */ + +export default class TokenReference { + constructor() { + /** + * @public + * @type {?TokenId} + */ + this.fungibleToken = null; + /** + * @public + * @type {?NftId} + */ + this.nft = null; + } + + /** + * @public + * @param {HashgraphProto.proto.TokenReference} reference + * @returns {TokenReference} + */ + static _fromProtobuf(reference) { + return { + fungibleToken: + reference.fungibleToken != undefined + ? TokenId._fromProtobuf(reference.fungibleToken) + : null, + nft: + reference.nft != undefined + ? NftId._fromProtobuf(reference.nft) + : null, + }; + } +} diff --git a/src/token/TokenRejectFlow.js b/src/token/TokenRejectFlow.js new file mode 100644 index 000000000..b583582e7 --- /dev/null +++ b/src/token/TokenRejectFlow.js @@ -0,0 +1,279 @@ +/*- + * ‌ + * Hedera JavaScript SDK + * ​ + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import TokenRejectTransaction from "../token/TokenRejectTransaction.js"; +import TokenDissociateTransaction from "../token/TokenDissociateTransaction.js"; + +/** + * @typedef {import("../PrivateKey.js").default} PrivateKey + * @typedef {import("../client/Client.js").default<*, *>} Client + * @typedef {import("../Signer.js").default} Signer + * @typedef {import("../transaction/TransactionId.js").default} TransactionId + * @typedef {import("../transaction/Transaction.js").default} Transaction + * @typedef {import("../transaction/TransactionResponse.js").default} TransactionResponse + * @typedef {import("../token/TokenId.js").default} TokenId + * @typedef {import("../token/NftId.js").default} NftId + * @typedef {import("../PublicKey.js").default} PublicKey + * @typedef {import("../account/AccountId.js").default} AccountId + */ + +/** + * Reject undesired token(s) and dissociate in a single flow. + */ +export default class TokenRejectFlow { + constructor() { + /** + * @private + * @type {?AccountId} + */ + this._ownerId = null; + + /** + * @private + * @type {TokenId[]} + */ + this._tokenIds = []; + + /** + * @private + * @type {NftId[]} + */ + this._nftIds = []; + + /** + * @private + * @type {?Client} + */ + this._freezeWithClient = null; + + /** + * @private + * @type {?PrivateKey} + */ + this._signPrivateKey = null; + + /** + * @private + * @type {?PublicKey} + */ + this._signPublicKey = null; + + /** + * @private + * @type {?(message: Uint8Array) => Promise} + */ + this._transactionSigner = null; + } + + /** + * + * @param {AccountId} ownerId + * @returns {this} + */ + setOwnerId(ownerId) { + this.requireNotFrozen(); + this._ownerId = ownerId; + return this; + } + + /** + * @returns {?AccountId} + */ + get ownerId() { + return this._ownerId; + } + + /** + * + * @param {TokenId[]} ids + * @returns {this} + */ + setTokenIds(ids) { + this.requireNotFrozen(); + this._tokenIds = ids; + return this; + } + + /** + * + * @param {TokenId} id + * @returns {this} + */ + addTokenId(id) { + this.requireNotFrozen(); + this._tokenIds.push(id); + return this; + } + + /** + * + * @returns {TokenId[]} + */ + get tokenIds() { + return this._tokenIds; + } + + /** + * + * @param {NftId[]} ids + * @returns {this} + */ + setNftIds(ids) { + this.requireNotFrozen(); + this._nftIds = ids; + return this; + } + + /** + * + * @param {NftId} id + * @returns {this} + */ + addNftId(id) { + this.requireNotFrozen(); + this._nftIds.push(id); + return this; + } + + /** + * + * @returns {NftId[]} + */ + get nftIds() { + return this._nftIds; + } + + /** + * + * @param {PrivateKey} privateKey + * @returns {this} + */ + sign(privateKey) { + this._signPrivateKey = privateKey; + this._signPublicKey = null; + this._transactionSigner = null; + return this; + } + + /** + * + * @param {PublicKey} publicKey + * @param {((message: Uint8Array) => Promise)} signer + * @returns {this} + */ + signWith(publicKey, signer) { + this._signPublicKey = publicKey; + this._transactionSigner = signer; + this._signPrivateKey = null; + return this; + } + + /** + * @param {Client} client + * @returns {this} + */ + signWithOperator(client) { + const operator = client.getOperator(); + if (operator == null) { + throw new Error("Client operator must be set"); + } + this._signPublicKey = operator.publicKey; + this._transactionSigner = operator.transactionSigner; + this._signPrivateKey = null; + return this; + } + + /** + * @private + * @param {Transaction} transaction + */ + fillOutTransaction(transaction) { + if (this._freezeWithClient) { + transaction.freezeWith(this._freezeWithClient); + } + if (this._signPrivateKey) { + void transaction.sign(this._signPrivateKey); + } else if (this._signPublicKey && this._transactionSigner) { + void transaction.signWith( + this._signPublicKey, + this._transactionSigner, + ); + } + } + /** + * + * @param {Client} client + * @returns {this} + */ + freezeWith(client) { + this._freezeWithClient = client; + return this; + } + + /** + * @param {Client} client + * @returns {Promise} + */ + async execute(client) { + const tokenRejectTxn = new TokenRejectTransaction() + .setTokenIds(this.tokenIds) + .setNftIds(this.nftIds); + + if (this.ownerId) { + tokenRejectTxn.setOwnerId(this.ownerId); + } + + this.fillOutTransaction(tokenRejectTxn); + + /* Get all token ids from NFT and remove duplicates as duplicated IDs + will trigger a TOKEN_REFERENCE_REPEATED error. */ + const nftTokenIds = this.nftIds + .map((nftId) => nftId.tokenId) + .filter(function (value, index, array) { + return array.indexOf(value) === index; + }); + + const tokenDissociateTxn = new TokenDissociateTransaction().setTokenIds( + [...this.tokenIds, ...nftTokenIds], + ); + + if (this.ownerId != null) { + tokenDissociateTxn.setAccountId(this.ownerId); + } + + this.fillOutTransaction(tokenDissociateTxn); + + const tokenRejectResponse = await tokenRejectTxn.execute(client); + await tokenRejectResponse.getReceipt(client); + + const tokenDissociateResponse = + await tokenDissociateTxn.execute(client); + await tokenDissociateResponse.getReceipt(client); + + return tokenRejectResponse; + } + + requireNotFrozen() { + if (this._freezeWithClient != null) { + throw new Error( + "Transaction is already frozen and cannot be modified", + ); + } + } +} diff --git a/src/token/TokenRejectTransaction.js b/src/token/TokenRejectTransaction.js new file mode 100644 index 000000000..9e5579714 --- /dev/null +++ b/src/token/TokenRejectTransaction.js @@ -0,0 +1,280 @@ +/*- + * ‌ + * Hedera JavaScript SDK + * ​ + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ +import AccountId from "../account/AccountId.js"; +import Transaction from "../transaction/Transaction.js"; +import { TRANSACTION_REGISTRY } from "../transaction/Transaction.js"; +import TokenReference from "../token/TokenReference.js"; + +/** + * @namespace proto + * @typedef {import("@hashgraph/proto").proto.ITransaction} HashgraphProto.proto.ITransaction + * @typedef {import("@hashgraph/proto").proto.ISignedTransaction} HashgraphProto.proto.ISignedTransaction + * @typedef {import("@hashgraph/proto").proto.ITransactionBody} HashgraphProto.proto.ITransactionBody + * @typedef {import("@hashgraph/proto").proto.ITransactionResponse} HashgraphProto.proto.ITransactionResponse + * @typedef {import("@hashgraph/proto").proto.TransactionBody} HashgraphProto.proto.TransactionBody + * @typedef {import("@hashgraph/proto").proto.ITokenRejectTransactionBody} HashgraphProto.proto.ITokenRejectTransactionBody + * @typedef {import("@hashgraph/proto").proto.TokenReference} HashgraphProto.proto.TokenReference + */ + +/** + * @typedef {import("../channel/Channel.js").default} Channel + * @typedef {import("../client/Client.js").default<*, *>} Client + * @typedef {import("../transaction/TransactionId.js").default} TransactionId + * @typedef {import("../token/TokenId.js").default} TokenId + * @typedef {import("../token/NftId.js").default} NftId + */ + +/** + * Reject a new Hedera™ crypto-currency token. + */ +export default class TokenRejectTransaction extends Transaction { + /** + * + * @param {object} [props] + * @param {?AccountId} [props.owner] + * @param {NftId[]} [props.nftIds] + * @param {TokenId[]} [props.tokenIds] + */ + constructor(props = {}) { + super(); + + /** + * @private + * @type {?AccountId} + */ + this._owner = null; + + if (props.owner != null) { + this.setOwnerId(props.owner); + } + + /** + * @private + * @type {TokenId[]} + */ + this._tokenIds = []; + + /** + * @private + * @type {NftId[]} + */ + this._nftIds = []; + + if (props.tokenIds != null) { + this.setTokenIds(props.tokenIds); + } + + if (props.nftIds != null) { + this.setNftIds(props.nftIds); + } + } + + /** + * @internal + * @param {HashgraphProto.proto.ITransaction[]} transactions + * @param {HashgraphProto.proto.ISignedTransaction[]} signedTransactions + * @param {TransactionId[]} transactionIds + * @param {AccountId[]} nodeIds + * @param {HashgraphProto.proto.ITransactionBody[]} bodies + * @returns {TokenRejectTransaction} + */ + static _fromProtobuf( + transactions, + signedTransactions, + transactionIds, + nodeIds, + bodies, + ) { + const body = bodies[0]; + const rejectToken = + /** @type {HashgraphProto.proto.ITokenRejectTransactionBody} */ ( + body.tokenReject + ); + + const tokenIds = rejectToken.rejections?.map((rejection) => + TokenReference._fromProtobuf(rejection), + ); + const ftIds = tokenIds + ?.filter((token) => token.fungibleToken) + .map(({ fungibleToken }) => { + if (fungibleToken == null) { + throw new Error("Fungible Token cannot be null"); + } + return fungibleToken; + }); + + const nftIds = tokenIds + ?.filter((token) => token.nft) + .map(({ nft }) => { + if (nft == null) { + throw new Error("Nft cannot be null"); + } + return nft; + }); + + return Transaction._fromProtobufTransactions( + new TokenRejectTransaction({ + owner: + rejectToken.owner != null + ? AccountId._fromProtobuf(rejectToken.owner) + : undefined, + + tokenIds: ftIds, + nftIds: nftIds, + }), + transactions, + signedTransactions, + transactionIds, + nodeIds, + bodies, + ); + } + + /** + * @returns {TokenId[]} + */ + get tokenIds() { + return this._tokenIds; + } + + /** + * @param {TokenId[]} tokenIds + * @returns {this} + */ + setTokenIds(tokenIds) { + this._requireNotFrozen(); + this._tokenIds = tokenIds; + return this; + } + + /** + * @param {TokenId} tokenId + * @returns {this} + */ + addTokenId(tokenId) { + this._requireNotFrozen(); + this._tokenIds?.push(tokenId); + return this; + } + + /** + * @returns {NftId[]} + * + */ + get nftIds() { + return this._nftIds; + } + + /** + * + * @param {NftId[]} nftIds + * @returns {this} + */ + setNftIds(nftIds) { + this._requireNotFrozen(); + this._nftIds = nftIds; + return this; + } + + /** + * @param {NftId} nftId + * @returns {this} + */ + addNftId(nftId) { + this._requireNotFrozen(); + this._nftIds?.push(nftId); + return this; + } + + /** + * @returns {?AccountId} + */ + get ownerId() { + return this._owner; + } + + /** + * @param {AccountId} owner + * @returns {this} + */ + setOwnerId(owner) { + this._requireNotFrozen(); + this._owner = owner; + return this; + } + + /** + * @override + * @internal + * @param {Channel} channel + * @param {HashgraphProto.proto.ITransaction} request + * @returns {Promise} + */ + _execute(channel, request) { + return channel.token.rejectToken(request); + } + + /** + * @override + * @protected + * @returns {NonNullable} + */ + _getTransactionDataCase() { + return "tokenReject"; + } + + /** + * @returns {HashgraphProto.proto.ITokenRejectTransactionBody} + */ + _makeTransactionData() { + /** @type {HashgraphProto.proto.TokenReference[]} */ + const rejections = []; + for (const tokenId of this._tokenIds) { + rejections.push({ + fungibleToken: tokenId._toProtobuf(), + }); + } + + for (const nftId of this._nftIds) { + rejections.push({ + nft: nftId._toProtobuf(), + }); + } + return { + owner: this.ownerId?._toProtobuf() ?? null, + rejections, + }; + } + + /** + * @returns {string} + */ + _getLogId() { + const timestamp = /** @type {import("../Timestamp.js").default} */ ( + this._transactionIds.current.validStart + ); + return `TokenRejectTransaction:${timestamp.toString()}`; + } +} +TRANSACTION_REGISTRY.set( + "tokenReject", + // eslint-disable-next-line @typescript-eslint/unbound-method + TokenRejectTransaction._fromProtobuf, +); diff --git a/src/token/TokenUpdateNftsTransaction.js b/src/token/TokenUpdateNftsTransaction.js new file mode 100644 index 000000000..0562d14d0 --- /dev/null +++ b/src/token/TokenUpdateNftsTransaction.js @@ -0,0 +1,234 @@ +/*- + * ‌ + * Hedera JavaScript SDK + * ​ + * Copyright (C) 2020 - 2023 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +import TokenId from "./TokenId.js"; +import Transaction, { + TRANSACTION_REGISTRY, +} from "../transaction/Transaction.js"; + +/** + * @namespace proto + * @typedef {import("@hashgraph/proto").proto.ITransaction} HashgraphProto.proto.ITransaction + * @typedef {import("@hashgraph/proto").proto.ISignedTransaction} HashgraphProto.proto.ISignedTransaction + * @typedef {import("@hashgraph/proto").proto.TransactionBody} HashgraphProto.proto.TransactionBody + * @typedef {import("@hashgraph/proto").proto.ITransactionBody} HashgraphProto.proto.ITransactionBody + * @typedef {import("@hashgraph/proto").proto.ITransactionResponse} HashgraphProto.proto.ITransactionResponse + * @typedef {import("@hashgraph/proto").proto.ITokenUpdateNftsTransactionBody} HashgraphProto.proto.ITokenUpdateNftsTransactionBody + * @typedef {import("@hashgraph/proto").proto.ITokenID} HashgraphProto.proto.ITokenID + */ + +/** + * @typedef {import("../channel/Channel.js").default} Channel + * @typedef {import("../client/Client.js").default<*, *>} Client + * @typedef {import("../transaction/TransactionId.js").default} TransactionId + * @typedef {import("../account/AccountId.js").default} AccountId + */ + +export default class TokenUpdateNftsTransaction extends Transaction { + /** + * @param {object} [props] + * @param {TokenId | string} [props.tokenId] + * @param {Long[]} [props.serialNumbers] + * @param {Uint8Array} [props.metadata] + */ + constructor(props = {}) { + super(); + + /** + * @private + * @type {?TokenId} + */ + this._tokenId = null; + + /** + * @private + * @type {?Long[]} + */ + this._serialNumbers = []; + + /** + * @private + * @type {?Uint8Array} + */ + this._metadata = null; + + if (props.tokenId != null) { + this.setTokenId(props.tokenId); + } + + if (props.serialNumbers != null) { + this.setSerialNumbers(props.serialNumbers); + } + + if (props.metadata != null) { + this.setMetadata(props.metadata); + } + } + + /** + * @internal + * @param {HashgraphProto.proto.ITransaction[]} transactions + * @param {HashgraphProto.proto.ISignedTransaction[]} signedTransactions + * @param {TransactionId[]} transactionIds + * @param {AccountId[]} nodeIds + * @param {HashgraphProto.proto.ITransactionBody[]} bodies + * @returns {TokenUpdateNftsTransaction} + */ + static _fromProtobuf( + transactions, + signedTransactions, + transactionIds, + nodeIds, + bodies, + ) { + const body = bodies[0]; + const tokenUpdate = + /** @type {HashgraphProto.proto.ITokenUpdateNftsTransactionBody} */ ( + body.tokenUpdate + ); + + return Transaction._fromProtobufTransactions( + new TokenUpdateNftsTransaction({ + tokenId: + tokenUpdate.token != null + ? TokenId._fromProtobuf(tokenUpdate.token) + : undefined, + serialNumbers: + tokenUpdate.serialNumbers != null + ? tokenUpdate.serialNumbers + : [], + metadata: + tokenUpdate.metadata != null + ? tokenUpdate.metadata.value != null + ? tokenUpdate.metadata.value + : undefined + : undefined, + }), + transactions, + signedTransactions, + transactionIds, + nodeIds, + bodies, + ); + } + + /** + * @description Assign the token id. + * @param {TokenId | string} tokenId + * @returns {this} + */ + setTokenId(tokenId) { + this._requireNotFrozen(); + this._tokenId = + typeof tokenId === "string" + ? TokenId.fromString(tokenId) + : tokenId.clone(); + + return this; + } + + /** + * @description Assign the list of serial numbers. + * @param {Long[]} serialNumbers + * @returns {this} + */ + setSerialNumbers(serialNumbers) { + this._requireNotFrozen(); + this._serialNumbers = serialNumbers; + + return this; + } + + /** + * @param {Uint8Array} metadata + * @returns {this} + */ + setMetadata(metadata) { + this._requireNotFrozen(); + this._metadata = metadata; + + return this; + } + + /** + * @param {Client} client + */ + _validateChecksums(client) { + if (this._tokenId != null) { + this._tokenId.validateChecksum(client); + } + } + + /** + * @override + * @internal + * @param {Channel} channel + * @param {HashgraphProto.proto.ITransaction} request + * @returns {Promise} + */ + _execute(channel, request) { + return channel.token.pauseToken(request); + } + + /** + * @override + * @protected + * @returns {NonNullable} + */ + _getTransactionDataCase() { + return "tokenUpdateNfts"; + } + + /** + * @override + * @protected + * @returns {HashgraphProto.proto.ITokenUpdateNftsTransactionBody} + */ + _makeTransactionData() { + return { + token: this._tokenId != null ? this._tokenId._toProtobuf() : null, + serialNumbers: + this._serialNumbers != null ? this._serialNumbers : [], + ...(this._metadata != null + ? { + metadata: { + value: this._metadata, + }, + } + : null), + }; + } + + /** + * @returns {string} + */ + _getLogId() { + const timestamp = /** @type {import("../Timestamp.js").default} */ ( + this._transactionIds.current.validStart + ); + return `TokenUpdateNftsTransaction:${timestamp.toString()}`; + } +} + +TRANSACTION_REGISTRY.set( + "tokenUpdateNfts", + // eslint-disable-next-line @typescript-eslint/unbound-method + TokenUpdateNftsTransaction._fromProtobuf, +); diff --git a/src/token/TokenUpdateTransaction.js b/src/token/TokenUpdateTransaction.js index 15cd07281..bcf2b9f64 100644 --- a/src/token/TokenUpdateTransaction.js +++ b/src/token/TokenUpdateTransaction.js @@ -26,6 +26,7 @@ import AccountId from "../account/AccountId.js"; import Timestamp from "../Timestamp.js"; import Duration from "../Duration.js"; import Key from "../Key.js"; +import TokenKeyValidation from "./TokenKeyValidation.js"; /** * @namespace proto @@ -66,6 +67,9 @@ export default class TokenUpdateTransaction extends Transaction { * @param {string} [props.tokenMemo] * @param {Key} [props.feeScheduleKey] * @param {Key} [props.pauseKey] + * @param {Key} [props.metadataKey] + * @param {Uint8Array} [props.metadata] + * @param {TokenKeyValidation} [props.keyVerificationMode] */ constructor(props = {}) { super(); @@ -160,6 +164,26 @@ export default class TokenUpdateTransaction extends Transaction { */ this._pauseKey = null; + /** + * @private + * @type {?Key} + */ + this._metadataKey = null; + + /** + * @private + * @type {?Uint8Array} + */ + this._metadata = null; + + /** + * @private + * @type {?TokenKeyValidation} + * Determines whether the system should check the validity of the passed keys for update. + * Defaults to FULL_VALIDATION + */ + this._keyVerificationMode = TokenKeyValidation.FullValidation; + if (props.tokenId != null) { this.setTokenId(props.tokenId); } @@ -219,6 +243,18 @@ export default class TokenUpdateTransaction extends Transaction { if (props.pauseKey != null) { this.setPauseKey(props.pauseKey); } + + if (props.metadataKey != null) { + this.setMetadataKey(props.metadataKey); + } + + if (props.metadata != null) { + this.setMetadata(props.metadata); + } + + if (props.keyVerificationMode != null) { + this.setKeyVerificationMode(props.keyVerificationMode); + } } /** @@ -301,6 +337,22 @@ export default class TokenUpdateTransaction extends Transaction { update.pauseKey != null ? Key._fromProtobufKey(update.pauseKey) : undefined, + metadataKey: + update.metadataKey != null + ? Key._fromProtobufKey(update.metadataKey) + : undefined, + metadata: + update.metadata != null + ? update.metadata.value != null + ? update.metadata.value + : undefined + : undefined, + keyVerificationMode: + update.keyVerificationMode != null + ? TokenKeyValidation._fromCode( + update.keyVerificationMode, + ) + : undefined, }), transactions, signedTransactions, @@ -602,6 +654,60 @@ export default class TokenUpdateTransaction extends Transaction { return this; } + /** + * @returns {?Key} + */ + get metadataKey() { + return this._metadataKey; + } + + /** + * @param {Key} metadataKey + * @returns {this} + */ + setMetadataKey(metadataKey) { + this._requireNotFrozen(); + this._metadataKey = metadataKey; + + return this; + } + + /** + * @returns {?Uint8Array} + */ + get metadata() { + return this._metadata; + } + + /** + * @param {Uint8Array} metadata + * @returns {this} + */ + setMetadata(metadata) { + this._requireNotFrozen(); + this._metadata = metadata; + + return this; + } + + /** + * @returns {?TokenKeyValidation} + */ + get keyVerificationMode() { + return this._keyVerificationMode; + } + + /** + * @param {TokenKeyValidation} keyVerificationMode + * @returns {this} + */ + setKeyVerificationMode(keyVerificationMode) { + this._requireNotFrozen(); + this._keyVerificationMode = keyVerificationMode; + + return this; + } + /** * @returns {this} */ @@ -700,6 +806,20 @@ export default class TokenUpdateTransaction extends Transaction { this._feeScheduleKey != null ? this._feeScheduleKey._toProtobufKey() : null, + metadataKey: + this._metadataKey != null + ? this._metadataKey._toProtobufKey() + : null, + metadata: + this._metadata != null + ? { + value: this._metadata, + } + : null, + keyVerificationMode: + this._keyVerificationMode != null + ? this._keyVerificationMode._code + : undefined, }; } diff --git a/src/token/TokenWipeTransaction.js b/src/token/TokenWipeTransaction.js index 86dfdaebf..d4c7201e7 100644 --- a/src/token/TokenWipeTransaction.js +++ b/src/token/TokenWipeTransaction.js @@ -70,7 +70,7 @@ export default class TokenWipeTransaction extends Transaction { /** * @private - * @type {Long[]} + * @type {?Long[]} */ this._serials = []; @@ -217,7 +217,7 @@ export default class TokenWipeTransaction extends Transaction { } /** - * @returns {Long[]} + * @returns {?Long[]} */ get serials() { return this._serials; diff --git a/src/transaction/Transaction.js b/src/transaction/Transaction.js index 5da957c23..e08a0d36e 100644 --- a/src/transaction/Transaction.js +++ b/src/transaction/Transaction.js @@ -1231,7 +1231,7 @@ export default class Transaction extends Executable { } /** - * Before we proceed exeuction, we need to do a couple checks + * Before we proceed execution, we need to do a couple checks * * @override * @protected @@ -1513,10 +1513,11 @@ export default class Transaction extends Executable { * @internal * @param {HashgraphProto.proto.ITransaction} request * @param {HashgraphProto.proto.ITransactionResponse} response + * @param {AccountId} nodeId * @returns {Error} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - _mapStatusError(request, response) { + _mapStatusError(request, response, nodeId) { const { nodeTransactionPrecheckCode } = response; const status = Status._fromCode( @@ -1532,6 +1533,7 @@ export default class Transaction extends Executable { } return new PrecheckStatusError({ + nodeId, status, transactionId: this._getTransactionId(), contractFunctionResult: null, diff --git a/src/transaction/TransactionReceiptQuery.js b/src/transaction/TransactionReceiptQuery.js index 0f747eb91..07983ee4e 100644 --- a/src/transaction/TransactionReceiptQuery.js +++ b/src/transaction/TransactionReceiptQuery.js @@ -221,6 +221,7 @@ export default class TransactionReceiptQuery extends Query { case Status.Busy: case Status.Unknown: case Status.ReceiptNotFound: + case Status.PlatformNotActive: return [status, ExecutionState.Retry]; case Status.Ok: break; @@ -282,10 +283,11 @@ export default class TransactionReceiptQuery extends Query { * @internal * @param {HashgraphProto.proto.IQuery} request * @param {HashgraphProto.proto.IResponse} response + * @param {AccountId} nodeId * @returns {Error} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - _mapStatusError(request, response) { + _mapStatusError(request, response, nodeId) { const { nodeTransactionPrecheckCode } = this._mapResponseHeader(response); @@ -302,6 +304,7 @@ export default class TransactionReceiptQuery extends Query { default: return new PrecheckStatusError({ + nodeId, status, transactionId: this._getTransactionId(), contractFunctionResult: null, diff --git a/src/transaction/TransactionRecordQuery.js b/src/transaction/TransactionRecordQuery.js index d4b677990..118371a0d 100644 --- a/src/transaction/TransactionRecordQuery.js +++ b/src/transaction/TransactionRecordQuery.js @@ -212,6 +212,7 @@ export default class TransactionRecordQuery extends Query { case Status.Unknown: case Status.ReceiptNotFound: case Status.RecordNotFound: + case Status.PlatformNotActive: return [status, ExecutionState.Retry]; case Status.Ok: @@ -281,10 +282,11 @@ export default class TransactionRecordQuery extends Query { * @internal * @param {HashgraphProto.proto.IQuery} request * @param {HashgraphProto.proto.IResponse} response + * @param {AccountId} nodeId * @returns {Error} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - _mapStatusError(request, response) { + _mapStatusError(request, response, nodeId) { const { nodeTransactionPrecheckCode } = this._mapResponseHeader(response); @@ -311,6 +313,7 @@ export default class TransactionRecordQuery extends Query { default: return new PrecheckStatusError({ + nodeId, status, transactionId: this._getTransactionId(), contractFunctionResult: null, @@ -412,7 +415,6 @@ export default class TransactionRecordQuery extends Query { /** @type {HashgraphProto.proto.ITransactionGetRecordResponse} */ ( response.transactionGetRecord ); - return Promise.resolve(TransactionRecord._fromProtobuf(record)); } diff --git a/test/integration/AccountBalanceIntegrationTest.js b/test/integration/AccountBalanceIntegrationTest.js index f2be8c9ca..28f994f04 100644 --- a/test/integration/AccountBalanceIntegrationTest.js +++ b/test/integration/AccountBalanceIntegrationTest.js @@ -1,7 +1,7 @@ import { AccountBalanceQuery, Status, - // TokenCreateTransaction, + TokenCreateTransaction, } from "../../src/exports.js"; import IntegrationTestEnv, { Client, @@ -85,32 +85,27 @@ describe("AccountBalanceQuery", function () { } }); - /** - * - * @description The test is temporarily commented because AccountBalanceQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should reflect token with no keys", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setTreasuryAccountId(operatorId) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // const balances = await new AccountBalanceQuery() - // .setAccountId(env.operatorId) - // .execute(env.client); - - // expect(balances.tokens.get(token).toInt()).to.be.equal(0); - // }); + it("should reflect token with no keys", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + const balances = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect(balances.tokens.get(token).toInt()).to.be.equal(0); + }); after(async function () { clientPreviewNet.close(); diff --git a/test/integration/AccountCreateIntegrationTest.js b/test/integration/AccountCreateIntegrationTest.js index a8060cb2c..2f150ada4 100644 --- a/test/integration/AccountCreateIntegrationTest.js +++ b/test/integration/AccountCreateIntegrationTest.js @@ -141,8 +141,7 @@ describe("AccountCreate", function () { it("should error when key is not set", async function () { this.timeout(15000); - - let err = false; + let status; try { const response = await new AccountCreateTransaction() @@ -151,12 +150,10 @@ describe("AccountCreate", function () { await response.getReceipt(env.client); } catch (error) { - err = error.toString().includes(Status.KeyRequired.toString()); + status = error.status; } - if (!err) { - throw new Error("account creation did not error"); - } + expect(status).to.be.eql(Status.KeyRequired); }); it("should be able to sign transaction and verify transaction signtatures", async function () { @@ -203,6 +200,7 @@ describe("AccountCreate", function () { }); it("should create account with a single key passed to `KeyList`", async function () { + this.timeout(15000); const publicKey = PrivateKey.generateED25519().publicKey; const thresholdKey = new KeyList(publicKey, 1); diff --git a/test/integration/AccountDeleteIntegrationTest.js b/test/integration/AccountDeleteIntegrationTest.js index acba5dc3f..abea8abd2 100644 --- a/test/integration/AccountDeleteIntegrationTest.js +++ b/test/integration/AccountDeleteIntegrationTest.js @@ -95,8 +95,7 @@ describe("AccountDelete", function () { it("should error with no account ID set", async function () { this.timeout(120000); - - let err = false; + let status; try { await ( @@ -106,14 +105,10 @@ describe("AccountDelete", function () { .execute(env.client) ).getReceipt(env.client); } catch (error) { - err = error - .toString() - .includes(Status.AccountIdDoesNotExist.toString()); + status = error.status; } - if (!err) { - throw new Error("account deletion did not error"); - } + expect(status).to.be.eql(Status.AccountIdDoesNotExist); }); after(async function () { diff --git a/test/integration/AccountInfoIntegrationTest.js b/test/integration/AccountInfoIntegrationTest.js index ea6cfbbd5..4ee2a083e 100644 --- a/test/integration/AccountInfoIntegrationTest.js +++ b/test/integration/AccountInfoIntegrationTest.js @@ -5,7 +5,7 @@ import { Hbar, PrivateKey, Status, - // TokenCreateTransaction, + TokenCreateTransaction, TransactionId, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -154,38 +154,33 @@ describe("AccountInfo", function () { } }); - /** - * - * @description The test is temporarily commented because AccountInfoQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should reflect token with no keys", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setTreasuryAccountId(operatorId) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // const info = await new AccountInfoQuery() - // .setAccountId(operatorId) - // .execute(env.client); - - // const relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.null; - // expect(relationship.isFrozen).to.be.null; - // }); + it("should reflect token with no keys", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + const info = await new AccountInfoQuery() + .setAccountId(operatorId) + .execute(env.client); + + const relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.null; + expect(relationship.isFrozen).to.be.null; + }); it("should be error with no account ID", async function () { this.timeout(120000); diff --git a/test/integration/AccountUpdateIntegrationTest.js b/test/integration/AccountUpdateIntegrationTest.js index 512e5a237..bcc9bb7de 100644 --- a/test/integration/AccountUpdateIntegrationTest.js +++ b/test/integration/AccountUpdateIntegrationTest.js @@ -16,6 +16,7 @@ describe("AccountUpdate", function () { let env; before(async function () { + this.timeout(120000); env = await IntegrationTestEnv.new(); }); @@ -189,8 +190,7 @@ describe("AccountUpdate", function () { it("should error when account ID is not set", async function () { this.timeout(120000); - - let err = false; + let status; try { await ( @@ -199,14 +199,10 @@ describe("AccountUpdate", function () { .execute(env.client) ).getReceipt(env.client); } catch (error) { - err = error - .toString() - .includes(Status.AccountIdDoesNotExist.toString()); + status = error.status; } - if (!err) { - throw new Error("account update did not error"); - } + expect(status).to.be.eql(Status.AccountIdDoesNotExist); }); it("should execute with only account ID", async function () { diff --git a/test/integration/ClientIntegrationTest.js b/test/integration/ClientIntegrationTest.js index 117278c79..204faaa38 100644 --- a/test/integration/ClientIntegrationTest.js +++ b/test/integration/ClientIntegrationTest.js @@ -172,6 +172,24 @@ describe("ClientIntegration", function () { expect(clientTestnet.isTransportSecurity()).to.be.an("boolean"); }); + it("should return the following error message `defaultMaxQueryPayment must be non-negative` when the user tries to set a negative value to the defaultMaxQueryPayment field", async function () { + this.timeout(120000); + try { + env.client.setDefaultMaxQueryPayment(new Hbar(1).negated()); + } catch (error) { + expect(error.message).to.be.equal( + "defaultMaxQueryPayment must be non-negative", + ); + } + }); + + it("should set defaultMaxQueryPayment field", async function () { + this.timeout(120000); + const value = new Hbar(100); + env.client.setDefaultMaxQueryPayment(value); + expect(env.client.defaultMaxQueryPayment).to.be.equal(value); + }); + after(async function () { await env.close(); clientTestnet.close(); diff --git a/test/integration/ContractCallIntegrationTest.js b/test/integration/ContractCallIntegrationTest.js index ff5798b04..253d917e7 100644 --- a/test/integration/ContractCallIntegrationTest.js +++ b/test/integration/ContractCallIntegrationTest.js @@ -8,15 +8,11 @@ import { FileDeleteTransaction, Hbar, Status, - PrivateKey, - FileAppendTransaction, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; const smartContractBytecode = "608060405234801561001057600080fd5b506040516104d73803806104d78339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b0319163317905550805161010890600190602084019061010f565b50506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061015057805160ff191683800117855561017d565b8280016001018555821561017d579182015b8281111561017d578251825591602001919060010190610162565b5061018992915061018d565b5090565b6101a791905b808211156101895760008155600101610193565b90565b61031e806101b96000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063368b87721461004657806341c0e1b5146100ee578063ce6d41de146100f6575b600080fd5b6100ec6004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610173945050505050565b005b6100ec6101a2565b6100fe6101ba565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610138578181015183820152602001610120565b50505050905090810190601f1680156101655780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000546001600160a01b0316331461018a5761019f565b805161019d906001906020840190610250565b505b50565b6000546001600160a01b03163314156101b85733ff5b565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156102455780601f1061021a57610100808354040283529160200191610245565b820191906000526020600020905b81548152906001019060200180831161022857829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061029157805160ff19168380011785556102be565b828001600101855582156102be579182015b828111156102be5782518255916020019190600101906102a3565b506102ca9291506102ce565b5090565b61024d91905b808211156102ca57600081556001016102d456fea264697066735822122084964d4c3f6bc912a9d20e14e449721012d625aa3c8a12de41ae5519752fc89064736f6c63430006000033"; -const readDataBytecode = - "0x608060405234801561001057600080fd5b5061026c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806304806dd61461003b5780634278774714610064575b600080fd5b61004e610049366004610178565b610084565b60405161005b91906101a2565b60405180910390f35b610077610072366004610178565b610101565b60405161005b91906101f1565b606060008262ffffff1667ffffffffffffffff8111156100b457634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156100f957816020015b60408051808201909152600080825260208201528152602001906001900390816100d25790505b509392505050565b606060008262ffffff1667ffffffffffffffff81111561013157634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156100f957816020015b60408051602081019091526000815281526020019060019003908161014f579050509392505050565b600060208284031215610189578081fd5b813562ffffff8116811461019b578182fd5b9392505050565b602080825282518282018190526000919060409081850190868401855b828110156101e4578151805185528601518685015292840192908501906001016101bf565b5091979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561022a578351518352928401929184019160010161020d565b5090969550505050505056fea26469706673582212201dc78aeb6e1955ac889c23cf72d0595af987863764ccb6270c7825992093969264736f6c63430008040033"; describe("ContractCallIntegration", function () { let env; @@ -316,87 +312,65 @@ describe("ContractCallIntegration", function () { } }); - it("should mark as busy when network node takes longer than 10s to execute the transaction", async function () { + it("should return error when the gas is not set", async function () { this.timeout(120000); - const myPrivateKey = PrivateKey.generateED25519(); - const env = await IntegrationTestEnv.new(); - const client = env.client; - // Create a file on Hedera and store the bytecode - const fileCreateTx = new FileCreateTransaction() - .setKeys([myPrivateKey]) - .freezeWith(client); - const fileCreateSign = await fileCreateTx.sign(myPrivateKey); - const fileCreateSubmit = await fileCreateSign.execute(client); - const fileCreateRx = await fileCreateSubmit.getReceipt(client); - const bytecodeFileId = fileCreateRx.fileId; - console.log(`The bytecode file ID is: ${bytecodeFileId} \n`); - - //Append contents to the file - const fileAppendTx = new FileAppendTransaction() - .setFileId(bytecodeFileId) - .setContents(readDataBytecode) - .setMaxChunks(10) - .freezeWith(client); - const fileAppendSign = await fileAppendTx.sign(myPrivateKey); - const fileAppendSubmit = await fileAppendSign.execute(client); - const fileAppendRx = await fileAppendSubmit.getReceipt(client); - console.log("Status of file append is", fileAppendRx.status.toString()); - - // Instantiate the contract instance - const contractTx = await new ContractCreateTransaction() - //Set the file ID of the Hedera file storing the bytecode - .setBytecodeFileId(bytecodeFileId) - //Set the gas to instantiate the contract - .setGas(100000) - //Provide the constructor parameters for the contract - .setConstructorParameters(); - - //Submit the transaction to the Hedera test network - const contractResponse = await contractTx.execute(client); - - //Get the receipt of the file create transaction - const contractReceipt = await contractResponse.getReceipt(client); - - expect(contractReceipt.contractId).to.not.be.null; - expect( - contractReceipt.contractId != null - ? contractReceipt.contractId.num > 0 - : false, - ).to.be.true; - - const contractId = contractReceipt.contractId; + const operatorKey = env.operatorKey.publicKey; - let err = false; - try { - const contractQuery = await new ContractCallQuery() - //Set the gas for the query - .setGas(16000000) - //Set the contract ID to return the request for - .setContractId(contractId) - //Set the contract function to call - .setFunction( - "getLotsOfData", - new ContractFunctionParameters().addUint24(17000), + const response = await new FileCreateTransaction() + .setKeys([operatorKey]) + .setContents(smartContractBytecode) + .execute(env.client); + + let receipt = await response.getReceipt(env.client); + + expect(receipt.fileId).to.not.be.null; + expect(receipt.fileId != null ? receipt.fileId.num > 0 : false).to.be + .true; + + const file = receipt.fileId; + + receipt = await ( + await new ContractCreateTransaction() + .setAdminKey(operatorKey) + .setGas(200000) + .setConstructorParameters( + new ContractFunctionParameters().addString( + "Hello from Hedera.", + ), ) - //Set the query payment for the node returning the request - //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(35)); - - //Submit to a Hedera network - // const txResponse = await contractQuery.execute(client); - // const txResponse2 = await contractQuery2.execute(client); - const txResponse = await contractQuery.execute(client); - console.log("Res:", txResponse.getUint32(1)); + .setBytecodeFileId(file) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(env.client) + ).getReceipt(env.client); + + expect(receipt.contractId).to.not.be.null; + expect(receipt.contractId != null ? receipt.contractId.num > 0 : false) + .to.be.true; + + const contract = receipt.contractId; + + try { + await new ContractCallQuery() + .setContractId(contract) + .setFunction("getMessage") + .execute(env.client); } catch (error) { - err = error; + expect(error.status).to.be.eql(Status.InsufficientGas); } - expect(err.toString()).to.includes("BUSY"); - if (!err) { - throw new Error("query did not error"); - } - await client.close(); + await ( + await new ContractDeleteTransaction() + .setContractId(contract) + .setTransferAccountId(env.client.operatorAccountId) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await new FileDeleteTransaction() + .setFileId(file) + .execute(env.client) + ).getReceipt(env.client); }); after(async function () { diff --git a/test/integration/ContractCreateIntegrationTest.js b/test/integration/ContractCreateIntegrationTest.js index 922141fa2..6e00e2c6b 100644 --- a/test/integration/ContractCreateIntegrationTest.js +++ b/test/integration/ContractCreateIntegrationTest.js @@ -90,79 +90,6 @@ describe("ContractCreate", function () { ).getReceipt(env.client); }); - it("should be able to create without admin key", async function () { - this.timeout(120000); - - const operatorKey = env.operatorKey.publicKey; - - let response = await new FileCreateTransaction() - .setKeys([operatorKey]) - .setContents(smartContractBytecode) - .execute(env.client); - - let receipt = await response.getReceipt(env.client); - - expect(receipt.fileId).to.not.be.null; - expect(receipt.fileId != null ? receipt.fileId.num > 0 : false).to.be - .true; - - const file = receipt.fileId; - - response = await new ContractCreateTransaction() - .setGas(200000) - .setConstructorParameters( - new ContractFunctionParameters().addString( - "Hello from Hedera.", - ), - ) - .setBytecodeFileId(file) - .setContractMemo("[e2e::ContractCreateTransaction]") - .execute(env.client); - - receipt = await response.getReceipt(env.client); - - expect(receipt.contractId).to.not.be.null; - expect(receipt.contractId != null ? receipt.contractId.num > 0 : false) - .to.be.true; - - let contract = receipt.contractId; - - let info = await new ContractInfoQuery() - .setContractId(contract) - .setQueryPayment(new Hbar(1)) - .execute(env.client); - - expect(info.contractId.toString()).to.be.equal(contract.toString()); - expect(info.accountId).to.be.not.null; - expect( - info.contractId != null ? info.contractId.toString() : "", - ).to.be.equal(contract.toString()); - expect(info.adminKey).to.be.not.null; - // expect(info.adminKey.toString()).to.be.equal( - // info.contractId.toString() - // ); - expect(info.storage.toInt()).to.be.equal(128); - expect(info.contractMemo).to.be.equal( - "[e2e::ContractCreateTransaction]", - ); - - let err = false; - - try { - await ( - await new ContractDeleteTransaction() - .setContractId(contract) - .execute(env.client) - ).getReceipt(env.client); - } catch (error) { - err = error.toString().includes(Status.ModifyingImmutableContract); - } - - if (!err) { - throw new Error("contract deletion did not error"); - } - }); - it("should error when gas is not set", async function () { this.timeout(120000); diff --git a/test/integration/ContractDeleteIntegrationTest.js b/test/integration/ContractDeleteIntegrationTest.js index 65ef2f420..7b10b278f 100644 --- a/test/integration/ContractDeleteIntegrationTest.js +++ b/test/integration/ContractDeleteIntegrationTest.js @@ -140,6 +140,76 @@ describe("ContractDelete", function () { throw new Error("contact deletion did not error"); } }); + + it("should create contract without admin key which can NOT be deleted", async function () { + this.timeout(120000); + + const operatorKey = env.operatorKey.publicKey; + + let response = await new FileCreateTransaction() + .setKeys([operatorKey]) + .setContents(smartContractBytecode) + .execute(env.client); + + let receipt = await response.getReceipt(env.client); + + expect(receipt.fileId).to.not.be.null; + expect(receipt.fileId != null ? receipt.fileId.num > 0 : false).to.be + .true; + + const file = receipt.fileId; + + response = await new ContractCreateTransaction() + .setGas(200000) + .setConstructorParameters( + new ContractFunctionParameters().addString( + "Hello from Hedera.", + ), + ) + .setBytecodeFileId(file) + .setContractMemo("[e2e::ContractCreateTransaction]") + .execute(env.client); + + receipt = await response.getReceipt(env.client); + + expect(receipt.contractId).to.not.be.null; + expect(receipt.contractId != null ? receipt.contractId.num > 0 : false) + .to.be.true; + + let contract = receipt.contractId; + + let info = await new ContractInfoQuery() + .setContractId(contract) + .setQueryPayment(new Hbar(1)) + .execute(env.client); + + expect(info.contractId.toString()).to.be.equal(contract.toString()); + expect(info.accountId).to.be.not.null; + expect( + info.contractId != null ? info.contractId.toString() : "", + ).to.be.equal(contract.toString()); + expect(info.adminKey).to.be.not.null; + expect(info.storage.toInt()).to.be.equal(128); + expect(info.contractMemo).to.be.equal( + "[e2e::ContractCreateTransaction]", + ); + + let status; + + try { + await ( + await new ContractDeleteTransaction() + .setContractId(contract) + .setTransferAccountId(env.client.operatorAccountId) + .execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.equal(Status.ModifyingImmutableContract); + }); + after(async function () { await env.close(); }); diff --git a/test/integration/ContractFunctionParametersIntegrationTest.js b/test/integration/ContractFunctionParametersIntegrationTest.js index 91d510f98..990919d2a 100644 --- a/test/integration/ContractFunctionParametersIntegrationTest.js +++ b/test/integration/ContractFunctionParametersIntegrationTest.js @@ -122,7 +122,8 @@ describe("ContractFunctionParameters", function () { let newContractId; before(async function () { - env = await IntegrationTestEnv.new({ balance: 10000 }); + this.timeout(120000); + env = await IntegrationTestEnv.new({ balance: 100000 }); // Create a file on Hedera and store the bytecode const fileCreateTx = new FileCreateTransaction() .setKeys([env.operatorKey]) @@ -148,7 +149,7 @@ describe("ContractFunctionParameters", function () { ); // Instantiate the contract instance - const contractTx = await new ContractCreateTransaction() + const contractTx = new ContractCreateTransaction() //Set the file ID of the Hedera file storing the bytecode .setBytecodeFileId(bytecodeFileId) //Set the gas to instantiate the contract @@ -171,6 +172,7 @@ describe("ContractFunctionParameters", function () { bitSizes.forEach((bitSize) => { describe(`Tests for addInt${bitSize} method`, function () { + this.timeout(120000); it( getDescription( EXTREMUM.MIN, @@ -179,9 +181,10 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { - const contractQuery = await new ContractCallQuery() + this.timeout(120000); + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -193,7 +196,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -213,9 +216,10 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { - const contractQuery = await new ContractCallQuery() + this.timeout(120000); + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -227,7 +231,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -248,9 +252,10 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { - const contractQuery = await new ContractCallQuery() + this.timeout(120000); + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -262,7 +267,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -281,9 +286,10 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { - const contractQuery = await new ContractCallQuery() + this.timeout(120000); + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -295,7 +301,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -317,9 +323,10 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { - const contractQuery = await new ContractCallQuery() + this.timeout(120000); + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -331,7 +338,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -352,9 +359,10 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { - const contractQuery = await new ContractCallQuery() + this.timeout(120000); + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -366,7 +374,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -380,6 +388,7 @@ describe("ContractFunctionParameters", function () { }); describe(`Tests for addInt${bitSize}Array method`, function () { + this.timeout(120000); it( getDescriptionForArrayMethod( bitSize, @@ -387,10 +396,11 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.INT, ), async function () { + this.timeout(120000); const arr = createArray(bitSize, INPUT_TYPE.NUMBER); - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -402,7 +412,8 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); + //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); const result = txResponse.getResult([`int${bitSize}[]`])[0]; @@ -424,9 +435,9 @@ describe("ContractFunctionParameters", function () { ), async function () { const arr = createArray(bitSize, INPUT_TYPE.BIG_NUMBER); - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -438,7 +449,8 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); + //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); const result = txResponse.getResult([`int${bitSize}[]`])[0]; @@ -460,9 +472,9 @@ describe("ContractFunctionParameters", function () { ), async function () { const arr = createArray(bitSize, INPUT_TYPE.LONG); - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -474,7 +486,8 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); + //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); const result = txResponse.getResult([`int${bitSize}[]`])[0]; @@ -489,9 +502,9 @@ describe("ContractFunctionParameters", function () { ); it(`addInt${bitSize}Array method should return an empty array`, async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -506,7 +519,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -516,9 +529,9 @@ describe("ContractFunctionParameters", function () { it(`addInt${bitSize}Array method should throw an error`, async function () { try { - await new ContractCallQuery() + new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -530,7 +543,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); } catch (error) { expect(error).to.be.instanceOf(Error); expect(error.message).to.be.equal(REQUIRE_ARRAY_ERROR); @@ -547,9 +560,9 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.UINT, ), async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -561,7 +574,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -582,9 +595,9 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.UINT, ), async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -596,7 +609,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -618,9 +631,9 @@ describe("ContractFunctionParameters", function () { METHOD_TYPE.UINT, ), async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -632,7 +645,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -655,9 +668,9 @@ describe("ContractFunctionParameters", function () { async function () { const range = calculateRange(bitSize, INPUT_TYPE.NUMBER); const arr = [0, range.min + range.max]; - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -669,7 +682,8 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); + //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); const result = txResponse.getResult([ @@ -692,9 +706,9 @@ describe("ContractFunctionParameters", function () { ), async function () { const arr = [0, new BigNumber(2).pow(bitSize - 1).minus(1)]; - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -706,7 +720,8 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); + //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); const result = txResponse.getResult([ @@ -730,9 +745,9 @@ describe("ContractFunctionParameters", function () { async function () { const range = calculateRange(bitSize, INPUT_TYPE.NUMBER); const arr = [0, new Long(range.min + range.max)]; - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -744,7 +759,8 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); + //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); const result = txResponse.getResult([ @@ -761,9 +777,9 @@ describe("ContractFunctionParameters", function () { ); it(`addUint${bitSize}Array method should return an empty array`, async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -778,7 +794,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -788,9 +804,9 @@ describe("ContractFunctionParameters", function () { it(`addUint${bitSize}Array method should throw an error`, async function () { try { - await new ContractCallQuery() + new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -802,7 +818,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); } catch (error) { expect(error).to.be.instanceOf(Error); expect(error.message).to.be.equal(REQUIRE_ARRAY_ERROR); @@ -812,9 +828,9 @@ describe("ContractFunctionParameters", function () { }); it("should return the right min multiple int8 value", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -824,7 +840,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -834,9 +850,9 @@ describe("ContractFunctionParameters", function () { }); it("should work the right way with 0 uint32 value", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -846,7 +862,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -855,9 +871,9 @@ describe("ContractFunctionParameters", function () { }); it("should return the right multiple values", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -867,7 +883,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -879,9 +895,9 @@ describe("ContractFunctionParameters", function () { }); it("should return the right multiple int40 values", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -891,7 +907,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -901,9 +917,9 @@ describe("ContractFunctionParameters", function () { }); it("should return the right zero uint256 value", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -916,7 +932,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -928,9 +944,9 @@ describe("ContractFunctionParameters", function () { }); it("should return the right 20 decimal uint256 value", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -943,7 +959,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); @@ -955,9 +971,9 @@ describe("ContractFunctionParameters", function () { }); it("should return the again right uint256 value", async function () { - const contractQuery = await new ContractCallQuery() + const contractQuery = new ContractCallQuery() //Set the gas for the query - .setGas(15000000) + .setGas(12000000) //Set the contract ID to return the request for .setContractId(newContractId) //Set the contract function to call @@ -970,7 +986,7 @@ describe("ContractFunctionParameters", function () { ) //Set the query payment for the node returning the request //This value must cover the cost of the request otherwise will fail - .setQueryPayment(new Hbar(15)); + .setQueryPayment(new Hbar(10)); //Submit to a Hedera network const txResponse = await contractQuery.execute(env.client); diff --git a/test/integration/CustomFeesIntegrationTest.js b/test/integration/CustomFeesIntegrationTest.js index 46f7eae79..d6e5a4b8a 100644 --- a/test/integration/CustomFeesIntegrationTest.js +++ b/test/integration/CustomFeesIntegrationTest.js @@ -1428,7 +1428,7 @@ describe("CustomFees", function () { .setFreezeKey(env.operatorKey) .setWipeKey(env.operatorKey) .setSupplyKey(env.operatorKey) - .setFeeScheduleKey(KeyList.of()) + .setFeeScheduleKey(new KeyList(KeyList.of(), 1)) .setTokenType(TokenType.NonFungibleUnique) .setFreezeDefault(false) .execute(env.client) diff --git a/test/integration/FileContentsIntegrationTest.js b/test/integration/FileContentsIntegrationTest.js index e6a4ae91e..7036d3f2e 100644 --- a/test/integration/FileContentsIntegrationTest.js +++ b/test/integration/FileContentsIntegrationTest.js @@ -85,9 +85,7 @@ describe("FileContents", function () { let err = false; try { - await new FileContentsQuery() - .setQueryPayment(new Hbar(1)) - .execute(env.client); + await new FileContentsQuery().execute(env.client); } catch (error) { err = error.toString().includes(Status.InvalidFileId); } diff --git a/test/integration/FileCreateIntegrationTest.js b/test/integration/FileCreateIntegrationTest.js index 291df89e4..61ca554de 100644 --- a/test/integration/FileCreateIntegrationTest.js +++ b/test/integration/FileCreateIntegrationTest.js @@ -91,26 +91,23 @@ describe("FileCreate", function () { it("should error with too large expiration time", async function () { this.timeout(120000); - + let status; + const timestamp = new Timestamp(Date.now() / 1000 + 9999999999, 0); const operatorKey = env.operatorKey.publicKey; - let err = false; - try { await ( await new FileCreateTransaction() .setKeys([operatorKey]) .setContents("[e2e::FileCreateTransaction]") - .setExpirationTime(new Timestamp(Date.now() + 99999999, 0)) + .setExpirationTime(timestamp) .execute(env.client) ).getReceipt(env.client); } catch (error) { - err = error.toString().includes(Status.AutorenewDurationNotInRange); + status = error.status; } - if (!err) { - throw new Error("file creation did not error"); - } + expect(status).to.be.eql(Status.AutorenewDurationNotInRange); }); after(async function () { diff --git a/test/integration/FreezeTransactionIntegrationTest.js b/test/integration/FreezeTransactionIntegrationTest.js index 20c15c52f..09dce30cc 100644 --- a/test/integration/FreezeTransactionIntegrationTest.js +++ b/test/integration/FreezeTransactionIntegrationTest.js @@ -34,16 +34,6 @@ describe("FreezeTransaction", function () { expect(error.status).to.be.equal(Status.NotSupported); } - // At the moment the API is not supported that's why the following lines are commented out. - // Once supported the try/catch block above should be removed. - // The status from execution of the transaction is code 13 which means NOT_SUPPORTED. - - // const response = await transaction.execute(client) - // expect(response).to.be.instanceof(TransactionResponse) - // const receipt = await response.getReceipt(client) - // expect(receipt).to.be.instanceof(TransactionReceipt) - // expect(receipt.status.toString).to.be.instanceof(Status.Success) - client.close(); }); }); diff --git a/test/integration/NftAllowancesIntegrationTest.js b/test/integration/NftAllowancesIntegrationTest.js index efabc0c14..291d4f769 100644 --- a/test/integration/NftAllowancesIntegrationTest.js +++ b/test/integration/NftAllowancesIntegrationTest.js @@ -25,7 +25,7 @@ describe("TokenNftAllowances", function () { it("Cannot transfer on behalf of `spender` account without allowance approval", async function () { this.timeout(120000); - + let status; const spenderKey = PrivateKey.generateED25519(); const spenderAccountId = ( await ( @@ -84,7 +84,6 @@ describe("TokenNftAllowances", function () { const nft1 = new NftId(nftTokenId, serials[0]); - let err = false; const onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); try { @@ -102,10 +101,10 @@ describe("TokenNftAllowances", function () { ).execute(env.client) ).getReceipt(env.client); } catch (error) { - err = error.toString().includes(Status.SpenderDoesNotHaveAllowance); + status = error.status; } - expect(err).to.be.true; + expect(status).to.be.eql(Status.TokenNotAssociatedToAccount); }); it("Cannot transfer on behalf of `spender` account after removing the allowance approval", async function () { diff --git a/test/integration/SystemIntegrationTest.js b/test/integration/SystemIntegrationTest.js index 2c4b6922c..9125ca6c3 100644 --- a/test/integration/SystemIntegrationTest.js +++ b/test/integration/SystemIntegrationTest.js @@ -13,9 +13,8 @@ describe("SystemIntegration", function () { env = await IntegrationTestEnv.new(); }); - it("should be executable", async function () { + it("should be executable when file id is not set", async function () { this.timeout(120000); - let errorThrown = false; try { @@ -28,7 +27,11 @@ describe("SystemIntegration", function () { } expect(errorThrown).to.be.true; - errorThrown = false; + }); + + it("should be executable when contract id is not set", async function () { + this.timeout(120000); + let errorThrown = false; try { await new SystemDeleteTransaction() @@ -40,7 +43,11 @@ describe("SystemIntegration", function () { } expect(errorThrown).to.be.true; - errorThrown = false; + }); + + it("should be executable when file id and expiratiion time are not set", async function () { + this.timeout(120000); + let errorThrown = false; try { await new SystemDeleteTransaction() @@ -51,7 +58,11 @@ describe("SystemIntegration", function () { } expect(errorThrown).to.be.true; - errorThrown = false; + }); + + it("should be executable when contract id and expiration time are not set", async function () { + this.timeout(120000); + let errorThrown = false; try { await new SystemDeleteTransaction() diff --git a/test/integration/TokenAllowancesIntegrationTest.js b/test/integration/TokenAllowancesIntegrationTest.js index b62de9054..76daff719 100644 --- a/test/integration/TokenAllowancesIntegrationTest.js +++ b/test/integration/TokenAllowancesIntegrationTest.js @@ -29,7 +29,7 @@ describe("TokenAllowances", function () { it("Cannot transfer on behalf of `spender` account without allowance approval", async function () { this.timeout(120000); - + let status; const spenderKey = PrivateKey.generateED25519(); const spenderAccountId = ( await ( @@ -76,7 +76,6 @@ describe("TokenAllowances", function () { ).execute(env.client) ).getReceipt(env.client); - let err = false; const onBehalfOfTransactionId = TransactionId.generate(spenderAccountId); try { @@ -91,10 +90,10 @@ describe("TokenAllowances", function () { ).execute(env.client) ).getReceipt(env.client); } catch (error) { - err = error.toString().includes(Status.SpenderDoesNotHaveAllowance); + status = error.status; } - expect(err).to.be.true; + expect(status).to.be.eql(Status.TokenNotAssociatedToAccount); }); it("Can transfer on behalf of `spender` account with allowance approval", async function () { diff --git a/test/integration/TokenAssociateIntegrationTest.js b/test/integration/TokenAssociateIntegrationTest.js index 020813a07..5d31f007d 100644 --- a/test/integration/TokenAssociateIntegrationTest.js +++ b/test/integration/TokenAssociateIntegrationTest.js @@ -1,12 +1,19 @@ import { - // AccountBalanceQuery, - // AccountCreateTransaction, - // AccountInfoQuery, - // Hbar, - // PrivateKey, + AccountAllowanceApproveTransaction, + AccountBalanceQuery, + AccountCreateTransaction, + AccountUpdateTransaction, + Hbar, + NftId, + AccountInfoQuery, + PrivateKey, Status, TokenAssociateTransaction, TokenCreateTransaction, + TokenMintTransaction, + TokenType, + TransactionId, + TransferTransaction, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -14,74 +21,69 @@ describe("TokenAssociate", function () { let env; before(async function () { - env = await IntegrationTestEnv.new(); + env = await IntegrationTestEnv.new({ balance: 1000 }); }); - /** - * - * @description The test is temporarily commented because AccountBalanceQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // const balances = await new AccountBalanceQuery() - // .setAccountId(account) - // .execute(env.client); - - // expect(balances.tokens.get(token).toInt()).to.be.equal(0); - - // const info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // const relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.false; - // expect(relationship.isFrozen).to.be.false; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + const balances = await new AccountBalanceQuery() + .setAccountId(account) + .execute(env.client); + + expect(balances.tokens.get(token).toInt()).to.be.equal(0); + + const info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + const relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.false; + expect(relationship.isFrozen).to.be.false; + }); it("should be executable even when no token IDs are set", async function () { this.timeout(120000); @@ -134,6 +136,770 @@ describe("TokenAssociate", function () { } }); + describe("Max Auto Associations", function () { + let receiverKey, receiverId; + const TOKEN_SUPPLY = 100, + TRANSFER_AMOUNT = 10; + + beforeEach(async function () { + receiverKey = PrivateKey.generateECDSA(); + const receiverAccountCreateTx = await new AccountCreateTransaction() + .setKey(receiverKey) + .freezeWith(env.client) + .sign(receiverKey); + receiverId = ( + await ( + await receiverAccountCreateTx.execute(env.client) + ).getReceipt(env.client) + ).accountId; + }); + + describe("Limited Auto Associations", function () { + it("should revert FT transfer when no auto associations left", async function () { + this.timeout(120000); + // update account to have one auto association + const accountUpdateTx = await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await accountUpdateTx.execute(env.client) + ).getReceipt(env.client); + + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + const tokenCreateTransaction2 = + await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateTransaction2.getReceipt(env.client); + + const sendTokenToReceiverTx = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) + .execute(env.client); + + await sendTokenToReceiverTx.getReceipt(env.client); + + const sendTokenToReceiverTx2 = await new TransferTransaction() + .addTokenTransfer( + tokenId2, + env.operatorId, + -TRANSFER_AMOUNT, + ) + .addTokenTransfer(tokenId2, receiverId, TRANSFER_AMOUNT) + .freezeWith(env.client) + .execute(env.client); + + let err = false; + + try { + await sendTokenToReceiverTx2.getReceipt(env.client); + } catch (error) { + err = error + .toString() + .includes(Status.NoRemainingAutomaticAssociations); + } + + if (!err) { + throw new Error( + "Token transfer did not error with NO_REMAINING_AUTOMATIC_ASSOCIATIONS", + ); + } + }); + + it("should revert NFTs transfer when no auto associations left", async function () { + this.timeout(120000); + const accountUpdateTx = await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await accountUpdateTx.execute(env.client) + ).getReceipt(env.client); + + // create token 1 + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + // mint a token in token 1 + const tokenMintSignedTransaction = + await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .execute(env.client); + + const { serials } = await tokenMintSignedTransaction.getReceipt( + env.client, + ); + + // transfer the token to receiver + + const transferTxSign = await new TransferTransaction() + .addNftTransfer( + tokenId, + serials[0], + env.operatorId, + receiverId, + ) + .execute(env.client); + + await transferTxSign.getReceipt(env.client); + + // create token 2 + const tokenCreateTransaction2 = + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateTransaction2.getReceipt(env.client); + + // mint token 2 + const tokenMintSignedTransaction2 = + await new TokenMintTransaction() + .setTokenId(tokenId2) + .addMetadata(Buffer.from("-")) + .execute(env.client); + + const serials2 = ( + await tokenMintSignedTransaction2.getReceipt(env.client) + ).serials; + + let err = false; + + try { + const transferToken2Response = + await new TransferTransaction() + .addNftTransfer( + tokenId2, + serials2[0], + env.operatorId, + receiverId, + ) + .execute(env.client); + + await transferToken2Response.getReceipt(env.client); + } catch (error) { + err = error + .toString() + .includes(Status.NoRemainingAutomaticAssociations); + } + + if (!err) { + throw new Error( + "Token transfer did not error with NO_REMAINING_AUTOMATIC_ASSOCIATIONS", + ); + } + }); + + it("should contain sent balance when transfering FT to account with manual token association", async function () { + this.timeout(120000); + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + const tokenAssociateTransaction = + await new TokenAssociateTransaction() + .setAccountId(receiverId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await tokenAssociateTransaction.execute(env.client) + ).getReceipt(env.client); + + const sendTokenToReceiverTx = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) + .execute(env.client); + + await sendTokenToReceiverTx.getReceipt(env.client); + + const tokenBalance = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + expect(tokenBalance.tokens.get(tokenId).toInt()).to.be.equal( + TRANSFER_AMOUNT, + ); + }); + + it("should contain sent balance when transfering NFT to account with manual token association", async function () { + this.timeout(120000); + const tokenCreateTransaction = + await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("FFFFF") + .setTokenSymbol("ffff") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateTransaction.getReceipt( + env.client, + ); + + const tokenAssociateTransaction = + await new TokenAssociateTransaction() + .setAccountId(receiverId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await tokenAssociateTransaction.execute(env.client) + ).getReceipt(env.client); + + const tokenMintTx = await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .freezeWith(env.client) + .sign(env.operatorKey); + + const { serials } = await ( + await tokenMintTx.execute(env.client) + ).getReceipt(env.client); + + const sendTokenToReceiverTx = await new TransferTransaction() + .addNftTransfer( + tokenId, + serials[0], + env.operatorId, + receiverId, + ) + .execute(env.client); + + await sendTokenToReceiverTx.getReceipt(env.client); + + const tokenBalance = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + expect(tokenBalance.tokens.get(tokenId).toInt()).to.be.equal(1); + }); + }); + + describe("Unlimited Auto Associations", function () { + it("receiver should contain FTs when transfering to account with unlimited auto associations", async function () { + this.timeout(120000); + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateResponse2.getReceipt(env.client); + + const updateUnlimitedAutomaticAssociations = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await updateUnlimitedAutomaticAssociations.execute( + env.client, + ) + ).getReceipt(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + const tokenTransferResponse2 = await new TransferTransaction() + .addTokenTransfer( + tokenId2, + env.operatorId, + -TRANSFER_AMOUNT, + ) + .addTokenTransfer(tokenId2, receiverId, TRANSFER_AMOUNT) + .execute(env.client); + + await tokenTransferResponse2.getReceipt(env.client); + + const newTokenBalance = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId); + + const newTokenBalance2 = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId2); + + expect(newTokenBalance.toInt()).to.equal(TRANSFER_AMOUNT); + expect(newTokenBalance2.toInt()).to.equal(TRANSFER_AMOUNT); + }); + + it("receiver should contain NFTs when transfering to account with unlimited auto associations", async function () { + this.timeout(120000); + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = + await tokenCreateResponse2.getReceipt(env.client); + + const mintTokenTx = await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .execute(env.client); + + const { serials } = await mintTokenTx.getReceipt(env.client); + + const mintTokenTx2 = await new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata([Buffer.from("-")]) + .execute(env.client); + + await mintTokenTx2.getReceipt(env.client); + + const updateUnlimitedAutomaticAssociations = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await updateUnlimitedAutomaticAssociations.execute( + env.client, + ) + ).getReceipt(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addNftTransfer( + tokenId, + serials[0], + env.operatorId, + receiverId, + ) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + const tokenTransferResponse2 = await new TransferTransaction() + .addNftTransfer(tokenId2, 1, env.operatorId, receiverId) + .execute(env.client); + + await tokenTransferResponse2.getReceipt(env.client); + + const newTokenBalance = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId); + + const newTokenBalance2 = ( + await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client) + ).tokens.get(tokenId2); + + expect(newTokenBalance.toInt()).to.equal(1); + expect(newTokenBalance2.toInt()).to.equal(1); + }); + + it("receiver should have token balance even if it has given allowance to spender", async function () { + this.timeout(120000); + const spenderKey = PrivateKey.generateECDSA(); + const spenderAccountCreateTx = + await new AccountCreateTransaction() + .setKey(spenderKey) + .setMaxAutomaticTokenAssociations(-1) + .setInitialBalance(new Hbar(1)) + .execute(env.client); + + const spenderId = ( + await spenderAccountCreateTx.getReceipt(env.client) + ).accountId; + + const unlimitedAutoAssociationReceiverTx = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await unlimitedAutoAssociationReceiverTx.execute(env.client) + ).getReceipt(env.client); + + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const tokenAllowanceTx = + await new AccountAllowanceApproveTransaction() + .approveTokenAllowance( + tokenId, + env.operatorId, + spenderId, + TRANSFER_AMOUNT, + ) + .execute(env.client); + + await tokenAllowanceTx.getReceipt(env.client); + + const onBehalfOfTransactionId = + TransactionId.generate(spenderId); + const tokenTransferApprovedSupply = + await new TransferTransaction() + .setTransactionId(onBehalfOfTransactionId) + .addApprovedTokenTransfer( + tokenId, + env.operatorId, + -TRANSFER_AMOUNT, + ) + .addTokenTransfer(tokenId, receiverId, TRANSFER_AMOUNT) + .freezeWith(env.client) + .sign(spenderKey); + + await ( + await tokenTransferApprovedSupply.execute(env.client) + ).getReceipt(env.client); + + const tokenBalanceReceiver = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceSpender = await new AccountBalanceQuery() + .setAccountId(spenderId) + .execute(env.client); + + const tokenBalanceTreasury = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect( + tokenBalanceReceiver.tokens.get(tokenId).toInt(), + ).to.equal(TRANSFER_AMOUNT); + + expect(tokenBalanceSpender.tokens.get(tokenId)).to.equal(null); + + expect( + tokenBalanceTreasury.tokens.get(tokenId).toInt(), + ).to.equal(TOKEN_SUPPLY - TRANSFER_AMOUNT); + }); + + it("receiver should have nft even if it has given allowance to spender", async function () { + this.timeout(120000); + const spenderKey = PrivateKey.generateECDSA(); + + const unlimitedAutoAssociationReceiverTx = + await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-1) + .freezeWith(env.client) + .sign(receiverKey); + + await ( + await unlimitedAutoAssociationReceiverTx.execute(env.client) + ).getReceipt(env.client); + + const spenderAccountCreateTx = + await new AccountCreateTransaction() + .setKey(spenderKey) + .setInitialBalance(new Hbar(1)) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client); + + const spenderId = ( + await spenderAccountCreateTx.getReceipt(env.client) + ).accountId; + + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + await ( + await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata([Buffer.from("-")]) + .execute(env.client) + ).getReceipt(env.client); + + const nftId = new NftId(tokenId, 1); + const nftAllowanceTx = + await new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance( + nftId, + env.operatorId, + spenderId, + ) + .execute(env.client); + + await nftAllowanceTx.getReceipt(env.client); + + // Generate TransactionId from spender's account id in order + // for the transaction to be to be executed on behalf of the spender + const onBehalfOfTransactionId = + TransactionId.generate(spenderId); + + const nftTransferToReceiver = await new TransferTransaction() + .addApprovedNftTransfer(nftId, env.operatorId, receiverId) + .setTransactionId(onBehalfOfTransactionId) + .freezeWith(env.client) + .sign(spenderKey); + + await ( + await nftTransferToReceiver.execute(env.client) + ).getReceipt(env.client); + + const tokenBalanceReceiver = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceSpender = await new AccountBalanceQuery() + .setAccountId(spenderId) + .execute(env.client); + + const tokenBalanceTreasury = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect( + tokenBalanceReceiver.tokens.get(tokenId).toInt(), + ).to.equal(1); + + expect(tokenBalanceSpender.tokens.get(tokenId)).to.equal(null); + + expect( + tokenBalanceTreasury.tokens.get(tokenId).toInt(), + ).to.equal(0); + }); + + it("receiver with unlimited auto associations should have FTs with decimal when sender transfers FTs", async function () { + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.FungibleCommon) + .setTokenName("FFFFFFF") + .setTokenSymbol("fff") + .setDecimals(3) + .setInitialSupply(TOKEN_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .setWipeKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId } = await tokenCreateResponse.getReceipt( + env.client, + ); + + const receiverKey = PrivateKey.generateECDSA(); + const receiverAccountResponse = + await new AccountCreateTransaction() + .setKey(receiverKey) + .setMaxAutomaticTokenAssociations(-1) + .setInitialBalance(new Hbar(1)) + .execute(env.client); + + const { accountId: receiverAccountId } = + await receiverAccountResponse.getReceipt(env.client); + + await ( + await new TokenAssociateTransaction() + .setAccountId(receiverAccountId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverKey) + ).execute(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -TRANSFER_AMOUNT) + .addTokenTransfer( + tokenId, + receiverAccountId, + TRANSFER_AMOUNT, + ) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + const receiverBalance = ( + await new AccountBalanceQuery() + .setAccountId(receiverAccountId) + .execute(env.client) + ).tokens + .get(tokenId) + .toInt(); + + expect(receiverBalance).to.equal(TRANSFER_AMOUNT); + }); + + it("should revert when auto association is set to less than -1", async function () { + let err = false; + + try { + const accountUpdateTx = await new AccountUpdateTransaction() + .setAccountId(receiverId) + .setMaxAutomaticTokenAssociations(-2) + .freezeWith(env.client) + .sign(receiverKey); + await ( + await accountUpdateTx.execute(env.client) + ).getReceipt(env.client); + } catch (error) { + err = error + .toString() + .includes(Status.InvalidMaxAutoAssociations); + } + + if (!err) { + throw new Error("Token association did not error"); + } + + try { + const key = PrivateKey.generateECDSA(); + const accountCreateInvalidAutoAssociation = + await new AccountCreateTransaction() + .setKey(key) + .setMaxAutomaticTokenAssociations(-2) + .execute(env.client); + + await accountCreateInvalidAutoAssociation.getReceipt( + env.client, + ); + } catch (error) { + err = error + .toString() + .includes(Status.InvalidMaxAutoAssociations); + } + + if (!err) { + throw new Error("Token association did not error"); + } + }); + }); + }); + after(async function () { await env.close(); }); diff --git a/test/integration/TokenBurnIntegrationTest.js b/test/integration/TokenBurnIntegrationTest.js index 5c7fae214..4cfc76019 100644 --- a/test/integration/TokenBurnIntegrationTest.js +++ b/test/integration/TokenBurnIntegrationTest.js @@ -4,7 +4,7 @@ import { TokenCreateTransaction, TokenSupplyType, TokenType, - // AccountBalanceQuery, + AccountBalanceQuery, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -64,58 +64,52 @@ describe("TokenBurn", function () { throw new Error("token Burn did not error"); } }); + it("should not error when amount is not set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client); - /** - * - * @description The test is temporarily commented because AccountBalanceQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should not error when amount is not set", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - - // const response = await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client); - - // const token = (await response.getReceipt(env.client)).tokenId; - - // let err = false; - - // try { - // await ( - // await new TokenBurnTransaction() - // .setTokenId(token) - // .execute(env.client) - // ).getReceipt(env.client); - // } catch (error) { - // err = error; - // } - - // const accountBalance = await new AccountBalanceQuery() - // .setAccountId(operatorId) - // .execute(env.client); - - // expect( - // accountBalance.tokens._map.get(token.toString()).toNumber(), - // ).to.be.equal(1000000); - - // if (err) { - // throw new Error("token burn did error"); - // } - // }); + const token = (await response.getReceipt(env.client)).tokenId; + + let err = false; + + try { + await ( + await new TokenBurnTransaction() + .setTokenId(token) + .execute(env.client) + ).getReceipt(env.client); + } catch (error) { + err = error; + } + + const accountBalance = await new AccountBalanceQuery() + .setAccountId(operatorId) + .execute(env.client); + + expect( + accountBalance.tokens._map.get(token.toString()).toNumber(), + ).to.be.equal(1000000); + + if (err) { + throw new Error("token burn did error"); + } + }); it("cannot burn token with invalid metadata", async function () { this.timeout(120000); diff --git a/test/integration/TokenCreateIntegrationTest.js b/test/integration/TokenCreateIntegrationTest.js index b4da60200..f9a16daf9 100644 --- a/test/integration/TokenCreateIntegrationTest.js +++ b/test/integration/TokenCreateIntegrationTest.js @@ -1,6 +1,7 @@ import { PrivateKey, Status, + Timestamp, TokenCreateTransaction, TokenDeleteTransaction, TokenInfoQuery, @@ -59,12 +60,8 @@ describe("TokenCreate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -101,12 +98,8 @@ describe("TokenCreate", function () { expect(info.defaultFreezeStatus).to.be.null; expect(info.defaultKycStatus).to.be.null; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; let err = false; @@ -126,6 +119,95 @@ describe("TokenCreate", function () { } }); + it("when autoRenewAccountId is set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .setAutoRenewAccountId(operatorId) + .execute(env.client); + + const tokenId = (await response.getReceipt(env.client)).tokenId; + + const info = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(info.autoRenewAccountId).to.be.not.null; + expect(info.autoRenewAccountId.toString()).to.be.eql( + operatorId.toString(), + ); + }); + + it("when expirationTime is set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const DAYS_45_IN_SECONDS = 3888000; + const expirationTime = new Timestamp( + Math.floor(Date.now() / 1000 + DAYS_45_IN_SECONDS), + 0, + ); + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .setExpirationTime(expirationTime) + .execute(env.client); + + const tokenId = (await response.getReceipt(env.client)).tokenId; + + const info = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(info.expirationTime).to.be.not.null; + expect(info.expirationTime.toString()).to.be.eql( + expirationTime.toString(), + ); + }); + + it("when autoRenewAccountId and expirationTime are set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const DAYS_90_IN_SECONDS = 7776000; + const expirationTime = new Timestamp( + Math.floor(Date.now() / 1000 + DAYS_90_IN_SECONDS), + 0, + ); + + const response = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(operatorId) + .setExpirationTime(expirationTime) + .setAutoRenewAccountId(operatorId) + .execute(env.client); + + const tokenId = (await response.getReceipt(env.client)).tokenId; + + const info = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(info.autoRenewAccountId).to.be.not.null; + expect(info.autoRenewAccountId.toString()).to.be.eql( + operatorId.toString(), + ); + expect(info.autoRenewPeriod).to.be.not.null; + expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.expirationTime).to.be.not.null; + expect(info.expirationTime.toDate().getTime()).to.be.at.least( + expirationTime.toDate().getTime(), + ); + }); + it("should error when token name is not set", async function () { this.timeout(120000); diff --git a/test/integration/TokenDissociateIntegrationTest.js b/test/integration/TokenDissociateIntegrationTest.js index 5b1e29414..92916b3a3 100644 --- a/test/integration/TokenDissociateIntegrationTest.js +++ b/test/integration/TokenDissociateIntegrationTest.js @@ -1,8 +1,8 @@ import { - // AccountBalanceQuery, + AccountBalanceQuery, AccountCreateTransaction, - // AccountInfoQuery, - // Hbar, + AccountInfoQuery, + Hbar, PrivateKey, Status, TokenAssociateTransaction, @@ -23,93 +23,88 @@ describe("TokenDissociate", function () { env = await IntegrationTestEnv.new(); }); - /** - * - * @description The test is temporarily commented because AccountBalanceQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // let balances = await new AccountBalanceQuery() - // .setAccountId(account) - // .execute(env.client); - - // expect(balances.tokens.get(token).toInt()).to.be.equal(0); - - // let info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // const relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.false; - // expect(relationship.isFrozen).to.be.false; - - // await ( - // await ( - // await new TokenDissociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // balances = await new AccountBalanceQuery() - // .setAccountId(account) - // .execute(env.client); - - // expect(balances.tokens.get(token)).to.be.null; - - // info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // expect(info.tokenRelationships.get(token)).to.be.null; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + let balances = await new AccountBalanceQuery() + .setAccountId(account) + .execute(env.client); + + expect(balances.tokens.get(token).toInt()).to.be.equal(0); + + let info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + const relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.false; + expect(relationship.isFrozen).to.be.false; + + await ( + await ( + await new TokenDissociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + balances = await new AccountBalanceQuery() + .setAccountId(account) + .execute(env.client); + + expect(balances.tokens.get(token)).to.be.null; + + info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + expect(info.tokenRelationships.get(token)).to.be.null; + }); it("should be executable even when no token IDs are set", async function () { this.timeout(120000); diff --git a/test/integration/TokenFreezeIntegrationTest.js b/test/integration/TokenFreezeIntegrationTest.js index f35aed9b6..48af381af 100644 --- a/test/integration/TokenFreezeIntegrationTest.js +++ b/test/integration/TokenFreezeIntegrationTest.js @@ -1,10 +1,10 @@ import { AccountCreateTransaction, - // AccountInfoQuery, + AccountInfoQuery, Hbar, PrivateKey, Status, - // TokenAssociateTransaction, + TokenAssociateTransaction, TokenCreateTransaction, TokenFreezeTransaction, } from "../../src/exports.js"; @@ -17,75 +17,70 @@ describe("TokenFreeze", function () { env = await IntegrationTestEnv.new(); }); - /** - * - * @description The test is temporarily commented because AccountInfoQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // await ( - // await ( - // await new TokenFreezeTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // const info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // const relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.false; - // expect(relationship.isFrozen).to.be.true; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new TokenFreezeTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + const info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + const relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.false; + expect(relationship.isFrozen).to.be.true; + }); it("should be executable with no tokens set", async function () { this.timeout(120000); diff --git a/test/integration/TokenGrantKycIntegrationTest.js b/test/integration/TokenGrantKycIntegrationTest.js index 2fa40851b..5355b2058 100644 --- a/test/integration/TokenGrantKycIntegrationTest.js +++ b/test/integration/TokenGrantKycIntegrationTest.js @@ -1,10 +1,10 @@ import { AccountCreateTransaction, - // AccountInfoQuery, + AccountInfoQuery, Hbar, PrivateKey, Status, - // TokenAssociateTransaction, + TokenAssociateTransaction, TokenCreateTransaction, TokenGrantKycTransaction, } from "../../src/exports.js"; @@ -17,75 +17,70 @@ describe("TokenGrantKyc", function () { env = await IntegrationTestEnv.new(); }); - /** - * - * @description The test is temporarily commented because AccountInfoQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // await ( - // await ( - // await new TokenGrantKycTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // const info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // const relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.true; - // expect(relationship.isFrozen).to.be.false; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new TokenGrantKycTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + const info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + const relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.true; + expect(relationship.isFrozen).to.be.false; + }); it("should be executable even when no token IDs are set", async function () { this.timeout(120000); diff --git a/test/integration/TokenInfoIntegrationTest.js b/test/integration/TokenInfoIntegrationTest.js index ec4001557..2bf94d1d9 100644 --- a/test/integration/TokenInfoIntegrationTest.js +++ b/test/integration/TokenInfoIntegrationTest.js @@ -22,6 +22,7 @@ describe("TokenInfo", function () { const key2 = PrivateKey.generateED25519(); const key3 = PrivateKey.generateED25519(); const key4 = PrivateKey.generateED25519(); + const key5 = PrivateKey.generateED25519(); const response = await new TokenCreateTransaction() .setTokenName("ffff") @@ -34,6 +35,7 @@ describe("TokenInfo", function () { .setFreezeKey(key2) .setWipeKey(key3) .setSupplyKey(key4) + .setMetadataKey(key5) .setFreezeDefault(false) .execute(env.client); @@ -56,15 +58,12 @@ describe("TokenInfo", function () { expect(info.freezeKey.toString()).to.eql(key2.publicKey.toString()); expect(info.wipeKey.toString()).to.eql(key3.publicKey.toString()); expect(info.supplyKey.toString()).to.eql(key4.publicKey.toString()); + expect(info.metadataKey.toString()).to.eql(key5.publicKey.toString()); expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -98,15 +97,12 @@ describe("TokenInfo", function () { expect(info.freezeKey).to.be.null; expect(info.wipeKey).to.be.null; expect(info.supplyKey).to.be.null; + expect(info.metadataKey).to.be.null; expect(info.defaultFreezeStatus).to.be.null; expect(info.defaultKycStatus).to.be.null; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); diff --git a/test/integration/TokenNftsUpdateTransactionIntegrationTest.js b/test/integration/TokenNftsUpdateTransactionIntegrationTest.js new file mode 100644 index 000000000..cb96dc1bf --- /dev/null +++ b/test/integration/TokenNftsUpdateTransactionIntegrationTest.js @@ -0,0 +1,367 @@ +import { + TokenCreateTransaction, + TokenType, + PrivateKey, + TokenMintTransaction, + TokenUpdateNftsTransaction, + TokenNftInfoQuery, + NftId, + Status, +} from "../../src/exports.js"; +import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; + +describe("TokenUpdateNftsTransaction", function () { + let client, + operatorId, + operatorKey, + metadata, + newMetadata, + metadataKey, + tokenName, + tokenSymbol, + supplyKey, + wrongMetadataKey, + nftCount; + + before(async function () { + const env = await IntegrationTestEnv.new(); + client = env.client; + operatorId = env.operatorId; + operatorKey = env.operatorKey; + metadata = new Uint8Array([1]); + newMetadata = new Uint8Array([1, 2]); + metadataKey = PrivateKey.generateECDSA(); + supplyKey = PrivateKey.generateECDSA(); + tokenName = "Test"; + tokenSymbol = "T"; + wrongMetadataKey = PrivateKey.generateECDSA(); + nftCount = 4; + }); + + it("should update the metadata of entire NFT collection", async function () { + this.timeout(120000); + + const createTokenTx = new TokenCreateTransaction() + .setTokenName(tokenName) + .setTokenSymbol(tokenSymbol) + .setAdminKey(operatorKey) + .setSupplyKey(supplyKey) + .setMetadataKey(metadataKey) + .setTreasuryAccountId(operatorId) + .setTokenType(TokenType.NonFungibleUnique); + + const createTokenTxResponse = await createTokenTx.execute(client); + const createTokenTxReceipt = + await createTokenTxResponse.getReceipt(client); + const tokenId = createTokenTxReceipt.tokenId; + + const tokenMintTx = new TokenMintTransaction() + .setMetadata(generateMetadataList(metadata, nftCount)) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintReceipt = await tokenMintResponse.getReceipt(client); + const serials = tokenMintReceipt.serials; + + const metadatas = await geNftsMetadata(client, tokenId, serials); + expect( + metadatas.every( + (mt) => mt === Buffer.from(metadata).toString("hex"), + ), + ).to.be.true; + + await ( + await ( + await new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerialNumbers(serials) + .setMetadata(newMetadata) + .freezeWith(client) + .sign(metadataKey) + ).execute(client) + ).getReceipt(client); + + const newMetadatas = await geNftsMetadata(client, tokenId, serials); + expect( + newMetadatas.every( + (mt) => mt === Buffer.from(newMetadata).toString("hex"), + ), + ).to.be.true; + }); + + it("should update the NFT's metadata", async function () { + this.timeout(120000); + + const createTokenTx = new TokenCreateTransaction() + .setTokenName(tokenName) + .setTokenSymbol(tokenSymbol) + .setAdminKey(operatorKey) + .setSupplyKey(supplyKey) + .setMetadataKey(metadataKey) + .setTreasuryAccountId(operatorId) + .setTokenType(TokenType.NonFungibleUnique); + + const createTokenTxResponse = await createTokenTx.execute(client); + const createTokenTxReceipt = + await createTokenTxResponse.getReceipt(client); + const tokenId = createTokenTxReceipt.tokenId; + + const tokenMintTx = new TokenMintTransaction() + .setMetadata(generateMetadataList(metadata, nftCount)) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintReceipt = await tokenMintResponse.getReceipt(client); + const serials = tokenMintReceipt.serials; + + const metadatas = await geNftsMetadata(client, tokenId, serials); + expect( + metadatas.every( + (mt) => mt === Buffer.from(metadata).toString("hex"), + ), + ).to.be.true; + + await ( + await ( + await new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerialNumbers([serials[0], serials[1]]) + .setMetadata(newMetadata) + .freezeWith(client) + .sign(metadataKey) + ).execute(client) + ).getReceipt(client); + + const newMetadatas = await geNftsMetadata(client, tokenId, serials); + expect( + newMetadatas.map( + (mt) => mt === Buffer.from(newMetadata).toString("hex"), + ), + ).to.deep.eql([true, true, false, false]); + }); + + it("should NOT update the NFT's metadata", async function () { + this.timeout(120000); + + const createTokenTx = new TokenCreateTransaction() + .setTokenName(tokenName) + .setTokenSymbol(tokenSymbol) + .setAdminKey(operatorKey) + .setSupplyKey(supplyKey) + .setMetadataKey(metadataKey) + .setTreasuryAccountId(operatorId) + .setTokenType(TokenType.NonFungibleUnique); + + const createTokenTxResponse = await createTokenTx.execute(client); + const createTokenTxReceipt = + await createTokenTxResponse.getReceipt(client); + const tokenId = createTokenTxReceipt.tokenId; + + const tokenMintTx = new TokenMintTransaction() + .setMetadata(generateMetadataList(metadata, nftCount)) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintReceipt = await tokenMintResponse.getReceipt(client); + const serials = tokenMintReceipt.serials; + + const metadatas = await geNftsMetadata(client, tokenId, serials); + expect( + metadatas.every( + (mt) => mt === Buffer.from(metadata).toString("hex"), + ), + ).to.be.true; + + await ( + await ( + await new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerialNumbers(serials) + .freezeWith(client) + .sign(metadataKey) + ).execute(client) + ).getReceipt(client); + + const sameMetadatas = await geNftsMetadata(client, tokenId, serials); + expect( + sameMetadatas.every( + (mt) => mt === Buffer.from(metadata).toString("hex"), + ), + ).to.be.true; + }); + + it("should earse the metadata of entire NFT collection", async function () { + this.timeout(120000); + + const createTokenTx = new TokenCreateTransaction() + .setTokenName(tokenName) + .setTokenSymbol(tokenSymbol) + .setAdminKey(operatorKey) + .setSupplyKey(supplyKey) + .setMetadataKey(metadataKey) + .setTreasuryAccountId(operatorId) + .setTokenType(TokenType.NonFungibleUnique); + + const createTokenTxResponse = await createTokenTx.execute(client); + const createTokenTxReceipt = + await createTokenTxResponse.getReceipt(client); + const tokenId = createTokenTxReceipt.tokenId; + + const tokenMintTx = new TokenMintTransaction() + .setMetadata(generateMetadataList(metadata, nftCount)) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintReceipt = await tokenMintResponse.getReceipt(client); + const serials = tokenMintReceipt.serials; + + const metadatas = await geNftsMetadata(client, tokenId, serials); + expect( + metadatas.every( + (mt) => mt === Buffer.from(metadata).toString("hex"), + ), + ).to.be.true; + + await ( + await ( + await new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setMetadata([]) + .setSerialNumbers(serials) + .freezeWith(client) + .sign(metadataKey) + ).execute(client) + ).getReceipt(client); + + const sameMetadatas = await geNftsMetadata(client, tokenId, serials); + expect( + sameMetadatas.every( + (mt) => mt === Buffer.from(new Uint8Array()).toString("hex"), + ), + ).to.be.true; + }); + + it("should NOT update the NFTs metadata if the metadataKey is NOT set", async function () { + this.timeout(120000); + try { + const createTokenTx = new TokenCreateTransaction() + .setTokenName(tokenName) + .setTokenSymbol(tokenSymbol) + .setAdminKey(operatorKey) + .setSupplyKey(supplyKey) + .setTreasuryAccountId(operatorId) + .setTokenType(TokenType.NonFungibleUnique); + + const createTokenTxResponse = await createTokenTx.execute(client); + const createTokenTxReceipt = + await createTokenTxResponse.getReceipt(client); + const tokenId = createTokenTxReceipt.tokenId; + + const tokenMintTx = new TokenMintTransaction() + .setMetadata(generateMetadataList(metadata, nftCount)) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintReceipt = await tokenMintResponse.getReceipt(client); + + const serials = tokenMintReceipt.serials; + + const tokenUpdateNftsTx = new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerialNumbers(serials) + .setMetadata(newMetadata) + .freezeWith(client); + + await ( + await ( + await tokenUpdateNftsTx.sign(metadataKey) + ).execute(client) + ).getReceipt(client); + } catch (error) { + expect(error.status).to.be.eql(Status.TokenHasNoMetadataKey); + } + }); + + it("should NOT update the NFTs metadata when the transaction is not signed with the metadataKey", async function () { + this.timeout(120000); + try { + const createTokenTx = new TokenCreateTransaction() + .setTokenName(tokenName) + .setTokenSymbol(tokenSymbol) + .setAdminKey(operatorKey) + .setMetadataKey(metadataKey) + .setSupplyKey(supplyKey) + .setTreasuryAccountId(operatorId) + .setTokenType(TokenType.NonFungibleUnique); + + const createTokenTxResponse = await createTokenTx.execute(client); + const createTokenTxReceipt = + await createTokenTxResponse.getReceipt(client); + const tokenId = createTokenTxReceipt.tokenId; + + const tokenMintTx = new TokenMintTransaction() + .setMetadata(generateMetadataList(metadata, nftCount)) + .setTokenId(tokenId) + .freezeWith(client); + + const tokenMintResponse = await ( + await tokenMintTx.sign(supplyKey) + ).execute(client); + const tokenMintReceipt = await tokenMintResponse.getReceipt(client); + + const serials = tokenMintReceipt.serials; + + const tokenUpdateNftsTx = new TokenUpdateNftsTransaction() + .setTokenId(tokenId) + .setSerialNumbers(serials) + .setMetadata(newMetadata) + .freezeWith(client); + + await ( + await ( + await tokenUpdateNftsTx.sign(wrongMetadataKey) + ).execute(client) + ).getReceipt(client); + } catch (error) { + expect(error.status).to.be.eql(Status.InvalidSignature); + } + }); +}); + +function generateMetadataList(metadata, count) { + const list = []; + + for (let index = 0; index < count; index++) { + list.push(metadata); + } + return list; +} + +async function geNftsMetadata(client, tokenId, serials) { + const metadatas = []; + + for (let index = 0; index < serials.length; index++) { + const nftId = new NftId(tokenId, serials[index]); + const nftInfo = await new TokenNftInfoQuery() + .setNftId(nftId) + .execute(client); + metadatas.push(nftInfo[0].metadata.toString("hex")); + } + + return metadatas; +} diff --git a/test/integration/TokenRejectFlowIntegrationTest.js b/test/integration/TokenRejectFlowIntegrationTest.js new file mode 100644 index 000000000..83ab17586 --- /dev/null +++ b/test/integration/TokenRejectFlowIntegrationTest.js @@ -0,0 +1,209 @@ +import { + AccountBalanceQuery, + AccountCreateTransaction, + Hbar, + NftId, + PrivateKey, + TokenAssociateTransaction, + TokenCreateTransaction, + TokenMintTransaction, + TokenRejectFlow, + TokenType, + TransferTransaction, +} from "../../src/exports.js"; +import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; + +describe("TokenRejectIntegrationTest", function () { + let env; + it("can execute TokenRejectFlow for fungible tokens", async function () { + this.timeout(120000); + env = await IntegrationTestEnv.new(); + const FULL_TREASURY_BALANCE = 1000000; + + // create token + const tokenCreateTx = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(FULL_TREASURY_BALANCE) + .setTreasuryAccountId(env.operatorId) + .setPauseKey(env.operatorKey) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + let tokenId1 = (await tokenCreateTx.getReceipt(env.client)).tokenId; + + // create token + const tokenCreateTx2 = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(env.operatorId) + .setPauseKey(env.operatorKey) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + let tokenId2 = (await tokenCreateTx2.getReceipt(env.client)).tokenId; + // create receiver account + let receiverPrivateKey = await PrivateKey.generateECDSA(); + const receiverCreateAccount = await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setInitialBalance(new Hbar(1)) + .execute(env.client); + + let receiverId = (await receiverCreateAccount.getReceipt(env.client)) + .accountId; + + await ( + await new TokenAssociateTransaction() + .setAccountId(receiverId) + .setTokenIds([tokenId1, tokenId2]) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client); + + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId1, env.operatorId, -100) + .addTokenTransfer(tokenId1, receiverId, 100) + .addTokenTransfer(tokenId2, env.operatorId, -100) + .addTokenTransfer(tokenId2, receiverId, 100) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await new TokenRejectFlow() + .setOwnerId(receiverId) + .setTokenIds([tokenId1, tokenId2]) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client); + + const receiverBalanceQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const treasuryBalanceQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect(receiverBalanceQuery.tokens.get(tokenId1)).to.be.eq(null); + expect(receiverBalanceQuery.tokens.get(tokenId2)).to.be.eq(null); + expect(treasuryBalanceQuery.tokens.get(tokenId1).toInt()).to.be.eq( + FULL_TREASURY_BALANCE, + ); + expect(treasuryBalanceQuery.tokens.get(tokenId2).toInt()).to.be.eq( + FULL_TREASURY_BALANCE, + ); + + let err; + try { + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId1, receiverId, 100) + .addTokenTransfer(tokenId1, env.operatorId, -100) + .execute(env.client) + ).getReceipt(env.client); + } catch (error) { + err = error.message.includes("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); + } + + if (!err) { + throw new Error( + "Token should not be associated with receiver account", + ); + } + }); + + it("can execute TokenRejectFlow for non-fungible tokens", async function () { + this.timeout(120000); + env = await IntegrationTestEnv.new(); + + // create token + const tokenCreateTx = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setPauseKey(env.operatorKey) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + let { tokenId } = await tokenCreateTx.getReceipt(env.client); + + // create receiver account + let receiverPrivateKey = await PrivateKey.generateECDSA(); + const receiverCreateAccount = await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setInitialBalance(new Hbar(1)) + .execute(env.client); + + let { accountId: receiverId } = await receiverCreateAccount.getReceipt( + env.client, + ); + + await ( + await new TokenAssociateTransaction() + .setAccountId(receiverId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client); + + await new TokenMintTransaction() + .setTokenId(tokenId) + .addMetadata(Buffer.from("=====")) + .execute(env.client); + + const nftId = new NftId(tokenId, 1); + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await new TokenRejectFlow() + .setOwnerId(receiverId) + .setNftIds([nftId]) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client); + + const receiverBalanceQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const treasuryBalanceQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect(receiverBalanceQuery.tokens.get(tokenId)).to.eq(null); + expect(treasuryBalanceQuery.tokens.get(tokenId).toInt()).to.be.eq(1); + + let err; + try { + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + } catch (error) { + err = error.message.includes("TOKEN_NOT_ASSOCIATED_TO_ACCOUNT"); + } + + if (!err) { + throw new Error( + "Token should not be associated with receiver account", + ); + } + }); + + after(async function () { + await env.close(); + }); +}); diff --git a/test/integration/TokenRejectIntegrationTest.js b/test/integration/TokenRejectIntegrationTest.js new file mode 100644 index 000000000..f08298bcc --- /dev/null +++ b/test/integration/TokenRejectIntegrationTest.js @@ -0,0 +1,1005 @@ +import { expect } from "chai"; +import { + AccountAllowanceApproveTransaction, + AccountBalanceQuery, + AccountCreateTransaction, + AccountUpdateTransaction, + Hbar, + NftId, + PrivateKey, + TokenCreateTransaction, + TokenFreezeTransaction, + TokenMintTransaction, + TokenPauseTransaction, + TokenRejectTransaction, + TokenType, + TransactionId, + TransferTransaction, +} from "../../src/exports.js"; +import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; + +describe("TokenRejectIntegrationTest", function () { + let env, tokenId, receiverId, receiverPrivateKey; + const INITIAL_SUPPLY = 1000000; + + describe("Fungible Tokens", function () { + beforeEach(async function () { + env = await IntegrationTestEnv.new(); + + // create token + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(INITIAL_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setPauseKey(env.operatorKey) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .execute(env.client); + + tokenId = (await tokenCreateResponse.getReceipt(env.client)) + .tokenId; + + // create receiver account + receiverPrivateKey = await PrivateKey.generateECDSA(); + const receiverCreateAccount = await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setInitialBalance(new Hbar(1)) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client); + + receiverId = (await receiverCreateAccount.getReceipt(env.client)) + .accountId; + }); + + it("should execute TokenReject Tx", async function () { + this.timeout(120000); + + // create another token + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenName("ffff2") + .setTokenSymbol("F2") + .setDecimals(3) + .setInitialSupply(INITIAL_SUPPLY) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = await tokenCreateResponse2.getReceipt( + env.client, + ); + + // transfer tokens of both types to receiver + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .addTokenTransfer(tokenId2, env.operatorId, -1) + .addTokenTransfer(tokenId2, receiverId, 1) + .execute(env.client) + ).getReceipt(env.client); + + // reject tokens + await ( + await ( + await new TokenRejectTransaction() + .setTokenIds([tokenId, tokenId2]) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + const tokenBalanceReceiverQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceReceiver = tokenBalanceReceiverQuery.tokens + .get(tokenId) + .toInt(); + const tokenBalanceReceiver2 = tokenBalanceReceiverQuery.tokens + .get(tokenId2) + .toInt(); + + const tokenBalanceTreasuryQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + const tokenBalanceTreasury = tokenBalanceTreasuryQuery.tokens + .get(tokenId) + .toInt(); + const tokenBalanceTreasury2 = tokenBalanceTreasuryQuery.tokens + .get(tokenId) + .toInt(); + + expect(tokenBalanceReceiver).to.be.equal(0); + expect(tokenBalanceReceiver2).to.be.equal(0); + + expect(tokenBalanceTreasury).to.be.equal(INITIAL_SUPPLY); + expect(tokenBalanceTreasury2).to.be.equal(INITIAL_SUPPLY); + }); + + it("should return token back when receiver has receiverSigRequired is true", async function () { + this.timeout(120000); + const TREASURY_TOKENS_AMOUNT = 1000000; + + await new AccountUpdateTransaction() + .setAccountId(env.operatorId) + .setReceiverSignatureRequired(true) + .execute(env.client); + + const transferTransactionResponse = await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .execute(env.client); + + await transferTransactionResponse.getReceipt(env.client); + + const tokenRejectResponse = await ( + await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client); + + await tokenRejectResponse.getReceipt(env.client); + + const tokenBalanceTreasuryQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + const tokenBalanceTreasury = tokenBalanceTreasuryQuery.tokens + .get(tokenId) + .toInt(); + expect(tokenBalanceTreasury).to.be.equal(TREASURY_TOKENS_AMOUNT); + + const tokenBalanceReceiverQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + const tokenBalanceReceiver = tokenBalanceReceiverQuery.tokens + .get(tokenId) + .toInt(); + expect(tokenBalanceReceiver).to.equal(0); + }); + + // temporary disabled until issue re nfts will be resolved on services side + // eslint-disable-next-line mocha/no-skipped-tests + it.skip("should not return spender allowance to zero after owner rejects FT", async function () { + this.timeout(120000); + + const spenderAccountPrivateKey = PrivateKey.generateED25519(); + const spenderAccountResponse = await new AccountCreateTransaction() + .setMaxAutomaticTokenAssociations(-1) + .setInitialBalance(new Hbar(10)) + .setKey(spenderAccountPrivateKey) + .execute(env.client); + + const { accountId: spenderAccountId } = + await spenderAccountResponse.getReceipt(env.client); + + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new AccountAllowanceApproveTransaction() + .approveTokenAllowance( + tokenId, + receiverId, + spenderAccountId, + 10, + ) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + // Confirm that token reject transaction has returned funds + const balanceReceiverPre = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const balanceTreasuryPre = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + expect(balanceReceiverPre.tokens.get(tokenId).toInt()).to.eq(0); + expect(balanceTreasuryPre.tokens.get(tokenId).toInt()).to.eq( + INITIAL_SUPPLY, + ); + + // after token reject transaction receiver doesn't have balance + // so we need some tokens back from treasury + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .execute(env.client) + ).getReceipt(env.client); + + const transactionId = TransactionId.generate(spenderAccountId); + await ( + await ( + await new TransferTransaction() + .addApprovedTokenTransfer(tokenId, receiverId, -1) + .addTokenTransfer(tokenId, spenderAccountId, 1) + .setTransactionId(transactionId) + .freezeWith(env.client) + .sign(spenderAccountPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + // Confirm spender has transfered tokens + const tokenBalanceReceiverPost = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + expect(tokenBalanceReceiverPost.tokens.get(tokenId).toInt()).to.eq( + 0, + ); + + const tokenBalanceSpenderPost = await new AccountBalanceQuery() + .setAccountId(spenderAccountId) + .execute(env.client); + + expect(tokenBalanceSpenderPost.tokens.get(tokenId).toInt()).to.eq( + 1, + ); + }); + + describe("should throw an error", function () { + it("when paused FT", async function () { + this.timeout(120000); + + await ( + await new TokenPauseTransaction() + .setTokenId(tokenId) + .execute(env.client) + ).getReceipt(env.client); + + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .execute(env.client); + + const tokenRejectTx = await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey); + + try { + await ( + await tokenRejectTx.execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("TOKEN_IS_PAUSED"); + } + }); + + it("when FT is frozen", async function () { + this.timeout(120000); + // transfer token to receiver + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .execute(env.client); + + // freeze token + await ( + await new TokenFreezeTransaction() + .setTokenId(tokenId) + .setAccountId(receiverId) + .execute(env.client) + ).getReceipt(env.client); + + try { + // reject token on frozen account for thsi token + await ( + await ( + await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("ACCOUNT_FROZEN_FOR_TOKEN"); + } + }); + + it("when there's a duplicated token reference", async function () { + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1) + .addTokenTransfer(tokenId, receiverId, 1) + .execute(env.client) + ).getReceipt(env.client); + + try { + await new TokenRejectTransaction() + .setTokenIds([tokenId, tokenId]) + .execute(env.client); + } catch (err) { + expect(err.message).to.include("TOKEN_REFERENCE_REPEATED"); + } + }); + + it("when user does not have balance", async function () { + this.timeout(120000); + + // create receiver account + const receiverPrivateKey = PrivateKey.generateED25519(); + const { accountId: emptyBalanceUserId } = await ( + await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1000) + .addTokenTransfer(tokenId, receiverId, 1000) + .execute(env.client) + ).getReceipt(env.client); + + const transactionId = + await TransactionId.generate(emptyBalanceUserId); + try { + await ( + await ( + await new TokenRejectTransaction() + .setOwnerId(emptyBalanceUserId) + .addTokenId(tokenId) + .setTransactionId(transactionId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include( + "INSUFFICIENT_PAYER_BALANCE", + ); + } + }); + + it("when trasury account rejects token", async function () { + try { + await ( + await new TokenRejectTransaction() + .addTokenId(tokenId) + .execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("ACCOUNT_IS_TREASURY"); + } + }); + + it("when more than 11 tokens in token list for RejectToken transaction", async function () { + this.timeout(120000); + const tokenIds = []; + + for (let i = 0; i < 11; i++) { + const { tokenId } = await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setTokenType(TokenType.FungibleCommon) + .setInitialSupply(1000) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client) + ).getReceipt(env.client); + tokenIds.push(tokenId); + } + try { + await ( + await new TokenRejectTransaction() + .setTokenIds(tokenIds) + .execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include( + "TOKEN_REFERENCE_LIST_SIZE_LIMIT_EXCEEDED", + ); + } + }); + }); + }); + + describe("Non-Fungible Tokens", function () { + let tokenId, receiverPrivateKey, receiverId, nftId; + beforeEach(async function () { + this.timeout(120000); + env = await IntegrationTestEnv.new(); + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .setPauseKey(env.operatorKey) + .setFreezeKey(env.operatorKey) + .execute(env.client); + + tokenId = (await tokenCreateResponse.getReceipt(env.client)) + .tokenId; + + receiverPrivateKey = await PrivateKey.generateECDSA(); + receiverId = ( + await ( + await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client) + ).getReceipt(env.client) + ).accountId; + + nftId = new NftId(tokenId, 1); + await ( + await new TokenMintTransaction() + .setTokenId(tokenId) + .setMetadata(Buffer.from("-")) + .execute(env.client) + ).getReceipt(env.client); + }); + + it("should execute TokenReject Tx", async function () { + this.timeout(120000); + + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff2") + .setTokenSymbol("F2") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + const { tokenId: tokenId2 } = await tokenCreateResponse2.getReceipt( + env.client, + ); + + const nftId2 = new NftId(tokenId2, 1); + await ( + await new TokenMintTransaction() + .setTokenId(tokenId2) + .setMetadata(Buffer.from("-")) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .addNftTransfer(nftId2, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await await new TokenRejectTransaction() + .setNftIds([nftId, nftId2]) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + const tokenBalanceReceiverQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceTreasuryQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + const tokenBalanceReceiver = tokenBalanceReceiverQuery.tokens + .get(tokenId) + .toInt(); + const tokenBalanceReceiver2 = tokenBalanceReceiverQuery.tokens + .get(tokenId2) + .toInt(); + + const tokenBalanceTreasury = tokenBalanceTreasuryQuery.tokens + .get(tokenId) + .toInt(); + const tokenBalanceTreasury2 = tokenBalanceTreasuryQuery.tokens + .get(tokenId2) + .toInt(); + + expect(tokenBalanceTreasury).to.be.equal(1); + expect(tokenBalanceTreasury2).to.be.equal(1); + + expect(tokenBalanceReceiver).to.be.equal(0); + expect(tokenBalanceReceiver2).to.be.equal(0); + }); + + it("should return tokens back to treasury receiverSigRequired is true", async function () { + this.timeout(1200000); + + await new AccountUpdateTransaction() + .setAccountId(env.operatorId) + .setReceiverSignatureRequired(true) + .execute(env.client); + + const transferTransactionResponse = await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .freezeWith(env.client) + .execute(env.client); + + await transferTransactionResponse.getReceipt(env.client); + + const tokenRejectResponse = await ( + await new TokenRejectTransaction() + .addNftId(nftId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client); + + await tokenRejectResponse.getReceipt(env.client); + + const tokenBalanceTreasuryQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + const tokenBalanceTreasury = tokenBalanceTreasuryQuery.tokens + .get(tokenId) + .toInt(); + expect(tokenBalanceTreasury).to.be.equal(1); + + const tokenBalanceReceiverQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceReceiver = tokenBalanceReceiverQuery.tokens + .get(tokenId) + .toInt(); + expect(tokenBalanceReceiver).to.equal(0); + }); + + // temporary disabled until issue re nfts will be resolved on services side + // eslint-disable-next-line mocha/no-skipped-tests + it.skip("should return spender allowance to 0 after owner rejects NFT", async function () { + this.timeout(120000); + + // create spender account + const spenderAccountPrivateKey = PrivateKey.generateED25519(); + const spenderAccountResponse = await new AccountCreateTransaction() + .setMaxAutomaticTokenAssociations(-1) + .setInitialBalance(new Hbar(10)) + .setKey(spenderAccountPrivateKey) + .execute(env.client); + + const { accountId: spenderAccountId } = + await spenderAccountResponse.getReceipt(env.client); + + // transfer nft to receiver + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + + // approve nft allowance + await ( + await ( + await new AccountAllowanceApproveTransaction() + .approveTokenNftAllowance( + nftId, + receiverId, + spenderAccountId, + ) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + // reject nft + await ( + await ( + await new TokenRejectTransaction() + .addNftId(nftId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + // transfer nft from receiver to spender using allowance + try { + const transactionId = TransactionId.generate(spenderAccountId); + await ( + await ( + await new TransferTransaction() + .addApprovedNftTransfer( + nftId, + receiverId, + spenderAccountId, + ) + .setTransactionId(transactionId) + .freezeWith(env.client) + .sign(spenderAccountPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include( + "SPENDER_DOES_NOT_HAVE_ALLOWANCE", + ); + } + }); + + describe("should throw an error", function () { + it("when paused NFT", async function () { + this.timeout(120000); + + await ( + await new TokenPauseTransaction() + .setTokenId(tokenId) + .execute(env.client) + ).getReceipt(env.client); + + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client); + const tokenRejectTx = await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey); + + try { + await ( + await tokenRejectTx.execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("TOKEN_IS_PAUSED"); + } + }); + + it("when NFT is frozen", async function () { + this.timeout(120000); + + // transfer token to receiver + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client); + + // freeze token + await ( + await new TokenFreezeTransaction() + .setTokenId(tokenId) + .setAccountId(receiverId) + .execute(env.client) + ).getReceipt(env.client); + + try { + // reject token on frozen account for thsi token + await ( + await ( + await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("ACCOUNT_FROZEN_FOR_TOKEN"); + } + }); + + it("when using Fungible Token id when referencing NFTs", async function () { + this.timeout(120000); + + // transfer to receiver + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + + try { + // reject nft using addTokenId + await ( + await ( + await new TokenRejectTransaction() + .setOwnerId(receiverId) + .addTokenId(tokenId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include( + "ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON", + ); + } + + try { + // reject nft using setTokenIds + await ( + await ( + await new TokenRejectTransaction() + .setOwnerId(receiverId) + .setTokenIds([tokenId]) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include( + "ACCOUNT_AMOUNT_TRANSFERS_ONLY_ALLOWED_FOR_FUNGIBLE_COMMON", + ); + } + }); + + it("when there's a duplicated token reference", async function () { + this.timeout(120000); + + // transfer nft to receiver + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + + // reject nft + try { + await new TokenRejectTransaction() + .setNftIds([nftId, nftId]) + .execute(env.client); + } catch (err) { + expect(err.message).to.include("TOKEN_REFERENCE_REPEATED"); + } + }); + + it("when user does not have balance", async function () { + this.timeout(120000); + + // transfer nft to receiver + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + const transactionId = await TransactionId.generate(receiverId); + + try { + // reject nft + await ( + await ( + await new TokenRejectTransaction() + .setOwnerId(receiverId) + .addNftId(nftId) + .setTransactionId(transactionId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include( + "INSUFFICIENT_PAYER_BALANCE", + ); + } + }); + + it("when wrong signature of owner", async function () { + // transfer token to receiver + await new TransferTransaction() + .addTokenTransfer(tokenId, env.operatorId, -1000) + .addTokenTransfer(tokenId, receiverId, 1000); + + try { + // reject token with wrong signature + const WRONG_SIGNATURE = PrivateKey.generateED25519(); + await ( + await ( + await new TokenRejectTransaction() + .addTokenId(tokenId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(WRONG_SIGNATURE) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("INVALID_SIGNATURE"); + } + }); + + it("when wrong owner id", async function () { + this.timeout(120000); + + // generate wrong owner account + const wrongOwnerPrivateKey = PrivateKey.generateED25519(); + const { accountId: wrongOwnerId } = await ( + await new AccountCreateTransaction() + .setKey(wrongOwnerPrivateKey) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client) + ).getReceipt(env.client); + + // transfer token to receiver + await ( + await new TransferTransaction() + .addNftTransfer(nftId, env.operatorId, receiverId) + .execute(env.client) + ).getReceipt(env.client); + + try { + // reject token with wrong token id + await ( + await ( + await new TokenRejectTransaction() + .addNftId(nftId) + .setOwnerId(wrongOwnerId) + .freezeWith(env.client) + .sign(wrongOwnerPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("INVALID_OWNER_ID"); + } + }); + }); + }); + + describe("Other", function () { + beforeEach(async function () { + env = await IntegrationTestEnv.new(); + + // create token + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(env.operatorId) + .setPauseKey(env.operatorKey) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + + tokenId = (await tokenCreateResponse.getReceipt(env.client)) + .tokenId; + + // create receiver account + receiverPrivateKey = await PrivateKey.generateECDSA(); + const receiverCreateAccountResponse = + await new AccountCreateTransaction() + .setKey(receiverPrivateKey) + .setInitialBalance(new Hbar(1)) + .setMaxAutomaticTokenAssociations(-1) + .execute(env.client); + + receiverId = ( + await receiverCreateAccountResponse.getReceipt(env.client) + ).accountId; + }); + + it("should execute TokenReject tx with mixed type of tokens in one tx", async function () { + this.timeout(120000); + + // create NFT collection + const tokenCreateResponse = await new TokenCreateTransaction() + .setTokenType(TokenType.NonFungibleUnique) + .setTokenName("ffff") + .setTokenSymbol("F") + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + const { tokenId: nftId } = await tokenCreateResponse.getReceipt( + env.client, + ); + const nftSerialId = new NftId(nftId, 1); + + // create FT + const tokenCreateResponse2 = await new TokenCreateTransaction() + .setTokenName("ffff2") + .setTokenSymbol("F2") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(env.operatorKey) + .setSupplyKey(env.operatorKey) + .execute(env.client); + const { tokenId: ftId } = await tokenCreateResponse2.getReceipt( + env.client, + ); + + await ( + await new TokenMintTransaction() + .setTokenId(nftId) + .setMetadata(Buffer.from("-")) + .execute(env.client) + ).getReceipt(env.client); + + const tokenTransferResponse = await new TransferTransaction() + .addTokenTransfer(ftId, env.operatorId, -1) + .addTokenTransfer(ftId, receiverId, 1) + .addNftTransfer(nftSerialId, env.operatorId, receiverId) + .execute(env.client); + + await tokenTransferResponse.getReceipt(env.client); + + // reject tokens + await ( + await ( + await new TokenRejectTransaction() + .addTokenId(ftId) + .addNftId(nftSerialId) + .setOwnerId(receiverId) + .freezeWith(env.client) + .sign(receiverPrivateKey) + ).execute(env.client) + ).getReceipt(env.client); + + // check token balance of receiver + const tokenBalanceReceiverQuery = await new AccountBalanceQuery() + .setAccountId(receiverId) + .execute(env.client); + + const tokenBalanceFTReceiver = tokenBalanceReceiverQuery.tokens + .get(ftId) + .toInt(); + const tokenBalanceNFTReceiver = tokenBalanceReceiverQuery.tokens + .get(nftId) + .toInt(); + + expect(tokenBalanceFTReceiver).to.be.equal(0); + expect(tokenBalanceNFTReceiver).to.be.equal(0); + + // check token balance of treasury + const tokenBalanceTreasuryQuery = await new AccountBalanceQuery() + .setAccountId(env.operatorId) + .execute(env.client); + + const tokenBalanceTreasury = tokenBalanceTreasuryQuery.tokens + .get(ftId) + .toInt(); + const tokenBalance2Treasury = tokenBalanceTreasuryQuery.tokens + .get(nftId) + .toInt(); + + expect(tokenBalanceTreasury).to.be.equal(1000000); + expect(tokenBalance2Treasury).to.be.equal(1); + }); + + it("should throw if RejectToken transaction has empty token id list", async function () { + try { + await ( + await new TokenRejectTransaction().execute(env.client) + ).getReceipt(env.client); + } catch (err) { + expect(err.message).to.include("EMPTY_TOKEN_REFERENCE_LIST"); + } + }); + }); + + after(async function () { + await env.close(); + }); +}); diff --git a/test/integration/TokenRevokeKycIntegrationTest.js b/test/integration/TokenRevokeKycIntegrationTest.js index d1023d9fa..8bbe25881 100644 --- a/test/integration/TokenRevokeKycIntegrationTest.js +++ b/test/integration/TokenRevokeKycIntegrationTest.js @@ -1,12 +1,12 @@ import { AccountCreateTransaction, - // AccountInfoQuery, + AccountInfoQuery, Hbar, PrivateKey, Status, - // TokenAssociateTransaction, + TokenAssociateTransaction, TokenCreateTransaction, - // TokenGrantKycTransaction, + TokenGrantKycTransaction, TokenRevokeKycTransaction, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -18,97 +18,92 @@ describe("TokenRevokeKyc", function () { env = await IntegrationTestEnv.new(); }); - /** - * - * @description The test is temporarily commented because AccountInfoQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // await ( - // await ( - // await new TokenGrantKycTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // let info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // let relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.true; - // expect(relationship.isFrozen).to.be.false; - - // await ( - // await ( - // await new TokenRevokeKycTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.false; - // expect(relationship.isFrozen).to.be.false; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new TokenGrantKycTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + let info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + let relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.true; + expect(relationship.isFrozen).to.be.false; + + await ( + await ( + await new TokenRevokeKycTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.false; + expect(relationship.isFrozen).to.be.false; + }); it("should be executable even when no token IDs are set", async function () { this.timeout(120000); diff --git a/test/integration/TokenUnfreezeIntegrationTest.js b/test/integration/TokenUnfreezeIntegrationTest.js index ec4390e8f..81a841d37 100644 --- a/test/integration/TokenUnfreezeIntegrationTest.js +++ b/test/integration/TokenUnfreezeIntegrationTest.js @@ -1,12 +1,12 @@ import { AccountCreateTransaction, - // AccountInfoQuery, + AccountInfoQuery, Hbar, PrivateKey, Status, - // TokenAssociateTransaction, + TokenAssociateTransaction, TokenCreateTransaction, - // TokenFreezeTransaction, + TokenFreezeTransaction, TokenUnfreezeTransaction, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -18,97 +18,92 @@ describe("TokenUnfreeze", function () { env = await IntegrationTestEnv.new(); }); - /** - * - * @description The test is temporarily commented because AccountInfoQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // await ( - // await ( - // await new TokenFreezeTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // let info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // let relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.false; - // expect(relationship.isFrozen).to.be.true; - - // await ( - // await ( - // await new TokenUnfreezeTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.false; - // expect(relationship.isFrozen).to.be.false; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new TokenFreezeTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + let info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + let relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.false; + expect(relationship.isFrozen).to.be.true; + + await ( + await ( + await new TokenUnfreezeTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.false; + expect(relationship.isFrozen).to.be.false; + }); it("should be executable even when no token IDs are set", async function () { this.timeout(120000); diff --git a/test/integration/TokenUpdateIntegrationTest.js b/test/integration/TokenUpdateIntegrationTest.js index 7f5d47e3b..83ba700c3 100644 --- a/test/integration/TokenUpdateIntegrationTest.js +++ b/test/integration/TokenUpdateIntegrationTest.js @@ -11,6 +11,9 @@ import { TokenType, TokenUpdateTransaction, TransferTransaction, + KeyList, + TokenKeyValidation, + PublicKey, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -18,7 +21,7 @@ describe("TokenUpdate", function () { let env; before(async function () { - env = await IntegrationTestEnv.new(); + env = await IntegrationTestEnv.new({ balance: 1000 }); }); it("should be executable", async function () { @@ -31,6 +34,9 @@ describe("TokenUpdate", function () { const key3 = PrivateKey.generateED25519(); const key4 = PrivateKey.generateED25519(); const key5 = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + const newMetadataKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); const response = await new TokenCreateTransaction() .setTokenName("ffff") @@ -45,6 +51,8 @@ describe("TokenUpdate", function () { .setSupplyKey(key4) .setFreezeDefault(false) .setPauseKey(key5) + .setMetadata(metadata) + .setMetadataKey(metadataKey) .execute(env.client); const token = (await response.getReceipt(env.client)).tokenId; @@ -67,15 +75,15 @@ describe("TokenUpdate", function () { expect(info.wipeKey.toString()).to.eql(key3.publicKey.toString()); expect(info.supplyKey.toString()).to.eql(key4.publicKey.toString()); expect(info.pauseKey.toString()).to.eql(key5.publicKey.toString()); + expect(info.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + expect(info.metadata).to.eql(metadata); expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; await ( @@ -83,6 +91,7 @@ describe("TokenUpdate", function () { .setTokenId(token) .setTokenName("aaaa") .setTokenSymbol("A") + .setMetadataKey(newMetadataKey) .execute(env.client) ).getReceipt(env.client); @@ -101,15 +110,15 @@ describe("TokenUpdate", function () { expect(info.freezeKey.toString()).to.eql(key2.publicKey.toString()); expect(info.wipeKey.toString()).to.eql(key3.publicKey.toString()); expect(info.supplyKey.toString()).to.eql(key4.publicKey.toString()); + expect(info.metadataKey.toString()).to.eql( + newMetadataKey.publicKey.toString(), + ); + expect(info.metadata).to.eql(metadata); expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -171,12 +180,8 @@ describe("TokenUpdate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; await ( @@ -219,12 +224,8 @@ describe("TokenUpdate", function () { expect(info.defaultFreezeStatus).to.be.false; expect(info.defaultKycStatus).to.be.false; expect(info.isDeleted).to.be.false; - expect(info.autoRenewAccountId).to.be.not.null; - expect(info.autoRenewAccountId.toString()).to.be.eql( - operatorId.toString(), - ); - expect(info.autoRenewPeriod).to.be.not.null; - expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); + expect(info.autoRenewAccountId).to.be.null; + expect(info.autoRenewPeriod).to.be.null; expect(info.expirationTime).to.be.not.null; }); @@ -274,7 +275,7 @@ describe("TokenUpdate", function () { const token = (await response.getReceipt(env.client)).tokenId; - let err = false; + let status; try { await ( @@ -285,12 +286,10 @@ describe("TokenUpdate", function () { .execute(env.client) ).getReceipt(env.client); } catch (error) { - err = error.toString().includes(Status.TokenIsImmutable); + status = error.status; } - if (!err) { - throw new Error("token update did not error"); - } + expect(status).to.be.eql(Status.TokenIsImmutable); }); it("should error when token ID is not set", async function () { @@ -311,24 +310,32 @@ describe("TokenUpdate", function () { } }); - it("should be exectuable when updating immutable token, but not setting any fields besides token ID", async function () { + it("should return error when updating immutable token", async function () { this.timeout(120000); + let status; const operatorId = env.operatorId; - const response = await new TokenCreateTransaction() - .setTokenName("ffff") - .setTokenSymbol("F") - .setTreasuryAccountId(operatorId) - .execute(env.client); + try { + const response = await new TokenCreateTransaction() + .setTokenSymbol("F") + .setTokenName("ffff") + .setTreasuryAccountId(operatorId) + .execute(env.client); - const token = (await response.getReceipt(env.client)).tokenId; + const token = (await response.getReceipt(env.client)).tokenId; - await ( - await new TokenUpdateTransaction() - .setTokenId(token) - .execute(env.client) - ).getReceipt(env.client); + await ( + await new TokenUpdateTransaction() + .setTokenId(token) + .setTokenName("aaaa") + .execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.TokenIsImmutable); }); it("should error when admin key does not sign transaction", async function () { @@ -449,7 +456,6 @@ describe("TokenUpdate", function () { ).execute(env.client) ).getReceipt(env.client); } catch (error) { - console.log(error); err = error .toString() .includes(Status.CurrentTreasuryStillOwnsNfts); @@ -460,7 +466,2672 @@ describe("TokenUpdate", function () { } }); + describe("[HIP-646] Fungible Token Metadata Field", function () { + it("should update the metadata of token after signing the transaction with metadata key", async function () { + this.timeout(120000); + + let tokenInfo; + const operatorId = env.operatorId; + const metadataKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata) + .setMetadataKey(metadataKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await ( + await tokenUpdateTx.sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should update the metadata of token after signing the transaction with admin key", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const adminKey = env.operatorKey; + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + let tokenInfo; + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await (await tokenUpdateTx.sign(adminKey)).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should NOT update the metadata of token when the new metadata is NOT set", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const adminKey = env.operatorKey; + const metadata = new Uint8Array([1]); + let tokenInfo; + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .freezeWith(env.client); + + await ( + await (await tokenUpdateTx.sign(adminKey)).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(metadata); + }); + + it("should earse the metadata of token after signing the transaction with metadata key", async function () { + this.timeout(120000); + + let tokenInfo; + const operatorId = env.operatorId; + const metadataKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array(); + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata) + .setMetadataKey(metadataKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await ( + await tokenUpdateTx.sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should earse the metadata of token after signing the transaction with admin key", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const adminKey = env.operatorKey; + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array(); + let tokenInfo; + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata) + .setAdminKey(adminKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await (await tokenUpdateTx.sign(adminKey)).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should NOT update the metadata of token when the transaction is not signed with metadata or admin key", async function () { + this.timeout(120000); + + let status; + const operatorId = env.operatorId; + const adminKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + const wrongKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + + try { + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata) + .setMetadataKey(metadataKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = + await tokenCreateTxresponse.getReceipt(env.client); + const tokenId = tokenCreateTxReceipt.tokenId; + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await ( + await tokenUpdateTx.sign(wrongKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("should NOT update the metadata of token if the metadata or admin keys are NOT set", async function () { + this.timeout(120000); + + let status; + const operatorId = env.operatorId; + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + + try { + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setTokenType(TokenType.FungibleCommon) + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = + await tokenCreateTxresponse.getReceipt(env.client); + const tokenId = tokenCreateTxReceipt.tokenId; + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await tokenUpdateTx.execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + expect(status).to.be.eql(Status.TokenIsImmutable); + }); + }); + + describe("[HIP-765] Non Fungible Token Metadata Field", function () { + it("should update the metadata of token after signing the transaction with metadata key", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const metadataKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + let tokenInfo; + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(supplyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata) + .setMetadataKey(metadataKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await ( + await tokenUpdateTx.sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should update the metadata of token after signing the transaction with admin key", async function () { + this.timeout(120000); + + let tokenInfo; + const operatorId = env.operatorId; + const adminKey = env.operatorKey; + const supplyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(supplyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await (await tokenUpdateTx.sign(adminKey)).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should NOT update the metadata of token when the new metadata is NOT set", async function () { + this.timeout(120000); + + let tokenInfo; + const operatorId = env.operatorId; + const adminKey = env.operatorKey; + const supplyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(supplyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .freezeWith(env.client); + + await ( + await (await tokenUpdateTx.sign(adminKey)).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(metadata); + }); + + it("should earse the metadata of token after signing the transaction with metadata key", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const metadataKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array(); + let tokenInfo; + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(supplyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata) + .setMetadataKey(metadataKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await ( + await tokenUpdateTx.sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should earse the metadata of token after signing the transaction with admin key", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const adminKey = env.operatorKey; + const suppyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array(); + let tokenInfo; + + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(suppyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = await tokenCreateTxresponse.getReceipt( + env.client, + ); + const tokenId = tokenCreateTxReceipt.tokenId; + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + expect(tokenInfo.metadata).to.eql(metadata); + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await (await tokenUpdateTx.sign(adminKey)).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.metadata).to.eql(newMetadata); + }); + + it("should NOT update the metadata of token when the transaction is not signed with metadata or admin key", async function () { + this.timeout(120000); + + let status; + const operatorId = env.operatorId; + const adminKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + const wrongKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + + try { + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(supplyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setAdminKey(adminKey) + .setMetadata(metadata) + .setMetadataKey(metadataKey); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = + await tokenCreateTxresponse.getReceipt(env.client); + const tokenId = tokenCreateTxReceipt.tokenId; + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await ( + await tokenUpdateTx.sign(wrongKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("should NOT update the metadata of token if the metadata or admin keys are NOT set", async function () { + this.timeout(120000); + + let status; + const operatorId = env.operatorId; + const supplyKey = PrivateKey.generateED25519(); + const metadata = new Uint8Array([1]); + const newMetadata = new Uint8Array([1, 2]); + + try { + const tokenCreateTx = new TokenCreateTransaction() + .setTokenName("Test") + .setTokenSymbol("T") + .setSupplyKey(supplyKey) + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(operatorId) + .setMetadata(metadata); + + const tokenCreateTxresponse = await tokenCreateTx.execute( + env.client, + ); + const tokenCreateTxReceipt = + await tokenCreateTxresponse.getReceipt(env.client); + const tokenId = tokenCreateTxReceipt.tokenId; + + const tokenUpdateTx = new TokenUpdateTransaction() + .setTokenId(tokenId) + .setMetadata(newMetadata) + .freezeWith(env.client); + + await ( + await tokenUpdateTx.execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + expect(status).to.be.eql(Status.TokenIsImmutable); + }); + }); + + describe("[HIP-540] Change or remove existing keys from a token", function () { + it("Can make the token immutable when updating all of its keys to an empty KeyList, signing with an Admin Key, and setting the key verification mode to NO_VALIDATION.", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newKey = KeyList.of(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode(TokenKeyValidation.NoValidation) + .setAdminKey(newKey) + .setWipeKey(newKey) + .setFreezeKey(newKey) + .setPauseKey(newKey) + .setSupplyKey(newKey) + .setFeeScheduleKey(newKey) + .setMetadataKey(newKey) + .freezeWith(env.client) + .sign(adminKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey).to.be.null; + expect(tokenInfo.wipeKey).to.be.null; + expect(tokenInfo.freezeKey).to.be.null; + expect(tokenInfo.pauseKey).to.be.null; + expect(tokenInfo.supplyKey).to.be.null; + expect(tokenInfo.feeScheduleKey).to.be.null; + expect(tokenInfo.metadataKey).to.be.null; + }); + + it("Can remove all of token's lower-privilege keys when updating them to an empty KeyList, signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const emptyKeyList = KeyList.of(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey(emptyKeyList) + .setFreezeKey(emptyKeyList) + .setPauseKey(emptyKeyList) + .setSupplyKey(emptyKeyList) + .setFeeScheduleKey(emptyKeyList) + .setMetadataKey(emptyKeyList) + .freezeWith(env.client) + .sign(adminKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey).to.be.null; + expect(tokenInfo.freezeKey).to.be.null; + expect(tokenInfo.pauseKey).to.be.null; + expect(tokenInfo.supplyKey).to.be.null; + expect(tokenInfo.feeScheduleKey).to.be.null; + expect(tokenInfo.metadataKey).to.be.null; + }); + + it("Can update all of token's lower-privilege keys to an unusable key (i.e. all-zeros key) when signing with an Admin Key, and setting the key verification mode to FULL_VALIDATION and then set all lower-privilege keys back by signing with an Admin Key and setting key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey(unusableKey) + .setFreezeKey(unusableKey) + .setPauseKey(unusableKey) + .setSupplyKey(unusableKey) + .setFeeScheduleKey(unusableKey) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(adminKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql(unusableKey.toString()); + expect(tokenInfo.freezeKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + unusableKey.toString(), + ); + + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode(TokenKeyValidation.NoValidation) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client) + .sign(adminKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + }); + + it("Can update all of token's lower-privilege keys when signing with an Admin Key and new respective lower-privilege key, and setting key verification mode to FULL_VALIDATION", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newWipeKey = PrivateKey.generateED25519(); + const newFreezeKey = PrivateKey.generateED25519(); + const newPauseKey = PrivateKey.generateED25519(); + const newSupplyKey = PrivateKey.generateED25519(); + const newFeeScheduleKey = PrivateKey.generateED25519(); + const newMetadataKey = PrivateKey.generateED25519(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey(newWipeKey) + .setFreezeKey(newFreezeKey) + .setPauseKey(newPauseKey) + .setSupplyKey(newSupplyKey) + .setFeeScheduleKey(newFeeScheduleKey) + .setMetadataKey(newMetadataKey) + .freezeWith(env.client) + .sign(adminKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + newWipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + newFreezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + newPauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + newSupplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + newFeeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + newMetadataKey.publicKey.toString(), + ); + }); + + it("Cannot make the token immutable when updating all of its keys to an empty KeyList, signing with a key that is different from an Admin Key, and setting the key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newKey = KeyList.of(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setAdminKey(newKey) + .setWipeKey(newKey) + .setFreezeKey(newKey) + .setPauseKey(newKey) + .setSupplyKey(newKey) + .setFeeScheduleKey(newKey) + .setMetadataKey(newKey) + .freezeWith(env.client) + .sign(env.operatorKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Cannot make a token immutable when updating all of its keys to an unusable key (i.e. all-zeros key), signing with a key that is different from an Admin Key, and setting the key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setAdminKey(unusableKey) + .setWipeKey(unusableKey) + .setFreezeKey(unusableKey) + .setPauseKey(unusableKey) + .setSupplyKey(unusableKey) + .setFeeScheduleKey(unusableKey) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(env.operatorKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Cannot update the Admin Key to an unusable key (i.e. all-zeros key), signing with an Admin Key, and setting the key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const adminKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setAdminKey(adminKey) + .setSupplyKey(supplyKey) + .freezeWith(env.client); + + let response = await ( + await token.sign(adminKey) + ).execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.adminKey.toString()).to.eql( + adminKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setAdminKey(unusableKey) + .freezeWith(env.client) + .sign(adminKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Can update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setWipeKey(unusableKey) + .setFreezeKey(unusableKey) + .setPauseKey(unusableKey) + .setSupplyKey(unusableKey) + .setFeeScheduleKey(unusableKey) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(wipeKey) + ).sign(freezeKey) + ).sign(pauseKey) + ).sign(supplyKey) + ).sign(feeScheduleKey) + ).sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql(unusableKey.toString()); + expect(tokenInfo.freezeKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + unusableKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + unusableKey.toString(), + ); + }); + + it("Can update all of token’s lower-privilege keys when signing with an old respective lower-privilege key and a new respective lower-privilege key, and setting key verification mode to FULL_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newWipeKey = PrivateKey.generateED25519(); + const newFreezeKey = PrivateKey.generateED25519(); + const newPauseKey = PrivateKey.generateED25519(); + const newSupplyKey = PrivateKey.generateED25519(); + const newFeeScheduleKey = PrivateKey.generateED25519(); + const newMetadataKey = PrivateKey.generateED25519(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId( + tokenId, + ) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey( + newWipeKey, + ) + .setFreezeKey( + newFreezeKey, + ) + .setPauseKey( + newPauseKey, + ) + .setSupplyKey( + newSupplyKey, + ) + .setFeeScheduleKey( + newFeeScheduleKey, + ) + .setMetadataKey( + newMetadataKey, + ) + .freezeWith( + env.client, + ) + .sign( + wipeKey, + ) + ).sign(newWipeKey) + ).sign(freezeKey) + ).sign(newFreezeKey) + ).sign(pauseKey) + ).sign(newPauseKey) + ).sign(supplyKey) + ).sign(newSupplyKey) + ).sign(feeScheduleKey) + ).sign(newFeeScheduleKey) + ).sign(metadataKey) + ).sign(newMetadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + newWipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + newFreezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + newPauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + newSupplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + newFeeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + newMetadataKey.publicKey.toString(), + ); + }); + + it("Can update all of token's lower-privilege keys when signing ONLY with an old respective lower-privilege key and setting key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newWipeKey = PrivateKey.generateED25519(); + const newFreezeKey = PrivateKey.generateED25519(); + const newPauseKey = PrivateKey.generateED25519(); + const newSupplyKey = PrivateKey.generateED25519(); + const newFeeScheduleKey = PrivateKey.generateED25519(); + const newMetadataKey = PrivateKey.generateED25519(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setWipeKey(newWipeKey) + .setFreezeKey(newFreezeKey) + .setPauseKey(newPauseKey) + .setSupplyKey(newSupplyKey) + .setFeeScheduleKey( + newFeeScheduleKey, + ) + .setMetadataKey(newMetadataKey) + .freezeWith(env.client) + .sign(wipeKey) + ).sign(freezeKey) + ).sign(pauseKey) + ).sign(supplyKey) + ).sign(feeScheduleKey) + ).sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + + tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + newWipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + newFreezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + newPauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + newSupplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + newFeeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + newMetadataKey.publicKey.toString(), + ); + }); + + it("Cannot remove all of token's lower-privilege keys when updating them to an empty KeyList, signing with a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newKey = KeyList.of(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await ( + await ( + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setWipeKey(newKey) + .setFreezeKey(newKey) + .setPauseKey(newKey) + .setSupplyKey(newKey) + .setFeeScheduleKey(newKey) + .setMetadataKey(newKey) + .freezeWith(env.client) + .sign(wipeKey) + ).sign(freezeKey) + ).sign(pauseKey) + ).sign(supplyKey) + ).sign(feeScheduleKey) + ).sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.TokenIsImmutable); + }); + + it("Cannot update all of token’s lower-privilege keys to an unusable key (i.e. all-zeros key), when signing with a key that is different from a respective lower-privilege key, and setting the key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setWipeKey(unusableKey) + .setFreezeKey(unusableKey) + .setPauseKey(unusableKey) + .setSupplyKey(unusableKey) + .setFeeScheduleKey(unusableKey) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(env.operatorKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Cannot update all of token's lower-privilege keys to an unusable key (i.e. all-zeros key), when signing ONLY with an old respective lower-privilege key, and setting key verification mode to FULL_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey(unusableKey) + .freezeWith(env.client) + .sign(wipeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setFreezeKey(unusableKey) + .freezeWith(env.client) + .sign(freezeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setPauseKey(unusableKey) + .freezeWith(env.client) + .sign(pauseKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setSupplyKey(unusableKey) + .freezeWith(env.client) + .sign(supplyKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setFeeScheduleKey(unusableKey) + .freezeWith(env.client) + .sign(feeScheduleKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Cannot update all of token's lower-privilege to an unusable key (i.e. all-zeros key), when signing with an old respective lower-privilege key and a new respective lower-privilege key, and setting key verification mode to FULL_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const newWipeKey = PrivateKey.generateED25519(); + const newFreezeKey = PrivateKey.generateED25519(); + const newPauseKey = PrivateKey.generateED25519(); + const newSupplyKey = PrivateKey.generateED25519(); + const newFeeScheduleKey = PrivateKey.generateED25519(); + const newMetadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey(unusableKey) + .freezeWith(env.client) + .sign(wipeKey) + ).sign(newWipeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setFreezeKey(unusableKey) + .freezeWith(env.client) + .sign(freezeKey) + ).sign(newFreezeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setPauseKey(unusableKey) + .freezeWith(env.client) + .sign(pauseKey) + ).sign(newPauseKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setSupplyKey(unusableKey) + .freezeWith(env.client) + .sign(supplyKey) + ).sign(newSupplyKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setFeeScheduleKey(unusableKey) + .freezeWith(env.client) + .sign(feeScheduleKey) + ).sign(newFeeScheduleKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(metadataKey) + ).sign(newMetadataKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Cannot update all of token's lower-privilege keys when signing ONLY with an old respective lower-privilege key and setting key verification mode to FULL_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const unusableKey = PublicKey.unusableKey(); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setWipeKey(unusableKey) + .freezeWith(env.client) + .sign(wipeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setFreezeKey(unusableKey) + .freezeWith(env.client) + .sign(freezeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setPauseKey(unusableKey) + .freezeWith(env.client) + .sign(pauseKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setSupplyKey(unusableKey) + .freezeWith(env.client) + .sign(supplyKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setFeeScheduleKey(unusableKey) + .freezeWith(env.client) + .sign(feeScheduleKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.FullValidation, + ) + .setMetadataKey(unusableKey) + .freezeWith(env.client) + .sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSignature); + }); + + it("Cannot update all of token's lower-privilege keys when updating them to a keys with an invalid structure and signing with an old respective lower-privilege and setting key verification mode to NO_VALIDATION", async function () { + this.timeout(120000); + + const wipeKey = PrivateKey.generateED25519(); + const freezeKey = PrivateKey.generateED25519(); + const pauseKey = PrivateKey.generateED25519(); + const supplyKey = PrivateKey.generateED25519(); + const feeScheduleKey = PrivateKey.generateED25519(); + const metadataKey = PrivateKey.generateED25519(); + + const structurallyInvalidKey = PublicKey.fromString( + "000000000000000000000000000000000000000000000000000000000000000000", + ); + + let token = new TokenCreateTransaction() + .setTokenName("Token") + .setTokenSymbol("T") + .setTokenType(TokenType.NonFungibleUnique) + .setTreasuryAccountId(env.operatorId) + .setWipeKey(wipeKey) + .setFreezeKey(freezeKey) + .setPauseKey(pauseKey) + .setSupplyKey(supplyKey) + .setFeeScheduleKey(feeScheduleKey) + .setMetadataKey(metadataKey) + .freezeWith(env.client); + + let response = await token.execute(env.client); + const tokenId = (await response.getReceipt(env.client)).tokenId; + + let tokenInfo = await new TokenInfoQuery() + .setTokenId(tokenId) + .execute(env.client); + + expect(tokenInfo.name).to.eql(token.tokenName); + expect(tokenInfo.symbol).to.eql(token.tokenSymbol); + expect(tokenInfo.tokenType).to.eql(token.tokenType); + expect(tokenInfo.treasuryAccountId.toString()).to.eql( + token.treasuryAccountId.toString(), + ); + expect(tokenInfo.wipeKey.toString()).to.eql( + wipeKey.publicKey.toString(), + ); + expect(tokenInfo.freezeKey.toString()).to.eql( + freezeKey.publicKey.toString(), + ); + expect(tokenInfo.pauseKey.toString()).to.eql( + pauseKey.publicKey.toString(), + ); + expect(tokenInfo.supplyKey.toString()).to.eql( + supplyKey.publicKey.toString(), + ); + expect(tokenInfo.feeScheduleKey.toString()).to.eql( + feeScheduleKey.publicKey.toString(), + ); + expect(tokenInfo.metadataKey.toString()).to.eql( + metadataKey.publicKey.toString(), + ); + + let status; + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setWipeKey(structurallyInvalidKey) + .freezeWith(env.client) + .sign(wipeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidWipeKey); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setFreezeKey(structurallyInvalidKey) + .freezeWith(env.client) + .sign(freezeKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidFreezeKey); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setPauseKey(structurallyInvalidKey) + .freezeWith(env.client) + .sign(pauseKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidPauseKey); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setSupplyKey(structurallyInvalidKey) + .freezeWith(env.client) + .sign(supplyKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidSupplyKey); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setFeeScheduleKey(structurallyInvalidKey) + .freezeWith(env.client) + .sign(feeScheduleKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidCustomFeeScheduleKey); + + try { + await ( + await ( + await new TokenUpdateTransaction() + .setTokenId(tokenId) + .setKeyVerificationMode( + TokenKeyValidation.NoValidation, + ) + .setMetadataKey(structurallyInvalidKey) + .freezeWith(env.client) + .sign(metadataKey) + ).execute(env.client) + ).getReceipt(env.client); + } catch (error) { + status = error.status; + } + + expect(status).to.be.eql(Status.InvalidMetadataKey); + }); + }); + after(async function () { - await env.close(); + if (env != null) { + env.close(); + } }); }); diff --git a/test/integration/TokenWipeIntegrationTest.js b/test/integration/TokenWipeIntegrationTest.js index 37234d778..5d66b8e98 100644 --- a/test/integration/TokenWipeIntegrationTest.js +++ b/test/integration/TokenWipeIntegrationTest.js @@ -1,6 +1,6 @@ import { AccountCreateTransaction, - // AccountInfoQuery, + AccountInfoQuery, Hbar, PrivateKey, Status, @@ -8,7 +8,7 @@ import { TokenCreateTransaction, TokenGrantKycTransaction, TokenWipeTransaction, - // TransferTransaction, + TransferTransaction, Transaction, } from "../../src/exports.js"; import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; @@ -21,102 +21,97 @@ describe("TokenWipe", function () { env = await IntegrationTestEnv.new(); }); - /** - * - * @description The test is temporarily commented because AccountInfoQuery does a query to the consensus node which was deprecated. - * @todo Uncomment a test when the new query to the mirror node is implemented as it described here https://github.com/hashgraph/hedera-sdk-reference/issues/144 - */ - // it("should be executable", async function () { - // this.timeout(120000); - - // const operatorId = env.operatorId; - // const operatorKey = env.operatorKey.publicKey; - // const key = PrivateKey.generateED25519(); - - // const response = await new AccountCreateTransaction() - // .setKey(key) - // .setInitialBalance(new Hbar(2)) - // .execute(env.client); - - // const account = (await response.getReceipt(env.client)).accountId; - - // const token = ( - // await ( - // await new TokenCreateTransaction() - // .setTokenName("ffff") - // .setTokenSymbol("F") - // .setDecimals(3) - // .setInitialSupply(1000000) - // .setTreasuryAccountId(operatorId) - // .setAdminKey(operatorKey) - // .setKycKey(operatorKey) - // .setFreezeKey(operatorKey) - // .setWipeKey(operatorKey) - // .setSupplyKey(operatorKey) - // .setFreezeDefault(false) - // .execute(env.client) - // ).getReceipt(env.client) - // ).tokenId; - - // await ( - // await ( - // await new TokenAssociateTransaction() - // .setTokenIds([token]) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // await ( - // await ( - // await new TokenGrantKycTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .freezeWith(env.client) - // .sign(key) - // ).execute(env.client) - // ).getReceipt(env.client); - - // await ( - // await new TransferTransaction() - // .addTokenTransfer(token, account, 10) - // .addTokenTransfer(token, env.operatorId, -10) - // .execute(env.client) - // ).getReceipt(env.client); - - // let info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // let relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(10); - // expect(relationship.isKycGranted).to.be.true; - // expect(relationship.isFrozen).to.be.false; - - // await ( - // await new TokenWipeTransaction() - // .setTokenId(token) - // .setAccountId(account) - // .setAmount(10) - // .execute(env.client) - // ).getReceipt(env.client); - - // info = await new AccountInfoQuery() - // .setAccountId(account) - // .execute(env.client); - - // relationship = info.tokenRelationships.get(token); - - // expect(relationship).to.be.not.null; - // expect(relationship.tokenId.toString()).to.be.equal(token.toString()); - // expect(relationship.balance.toInt()).to.be.equal(0); - // expect(relationship.isKycGranted).to.be.true; - // expect(relationship.isFrozen).to.be.false; - // }); + it("should be executable", async function () { + this.timeout(120000); + + const operatorId = env.operatorId; + const operatorKey = env.operatorKey.publicKey; + const key = PrivateKey.generateED25519(); + + const response = await new AccountCreateTransaction() + .setKey(key) + .setInitialBalance(new Hbar(2)) + .execute(env.client); + + const account = (await response.getReceipt(env.client)).accountId; + + const token = ( + await ( + await new TokenCreateTransaction() + .setTokenName("ffff") + .setTokenSymbol("F") + .setDecimals(3) + .setInitialSupply(1000000) + .setTreasuryAccountId(operatorId) + .setAdminKey(operatorKey) + .setKycKey(operatorKey) + .setFreezeKey(operatorKey) + .setWipeKey(operatorKey) + .setSupplyKey(operatorKey) + .setFreezeDefault(false) + .execute(env.client) + ).getReceipt(env.client) + ).tokenId; + + await ( + await ( + await new TokenAssociateTransaction() + .setTokenIds([token]) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await ( + await new TokenGrantKycTransaction() + .setTokenId(token) + .setAccountId(account) + .freezeWith(env.client) + .sign(key) + ).execute(env.client) + ).getReceipt(env.client); + + await ( + await new TransferTransaction() + .addTokenTransfer(token, account, 10) + .addTokenTransfer(token, env.operatorId, -10) + .execute(env.client) + ).getReceipt(env.client); + + let info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + let relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(10); + expect(relationship.isKycGranted).to.be.true; + expect(relationship.isFrozen).to.be.false; + + await ( + await new TokenWipeTransaction() + .setTokenId(token) + .setAccountId(account) + .setAmount(10) + .execute(env.client) + ).getReceipt(env.client); + + info = await new AccountInfoQuery() + .setAccountId(account) + .execute(env.client); + + relationship = info.tokenRelationships.get(token); + + expect(relationship).to.be.not.null; + expect(relationship.tokenId.toString()).to.be.equal(token.toString()); + expect(relationship.balance.toInt()).to.be.equal(0); + expect(relationship.isKycGranted).to.be.true; + expect(relationship.isFrozen).to.be.false; + }); it("should error when token ID is not set", async function () { this.timeout(120000); @@ -274,6 +269,7 @@ describe("TokenWipe", function () { .execute(env.client); const account = (await response.getReceipt(env.client)).accountId; + const serials = [1, 2, 3]; const token = ( await ( @@ -295,16 +291,17 @@ describe("TokenWipe", function () { const transaction = new TokenWipeTransaction() .setTokenId(token) .setAccountId(account) - .setSerials([1, 2, 3]) + .setSerials(serials) .freezeWith(env.client) .toBytes(); const restoredTransaction = Transaction.fromBytes(transaction); - expect(restoredTransaction._serials).to.deep.equal([ - Long.fromNumber(1), - Long.fromNumber(2), - Long.fromNumber(3), - ]); + + expect(restoredTransaction.serials).to.be.an("array"); + expect(restoredTransaction.serials).to.have.length(3); + expect(restoredTransaction.serials.toString()).to.deep.eql( + serials.map((number) => Long.fromNumber(number)).toString(), + ); }); after(async function () { diff --git a/test/integration/TopicInfoIntegrationTest.js b/test/integration/TopicInfoIntegrationTest.js index 3990ec045..e02e9d4db 100644 --- a/test/integration/TopicInfoIntegrationTest.js +++ b/test/integration/TopicInfoIntegrationTest.js @@ -42,6 +42,8 @@ describe("TopicInfo", function () { ); expect(info.autoRenewPeriod.seconds.toInt()).to.be.eql(7776000); expect(info.expirationTime).to.be.not.null; + expect(info.metadataKey).to.be.not.null; + expect(info.metadata).to.be.not.null; await ( await new TopicDeleteTransaction() diff --git a/test/integration/TransactionReceiptIntegrationTest.js b/test/integration/TransactionReceiptIntegrationTest.js index 2c54010a3..2670c7a9f 100644 --- a/test/integration/TransactionReceiptIntegrationTest.js +++ b/test/integration/TransactionReceiptIntegrationTest.js @@ -11,6 +11,7 @@ import { import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js"; describe("TransactionReceipt", function () { + this.timeout(120000); let env; before(async function () { diff --git a/test/integration/client/BaseIntegrationTestEnv.js b/test/integration/client/BaseIntegrationTestEnv.js index 5a0c4d0c9..723d8d6c3 100644 --- a/test/integration/client/BaseIntegrationTestEnv.js +++ b/test/integration/client/BaseIntegrationTestEnv.js @@ -92,7 +92,7 @@ export default class BaseIntegrationTestEnv { options.env.OPERATOR_KEY != null ) { const operatorId = AccountId.fromString(options.env.OPERATOR_ID); - const operatorKey = PrivateKey.fromStringDer(options.env.OPERATOR_KEY); + const operatorKey = PrivateKey.fromStringED25519(options.env.OPERATOR_KEY); client.setOperator(operatorId, operatorKey); } diff --git a/test/unit/AccountInfoMocking.js b/test/unit/AccountInfoMocking.js index 2a532216b..10722ec55 100644 --- a/test/unit/AccountInfoMocking.js +++ b/test/unit/AccountInfoMocking.js @@ -118,6 +118,8 @@ describe("AccountInfoMocking", function () { it("should error when cost is greater than max cost set on client", async function () { this.timeout(10000); + let errorName; + ({ client, servers } = await Mocker.withResponses([ [ { response: ACCOUNT_INFO_QUERY_COST_RESPONSE }, @@ -127,17 +129,15 @@ describe("AccountInfoMocking", function () { client.setDefaultMaxQueryPayment(Hbar.fromTinybars(10)); - let err = false; - try { await new AccountInfoQuery() .setAccountId("0.0.3") .execute(client, 1); } catch (error) { - err = error instanceof MaxQueryPaymentExceeded; + errorName = error.name; } - expect(err).to.be.true; + expect(errorName).to.be.eql("MaxQueryPaymentExceededError"); }); it("setQueryPayemnt() avoids querying actual cost", async function () { @@ -154,7 +154,7 @@ describe("AccountInfoMocking", function () { await query.execute(client, 1); expect(query._queryPayment.toTinybars().toInt()).to.be.equal(10); - }); + }, 15000); it("setQueryPayemnt() + setMaxQueryPayment() avoids querying actual cost", async function () { this.timeout(10000); @@ -399,7 +399,7 @@ describe("AccountInfoMocking", function () { } catch (error) { if ( error.message !== - "transaction 0.0.1854@1651168054.029348185 failed precheck with status TRANSACTION_EXPIRED" + "transaction 0.0.1854@1651168054.029348185 failed precheck with status TRANSACTION_EXPIRED against node account id 0.0.3" ) { throw error; } diff --git a/test/unit/EcdsaPrivateKey.js b/test/unit/EcdsaPrivateKey.js index cdb8dfd5e..581b471e6 100644 --- a/test/unit/EcdsaPrivateKey.js +++ b/test/unit/EcdsaPrivateKey.js @@ -370,8 +370,6 @@ describe("EcdsaPrivateKey", function () { const privateKey = PrivateKey.generateECDSA(); const publicKey = privateKey.publicKey; - console.log(privateKey.type); - const aliasAccountId = publicKey.toAccountId(0, 0); expect(aliasAccountId.toString()).to.be.equal( diff --git a/test/unit/FileAppendMocking.js b/test/unit/FileAppendMocking.js index aab0ccf5d..e0c55883a 100644 --- a/test/unit/FileAppendMocking.js +++ b/test/unit/FileAppendMocking.js @@ -168,7 +168,7 @@ describe("FileAppendMocking", function () { } catch (error) { if ( error.message !== - "max attempts of 1 was reached for request with last error being: GrpcServiceError: node is UNAVAILABLE" + "max attempts of 1 was reached for request with last error being: GrpcServiceError: gRPC service failed with: Status: UNAVAILABLE, Code: 14: node is UNAVAILABLE" ) { throw error; } diff --git a/test/unit/LoggerTest.js b/test/unit/LoggerTest.js index 59d968a73..a42238df7 100644 --- a/test/unit/LoggerTest.js +++ b/test/unit/LoggerTest.js @@ -1,5 +1,8 @@ import { Logger, LogLevel, Transaction } from "../../src/exports.js"; import { Client } from "../../src/index.js"; +import { tmpdir } from "node:os"; +import fs from "fs"; +import { spy } from "sinon"; describe("Logger", function () { this.timeout(50000); @@ -68,4 +71,59 @@ describe("Logger", function () { expect(levels).to.include("error"); expect(levels).to.include("fatal"); }); + + it("check that it can write to a log file", function () { + const logFile = `${tmpdir()}/test.log`; + fs.rmSync(logFile, { force: true }); + const logger = new Logger(LogLevel.Trace, logFile); + let assertionCount = 0; + for (const level of Object.values(LogLevel)) { + if (level === LogLevel.Silent) continue; + logger[level](`This is a test ${level} message`); + + const logContent = fs.readFileSync(logFile, "utf8"); + expect(logContent).to.contain(`This is a test ${level} message`); + expect(logContent).to.contain( + level.toString().toUpperCase(), + `should contain ${level.toString().toUpperCase()}`, + ); + assertionCount += 2; + } + expect(assertionCount).to.be.equal( + 12, + "should have made 12 assertions", + ); + }); + + it("check that it can write to stdout", function () { + let assertionCount = 0; + const logger = new Logger(LogLevel.Trace); + for (const level of Object.values(LogLevel)) { + if (level === LogLevel.Silent) continue; + const loggerLogSpy = spy(logger._logger, level); + logger[level](`This is a test ${level} message`); + expect(loggerLogSpy.calledWith(`This is a test ${level} message`)) + .to.be.true; + assertionCount++; + } + expect(assertionCount).to.be.equal(6, "should have made 6 assertions"); + }); + + it("check that silent blocks output", function () { + const logFile = `${tmpdir()}/test2.log`; + fs.rmSync(logFile, { force: true }); + const logger = new Logger(LogLevel.Trace, logFile); + expect(logger.silent).to.be.equal(false); + logger.warn("This is a test warn message"); + logger.setSilent(true); + expect(logger.silent).to.be.equal(true); + logger.fatal("This is a test fatal message"); + logger.setSilent(false); + logger.error("This is a test error message"); + const logContent = fs.readFileSync(logFile, "utf8"); + expect(logger.silent).to.be.equal(false); + expect(logContent).to.contain("This is a test warn message"); + expect(logContent).to.contain("This is a test error message"); + expect(logContent).to.not.contain("This is a test fatal message"); + }); }); diff --git a/test/unit/Mnemonic.js b/test/unit/Mnemonic.js index 1f434c4a5..d1033eafc 100644 --- a/test/unit/Mnemonic.js +++ b/test/unit/Mnemonic.js @@ -496,4 +496,78 @@ describe("Mnemonic", function () { expect(key6.toStringRaw()).to.be.equal(PRIVATE_KEY6); expect(key6.publicKey.toStringRaw()).to.be.equal(PUBLIC_KEY6); }); + + it("Mnemonic.calculateDerivationPathValues() test vector", async function () { + const DPATH_1 = "m/44'/60'/0'/0/0"; + const DPATH_2 = "m/44/60/0/2147483647'/2147483646'"; + + const mnemonic1 = await Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + const result1 = await mnemonic1.calculateDerivationPathValues(DPATH_1); + const result2 = await mnemonic1.calculateDerivationPathValues(DPATH_2); + + // NOTE that 0x80000000 == 2147483648 + expect(result1).to.deep.equal([ + -2147483604, -2147483588, -2147483648, 0, 0, + ]); + expect(result2).to.deep.equal([44, 60, 0, -1, -2]); + }); + + it("Mnemonic.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath() test vector", async function () { + const DPATH_1 = "m/44'/60'/0'/0/0"; + const PASSPHRASE_1 = ""; + const CHAIN_CODE_1 = + "58a9ee31eaf7499abc01952b44dbf0a2a5d6447512367f09d99381c9605bf9e8"; + const PRIVATE_KEY_1 = + "78f9545e40025cf7da9126a4d6a861ae34031d1c74c3404df06110c9fde371ad"; + const PUBLIC_KEY_1 = + "02a8f4c22eea66617d4f119e3a951b93f584949bbfee90bd555305402da6c4e569"; + + const DPATH_2 = "m/44'/60'/0'/0/1"; + const PASSPHRASE_2 = ""; + const CHAIN_CODE_2 = + "6dcfc7a4914bd0e75b94a2f38afee8c247b34810202a2c64fe599ee1b88afdc9"; + const PRIVATE_KEY_2 = + "77ca263661ebdd5a8b33c224aeff5e7bf67eedacee68a1699d97ee8929d7b130"; + const PUBLIC_KEY_2 = + "03e84c9be9be53ad722038cc1943e79df27e5c1d31088adb4f0e62444f4dece683"; + + const DPATH_3 = "m/44'/60'/0'/0/2"; + const PASSPHRASE_3 = ""; + const CHAIN_CODE_3 = + "c8c798d2b3696be1e7a29d1cea205507eedc2057006b9ef1cde1b4e346089e17"; + const PRIVATE_KEY_3 = + "31c24292eac951279b659c335e44a2e812d0f1a228b1d4d87034874d376e605a"; + const PUBLIC_KEY_3 = + "0207ff3faf4055c1aa7a5ad94d6ff561fac35b9ae695ef486706243667d2b4d10e"; + + const mnemonic1 = await Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + const key1 = + await mnemonic1.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( + PASSPHRASE_1, + DPATH_1, + ); + expect(hex.encode(key1.chainCode)).to.equal(CHAIN_CODE_1); + expect(key1.toStringRaw()).to.equal(PRIVATE_KEY_1); + expect(key1.publicKey.toStringRaw()).to.include(PUBLIC_KEY_1); + + const mnemonic2 = await Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + const key2 = + await mnemonic2.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( + PASSPHRASE_2, + DPATH_2, + ); + expect(hex.encode(key2.chainCode)).to.equal(CHAIN_CODE_2); + expect(key2.toStringRaw()).to.equal(PRIVATE_KEY_2); + expect(key2.publicKey.toStringRaw()).to.include(PUBLIC_KEY_2); + + const mnemonic3 = await Mnemonic.fromString(MNEMONIC_24_WORD_STRING); + const key3 = + await mnemonic3.toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( + PASSPHRASE_3, + DPATH_3, + ); + expect(hex.encode(key3.chainCode)).to.equal(CHAIN_CODE_3); + expect(key3.toStringRaw()).to.equal(PRIVATE_KEY_3); + expect(key3.publicKey.toStringRaw()).to.include(PUBLIC_KEY_3); + }); }); diff --git a/test/unit/TokenCreateTransaction.js b/test/unit/TokenCreateTransaction.js index 7fc942503..6e0aba90d 100644 --- a/test/unit/TokenCreateTransaction.js +++ b/test/unit/TokenCreateTransaction.js @@ -7,6 +7,7 @@ import { Timestamp, } from "../../src/index.js"; import Long from "long"; +import { DEFAULT_AUTO_RENEW_PERIOD } from "../../src/transaction/Transaction.js"; describe("TokenCreateTransaction", function () { it("encodes to correct protobuf", function () { @@ -31,9 +32,20 @@ describe("TokenCreateTransaction", function () { const key7 = PrivateKey.fromStringDer( "302e020100300506032b657004220420542b4d4a318a1ae5f91071f34c8d900b1150e83d15fe71d22b8581e1203f99ad", ); + const key8 = PrivateKey.fromStringDer( + "302e020100300506032b6570042204205447805ce906170817e2bd4e26f4ea1fd5bbc38a2532c7f66b7d7a24f60ee9d5", + ); + const metadata = new Uint8Array([1, 2, 3, 4, 5]); const autoRenewAccountId = new AccountId(10); const treasuryAccountId = new AccountId(11); + const expirationTime = new Timestamp( + Math.floor( + Date.now() / 1000 + DEFAULT_AUTO_RENEW_PERIOD.toNumber(), + ), + 0, + ); + const transaction = new TokenCreateTransaction() .setMaxTransactionFee(new Hbar(30)) .setTransactionId( @@ -48,6 +60,7 @@ describe("TokenCreateTransaction", function () { .setDecimals(7) .setTreasuryAccountId(treasuryAccountId) .setAutoRenewAccountId(autoRenewAccountId) + .setExpirationTime(expirationTime) .setAdminKey(key1) .setKycKey(key2) .setFreezeKey(key3) @@ -55,6 +68,8 @@ describe("TokenCreateTransaction", function () { .setWipeKey(key5) .setSupplyKey(key6) .setFeeScheduleKey(key7) + .setMetadata(metadata) + .setMetadataKey(key8) .setNodeAccountIds([new AccountId(4)]) .setTransactionMemo("random memo") .freeze(); @@ -77,7 +92,7 @@ describe("TokenCreateTransaction", function () { autoRenewPeriod: { seconds: Long.fromValue(7776000), }, - expiry: null, + expiry: expirationTime._toProtobuf(), treasury: treasuryAccountId._toProtobuf(), adminKey: { ed25519: key1.publicKey.toBytesRaw(), @@ -100,6 +115,10 @@ describe("TokenCreateTransaction", function () { feeScheduleKey: { ed25519: key7.publicKey.toBytesRaw(), }, + metadata: metadata, + metadataKey: { + ed25519: key8.publicKey.toBytesRaw(), + }, }, transactionFee: new Hbar(30).toTinybars(), memo: "random memo", diff --git a/test/unit/TokenRejectFlow.js b/test/unit/TokenRejectFlow.js new file mode 100644 index 000000000..33c10ab4d --- /dev/null +++ b/test/unit/TokenRejectFlow.js @@ -0,0 +1,81 @@ +/* eslint-disable mocha/no-setup-in-describe */ + +import { + AccountId, + Client, + NftId, + TokenId, + TokenRejectFlow, +} from "../../src/index.js"; + +describe("TokenRejectFlow", function () { + let tokenIds = [ + TokenId.fromString("1.2.3"), + TokenId.fromString("1.2.4"), + TokenId.fromString("1.2.5"), + ]; + + let nftIds = [ + new NftId(tokenIds[0], 1), + new NftId(tokenIds[1], 2), + new NftId(tokenIds[2], 3), + ]; + + let tx; + + it("should set owner id", function () { + const owner = new AccountId(1); + tx = new TokenRejectFlow().setOwnerId(owner); + expect(tx.ownerId.toString()).to.equal(owner.toString()); + }); + + it("set owner id when frozen", async function () { + const client = Client.forLocalNode(); + tx = new TokenRejectFlow().addNftId(nftIds[0]).freezeWith(client); + + let err = false; + try { + tx.setOwnerId(new AccountId(2)); + } catch (error) { + err = true; + } + + expect(err).to.equal(true); + }); + + it("should set token ids", function () { + const tx = new TokenRejectFlow().setTokenIds(tokenIds); + expect(tx.tokenIds).to.deep.equal(tokenIds); + }); + + it("should not be able to set token ids frozen", function () { + const client = Client.forLocalNode(); + const tx = new TokenRejectFlow().setTokenIds().freezeWith(client); + let err = false; + try { + tx.setTokenIds(tokenIds); + } catch (error) { + err = true; + } + + expect(err).to.equal(true); + }); + + it("should be able to set token nft ids", function () { + const tx = new TokenRejectFlow().setNftIds(nftIds); + expect(tx.nftIds).to.deep.equal(nftIds); + }); + + it("should not be able to set nft ids frozen", function () { + const client = Client.forLocalNode(); + const tx = new TokenRejectFlow().setNftIds().freezeWith(client); + let err = false; + try { + tx.setNftIds(nftIds); + } catch (error) { + err = true; + } + + expect(err).to.equal(true); + }); +}); diff --git a/test/unit/TokenRejectTransaction.js b/test/unit/TokenRejectTransaction.js new file mode 100644 index 000000000..eb8b785bd --- /dev/null +++ b/test/unit/TokenRejectTransaction.js @@ -0,0 +1,112 @@ +/* eslint-disable mocha/no-setup-in-describe */ +import { + AccountId, + NftId, + Timestamp, + TokenId, + TokenRejectTransaction, + Transaction, + TransactionId, +} from "../../src/index.js"; + +describe("Transaction", function () { + const owner = new AccountId(1); + const tokenIds = [new TokenId(2)]; + const nftId = new NftId(tokenIds[0], 3); + it("encodes to correct protobuf", async function () { + const owner = new AccountId(1); + const tokenReject = new TokenRejectTransaction() + .setOwnerId(owner) + .setTokenIds(tokenIds) + .setNftIds([nftId]); + + const protobuf = await tokenReject._makeTransactionData(); + expect(protobuf).to.deep.include({ + owner: owner._toProtobuf(), + rejections: [ + { + fungibleToken: tokenIds[0]._toProtobuf(), + }, + { + nft: nftId._toProtobuf(), + }, + ], + }); + }); + + it("decodes from protobuf", async function () { + const tx = new TokenRejectTransaction() + .setOwnerId(owner) + .setTokenIds(tokenIds) + .setNftIds([nftId]); + + const decodedBackTx = Transaction.fromBytes(tx.toBytes()); + expect(tx.ownerId.toString()).to.equal( + decodedBackTx.ownerId.toString(), + ); + expect(tx.tokenIds.toString()).to.equal( + decodedBackTx.tokenIds.toString(), + ); + expect(tx.nftIds.toString()).to.equal(decodedBackTx.nftIds.toString()); + }); + + it("should set owner id", function () { + const owner = new AccountId(1); + const tx = new TokenRejectTransaction().setOwnerId(owner); + expect(tx.ownerId).to.equal(owner); + }); + + it("should revert when updating owner id while frozen", function () { + const owner = new AccountId(1); + const timestamp = new Timestamp(14, 15); + + const tx = new TokenRejectTransaction() + .setTransactionId(TransactionId.withValidStart(owner, timestamp)) + .setNodeAccountIds([new AccountId(10, 11, 12)]) + .freeze(); + + expect(() => tx.setOwnerId(new AccountId(2))).to.throw( + "transaction is immutable; it has at least one signature or has been explicitly frozen", + ); + }); + + it("should set token ids", function () { + const tokenIds = [new TokenId(1), new TokenId(2)]; + const tx = new TokenRejectTransaction().setTokenIds(tokenIds); + expect(tx.tokenIds).to.deep.equal(tokenIds); + }); + + it("should revert when updating token ids when frozen", function () { + const tokenIds = [new TokenId(1), new TokenId(2)]; + const owner = new AccountId(1); + const timestamp = new Timestamp(14, 15); + + const tx = new TokenRejectTransaction() + .setNodeAccountIds([new AccountId(10, 11, 12)]) + .setTransactionId(TransactionId.withValidStart(owner, timestamp)) + .freeze(); + expect(() => tx.setTokenIds(tokenIds)).to.throw( + "transaction is immutable; it has at least one signature or has been explicitly frozen", + ); + }); + + it("should set nft ids", function () { + const nftIds = [new NftId(1), new NftId(2)]; + const tx = new TokenRejectTransaction().setNftIds(nftIds); + expect(tx.nftIds).to.deep.equal(nftIds); + }); + + it("should revert when updating nft ids when frozen", function () { + const nftIds = [new NftId(1), new NftId(2)]; + const owner = new AccountId(1); + const timestamp = new Timestamp(14, 15); + + const tx = new TokenRejectTransaction() + .setNodeAccountIds([new AccountId(10, 11, 12)]) + .setTransactionId(TransactionId.withValidStart(owner, timestamp)) + .freeze(); + expect(() => tx.setNftIds(nftIds)).to.throw( + "transaction is immutable; it has at least one signature or has been explicitly frozen", + ); + }); +}); diff --git a/test/unit/TokenUpdateTransaction.js b/test/unit/TokenUpdateTransaction.js index a8c83c5ed..d1b4f145a 100644 --- a/test/unit/TokenUpdateTransaction.js +++ b/test/unit/TokenUpdateTransaction.js @@ -4,6 +4,7 @@ import { TransactionId, AccountId, Timestamp, + TokenKeyValidation, } from "../../src/index.js"; import Long from "long"; @@ -30,6 +31,10 @@ describe("TokenUpdateTransaction", function () { const key7 = PrivateKey.fromStringDer( "302e020100300506032b657004220420542b4d4a318a1ae5f91071f34c8d900b1150e83d15fe71d22b8581e1203f99ad", ); + const key8 = PrivateKey.fromStringDer( + "302e020100300506032b6570042204205447805ce906170817e2bd4e26f4ea1fd5bbc38a2532c7f66b7d7a24f60ee9d5", + ); + const metadata = new Uint8Array([1, 2, 3, 4, 5]); const autoRenewAccountId = new AccountId(10); const treasuryAccountId = new AccountId(11); @@ -52,6 +57,8 @@ describe("TokenUpdateTransaction", function () { .setWipeKey(key5) .setSupplyKey(key6) .setFeeScheduleKey(key7) + .setMetadata(metadata) + .setMetadataKey(key8) .setNodeAccountIds([new AccountId(4)]) .setTokenId("0.0.5") .setTransactionMemo("random memo") @@ -75,6 +82,8 @@ describe("TokenUpdateTransaction", function () { autoRenewPeriod: null, expiry: null, treasury: treasuryAccountId._toProtobuf(), + keyVerificationMode: + TokenKeyValidation.FullValidation.valueOf(), adminKey: { ed25519: key1.publicKey.toBytesRaw(), }, @@ -96,6 +105,12 @@ describe("TokenUpdateTransaction", function () { feeScheduleKey: { ed25519: key7.publicKey.toBytesRaw(), }, + metadata: { + value: metadata, + }, + metadataKey: { + ed25519: key8.publicKey.toBytesRaw(), + }, }, transactionFee: Long.fromNumber(200000000), memo: "random memo", diff --git a/test/unit/TransferTransaction.js b/test/unit/TransferTransaction.js index 770ce963c..1c7a2fa43 100644 --- a/test/unit/TransferTransaction.js +++ b/test/unit/TransferTransaction.js @@ -374,4 +374,27 @@ describe("TransferTransaction", function () { ], }); }); + + it("should return hbarTransfer list", function () { + const accountId1 = AccountId.fromString("0.0.0"); + const accountId2 = AccountId.fromString("0.0.1"); + const amount = new Hbar(1); + + const tx = new TransferTransaction() + .addHbarTransfer(accountId1, amount.negated()) + .addHbarTransfer(accountId2, amount); + + expect(tx.hbarTransfersList).to.deep.equal([ + { + accountId: accountId1, + amount: amount.negated(), + isApproved: false, + }, + { + accountId: accountId2, + amount: amount, + isApproved: false, + }, + ]); + }); });