diff --git a/.github/package-filters/js-packages-direct.yml b/.github/package-filters/js-packages-direct.yml index c4efc002748..75845654bdd 100644 --- a/.github/package-filters/js-packages-direct.yml +++ b/.github/package-filters/js-packages-direct.yml @@ -45,3 +45,9 @@ dashmate: '@dashevo/platform-test-suite': - packages/platform-test-suite/** + +'@dashevo/wasm-sdk': + - packages/wasm-sdk/** + +'@dashevo/evo-sdk': + - packages/js-evo-sdk/** diff --git a/.github/package-filters/js-packages-no-workflows.yml b/.github/package-filters/js-packages-no-workflows.yml index bb0ca7da81f..cf3f50f2ad1 100644 --- a/.github/package-filters/js-packages-no-workflows.yml +++ b/.github/package-filters/js-packages-no-workflows.yml @@ -77,3 +77,16 @@ dashmate: '@dashevo/platform-test-suite': - packages/platform-test-suite/** - *dash + +'@dashevo/wasm-sdk': &wasm-sdk + - packages/wasm-sdk/** + - packages/rs-drive-proof-verifier/** + - packages/rs-sdk/** + - packages/rs-dapi-client/** + - packages/rs-platform-version/** + - packages/rs-dapi-grpc-macros/** + - packages/dapi-grpc/** + +'@dashevo/evo-sdk': &evo-sdk + - packages/js-evo-sdk/** + - *wasm-sdk diff --git a/.github/package-filters/js-packages.yml b/.github/package-filters/js-packages.yml index 0c4e0260fa7..e6c20791042 100644 --- a/.github/package-filters/js-packages.yml +++ b/.github/package-filters/js-packages.yml @@ -93,3 +93,20 @@ dashmate: - .github/workflows/tests* - packages/platform-test-suite/** - *dash + +'@dashevo/wasm-sdk': &wasm-sdk + - .github/workflows/tests* + - packages/wasm-sdk/** + - packages/rs-drive-proof-verifier/** + - packages/rs-sdk/** + - packages/rs-dapi-client/** + - packages/rs-platform-version/** + - packages/rs-dapi-grpc-macros/** + - packages/dapi-grpc/** + +'@dashevo/evo-sdk': &evo-sdk + - .github/workflows/tests* + - packages/js-evo-sdk/** + - *wasm-sdk + + diff --git a/.github/package-filters/rs-packages-direct.yml b/.github/package-filters/rs-packages-direct.yml index 0d2d079b407..5cb84f53283 100644 --- a/.github/package-filters/rs-packages-direct.yml +++ b/.github/package-filters/rs-packages-direct.yml @@ -42,3 +42,6 @@ dash-sdk: rs-sdk-ffi: - packages/rs-sdk-ffi/** + +wasm-sdk: + - packages/wasm-sdk/** diff --git a/.github/package-filters/rs-packages-no-workflows.yml b/.github/package-filters/rs-packages-no-workflows.yml index 54417389b16..51c1f01fd43 100644 --- a/.github/package-filters/rs-packages-no-workflows.yml +++ b/.github/package-filters/rs-packages-no-workflows.yml @@ -56,7 +56,7 @@ rs-dapi-client: &dapi_client - packages/rs-dapi-client/** - *dapi_grpc -dash-sdk: +dash-sdk: &sdk - packages/rs-drive-proof-verifier/** - packages/rs-sdk/** - *dapi_client @@ -68,3 +68,8 @@ rs-sdk-ffi: - packages/rs-drive-proof-verifier/** - *dapi_client - *drive + +wasm-sdk: + - .github/workflows/tests* + - packages/wasm-sdk/** + - *sdk diff --git a/.github/package-filters/rs-packages.yml b/.github/package-filters/rs-packages.yml index 57d2417cbb5..78129be673c 100644 --- a/.github/package-filters/rs-packages.yml +++ b/.github/package-filters/rs-packages.yml @@ -69,7 +69,7 @@ rs-dapi-client: &dapi_client - packages/rs-dapi-client/** - *dapi_grpc -dash-sdk: +dash-sdk: &sdk - .github/workflows/tests* - packages/rs-drive-proof-verifier/** - packages/rs-sdk/** @@ -83,3 +83,8 @@ rs-sdk-ffi: - packages/rs-drive-proof-verifier/** - *dapi_client - *drive + +wasm-sdk: + - .github/workflows/tests* + - packages/wasm-sdk/** + - *sdk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d595548df23..8040aa1620f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,6 +73,10 @@ jobs: run: cargo binstall wasm-bindgen-cli@0.2.100 if: ${{ steps.check-artifact.outputs.exists != 'true' }} + - name: Install wasm-pack + run: cargo binstall wasm-pack + if: ${{ steps.check-artifact.outputs.exists != 'true' }} + - name: Install Binaryen run: | wget https://github.com/WebAssembly/binaryen/releases/download/version_121/binaryen-version_121-x86_64-linux.tar.gz -P /tmp diff --git a/.github/workflows/tests-build-js.yml b/.github/workflows/tests-build-js.yml index a2a70ff1d75..86eb576bf4d 100644 --- a/.github/workflows/tests-build-js.yml +++ b/.github/workflows/tests-build-js.yml @@ -57,32 +57,8 @@ jobs: run: cargo binstall wasm-bindgen-cli@0.2.100 if: ${{ steps.check-artifact.outputs.exists != 'true' }} - - name: Restore cached wasm-opt (Binaryen) - id: cache-binaryen - uses: actions/cache@v4 - with: - path: ${{ env.HOME }}/.cache/binaryen/version_121 - key: binaryen/version_121/${{ runner.os }}/x86_64 - - - name: Install wasm-opt if cache miss - if: steps.cache-binaryen.outputs.cache-hit != 'true' - run: | - set -euxo pipefail - mkdir -p "${HOME}/.cache/binaryen" - curl -fsSL -o /tmp/binaryen.tar.gz \ - "https://github.com/WebAssembly/binaryen/releases/download/version_121/binaryen-version_121-x86_64-linux.tar.gz" - tar -xzf /tmp/binaryen.tar.gz -C "${HOME}/.cache/binaryen" - mv "${HOME}/.cache/binaryen/binaryen-version_121" "${HOME}/.cache/binaryen/version_121" - - - name: Save cached wasm-opt - if: steps.cache-binaryen.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 - with: - path: ${{ env.HOME }}/.cache/binaryen/version_121 - key: binaryen/version_121/${{ runner.os }}/x86_64 - - - name: Export wasm-opt to PATH - run: echo "${HOME}/.cache/binaryen/version_121/bin" >> $GITHUB_PATH + - name: Install wasm-pack + run: cargo binstall wasm-pack if: ${{ steps.check-artifact.outputs.exists != 'true' }} - name: Build JS packages diff --git a/.github/workflows/wasm-sdk-build.yml b/.github/workflows/wasm-sdk-build.yml deleted file mode 100644 index 4b1b0d5b5a0..00000000000 --- a/.github/workflows/wasm-sdk-build.yml +++ /dev/null @@ -1,248 +0,0 @@ -name: Build and Test WASM SDK - -on: - pull_request: - paths: - - 'packages/wasm-sdk/**' - - 'packages/rs-sdk/**' - - 'packages/rs-drive-proof-verifier/**' - - 'packages/rs-platform-value/**' - - 'packages/rs-dpp/**' - - 'packages/rs-drive/src/verify/**' - - 'packages/rs-context-provider/**' - push: - branches: - - main - - master - - 'v[0-9]+.[0-9]+-dev' - - 'v[0-9]+.[0-9]+-dev-sdk' - paths: - - 'packages/wasm-sdk/**' - - 'packages/rs-sdk/**' - - 'packages/rs-drive-proof-verifier/**' - - 'packages/rs-platform-value/**' - - 'packages/rs-dpp/**' - - 'packages/rs-drive/src/verify/**' - - 'packages/rs-context-provider/**' - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - RUSTFLAGS: "-C lto=off" - CARGO_PROFILE_RELEASE_LTO: false - CI: true - -jobs: - build-wasm-sdk: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - targets: wasm32-unknown-unknown - - - name: Cache and install protoc (v32.0) - uses: actions/cache@v4 - id: cache-protoc - with: - path: | - ${{ env.HOME }}/.local/protoc-32.0/bin - ${{ env.HOME }}/.local/protoc-32.0/include - key: protoc/32.0/${{ runner.os }}/x86_64 - - - name: Install protoc v32.0 if cache miss - if: steps.cache-protoc.outputs.cache-hit != 'true' - run: | - set -euxo pipefail - PROTOC_DIR="${HOME}/.local/protoc-32.0" - mkdir -p "$PROTOC_DIR" - curl -fsSL -o /tmp/protoc.zip \ - "https://github.com/protocolbuffers/protobuf/releases/download/v32.0/protoc-32.0-linux-x86_64.zip" - unzip -o /tmp/protoc.zip -d "$PROTOC_DIR" - - - name: Export protoc v32.0 to PATH - run: | - echo "${HOME}/.local/protoc-32.0/bin" >> $GITHUB_PATH - echo "PROTOC=${HOME}/.local/protoc-32.0/bin/protoc" >> $GITHUB_ENV - - - name: Install clang and llvm - run: | - sudo apt update -qq - sudo apt install -qq --yes clang llvm - - - name: Cache cargo dependencies - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: ${{ runner.os }}-cargo-wasm-sdk-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-wasm-sdk- - - - name: Install wasm-pack - run: | - if ! command -v wasm-pack &> /dev/null; then - echo "Installing wasm-pack..." - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - else - echo "wasm-pack already installed" - fi - - - name: Cache and install wasm-opt (Binaryen) - uses: actions/cache@v4 - id: cache-binaryen - with: - path: ${{ env.HOME }}/.cache/binaryen/version_121 - key: binaryen/version_121/${{ runner.os }}/x86_64 - - - name: Install wasm-opt if cache miss - if: steps.cache-binaryen.outputs.cache-hit != 'true' - run: | - set -euxo pipefail - mkdir -p "${HOME}/.cache/binaryen" - curl -fsSL -o /tmp/binaryen.tar.gz \ - "https://github.com/WebAssembly/binaryen/releases/download/version_121/binaryen-version_121-x86_64-linux.tar.gz" - tar -xzf /tmp/binaryen.tar.gz -C "${HOME}/.cache/binaryen" - mv "${HOME}/.cache/binaryen/binaryen-version_121" "${HOME}/.cache/binaryen/version_121" - - - name: Export wasm-opt to PATH - run: | - echo "${HOME}/.cache/binaryen/version_121/bin" >> $GITHUB_PATH - - - name: Build WASM SDK - working-directory: packages/wasm-sdk - run: | - chmod +x build.sh - ./build.sh - - - name: Verify build output - working-directory: packages/wasm-sdk - run: | - echo "Checking build output..." - ls -lah pkg/ - # Verify required files exist - test -f pkg/wasm_sdk_bg.wasm - test -f pkg/optimized.wasm - test -f pkg/wasm_sdk.js - test -f pkg/wasm_sdk.d.ts - test -f pkg/package.json - echo "Build verification successful!" - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: wasm-sdk-build - path: packages/wasm-sdk/pkg/ - retention-days: 7 - - test-wasm-sdk: - runs-on: ubuntu-latest - needs: build-wasm-sdk - - steps: - - name: Checkout test directory only - uses: actions/checkout@v4 - with: - sparse-checkout: | - packages/wasm-sdk/test - sparse-checkout-cone-mode: false - - - name: Download WASM SDK build artifacts - uses: actions/download-artifact@v4 - with: - name: wasm-sdk-build - path: packages/wasm-sdk/pkg/ - - - name: Verify WASM SDK artifacts - working-directory: packages/wasm-sdk - run: | - echo "Verifying downloaded WASM SDK artifacts..." - ls -lah pkg/ - - # Verify all required files exist - required_files=( - "pkg/wasm_sdk_bg.wasm" - "pkg/optimized.wasm" - "pkg/wasm_sdk.js" - "pkg/wasm_sdk.d.ts" - "pkg/package.json" - ) - - for file in "${required_files[@]}"; do - if [ ! -f "$file" ]; then - echo "❌ Missing required file: $file" - exit 1 - else - echo "βœ… Found: $file" - fi - done - - echo "πŸŽ‰ All WASM SDK artifacts verified successfully!" - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install test dependencies - working-directory: packages/wasm-sdk/test - run: | - if [ -f package.json ]; then - npm install - fi - - - name: Run comprehensive test suite - working-directory: packages/wasm-sdk - run: | - echo "Running WASM SDK comprehensive test suite..." - set -o pipefail && node test/run-all-tests.mjs | tee test-output.log - - - name: Generate job summary - if: always() - working-directory: packages/wasm-sdk - run: | - echo "## πŸ§ͺ WASM SDK Test Results" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Extract test results from the test output - if [ -f test-output.log ]; then - # Extract overall summary - total_tests=$(grep -o "Total Tests: [0-9]*" test-output.log | grep -o "[0-9]*" || echo "0") - total_passed=$(grep -o "Passed: [0-9]*" test-output.log | grep -o "[0-9]*" || echo "0") - total_failed=$(grep -o "Failed: [0-9]*" test-output.log | grep -o "[0-9]*" || echo "0") - total_time=$(grep -o "Time: [0-9]*\.[0-9]*s" test-output.log | grep -o "[0-9]*\.[0-9]*" || echo "0.00") - - # Display overall summary - echo "**$total_tests** tests β€’ **$total_passed** passed β€’ **$total_failed** failed β€’ **${total_time}s**" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - if [ "$total_failed" != "0" ]; then - echo "❌ **Some tests failed** - Check the detailed test report for specifics" >> $GITHUB_STEP_SUMMARY - else - echo "βœ… **All tests passed**" >> $GITHUB_STEP_SUMMARY - fi - else - echo "⚠️ No test output captured" >> $GITHUB_STEP_SUMMARY - fi - - echo "" >> $GITHUB_STEP_SUMMARY - echo "πŸ“¦ **Artifacts**: WASM SDK build files and detailed test report available for download" >> $GITHUB_STEP_SUMMARY - - - name: Upload test report - if: always() && hashFiles('packages/wasm-sdk/test/test-report.html') != '' - uses: actions/upload-artifact@v4 - with: - name: wasm-sdk-test-report - path: packages/wasm-sdk/test/test-report.html - retention-days: 7 diff --git a/.github/workflows/wasm-sdk-documentation-check.yml b/.github/workflows/wasm-sdk-documentation-check.yml deleted file mode 100644 index a77d22c559b..00000000000 --- a/.github/workflows/wasm-sdk-documentation-check.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: Check WASM SDK Documentation - -on: - workflow_dispatch: - pull_request: - paths: - - 'packages/wasm-sdk/index.html' - - 'packages/wasm-sdk/docs.html' - - 'packages/wasm-sdk/AI_REFERENCE.md' - - 'packages/wasm-sdk/docs_manifest.json' - - 'packages/wasm-sdk/generate_docs.py' - - 'packages/wasm-sdk/check_documentation.py' - - '.github/workflows/wasm-sdk-documentation-check.yml' - push: - branches: - - master - - 'v*-dev' - paths: - - 'packages/wasm-sdk/index.html' - - 'packages/wasm-sdk/docs.html' - - 'packages/wasm-sdk/AI_REFERENCE.md' - - 'packages/wasm-sdk/docs_manifest.json' - - 'packages/wasm-sdk/generate_docs.py' - - 'packages/wasm-sdk/check_documentation.py' - - '.github/workflows/wasm-sdk-documentation-check.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - check-wasm-sdk-documentation: - name: Check WASM SDK Documentation - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - steps: - - name: Check out repo - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.x' - - - name: Check documentation completeness - id: doc-check - working-directory: packages/wasm-sdk - run: | - # Run the documentation check - if python3 check_documentation.py; then - echo "status=success" >> $GITHUB_OUTPUT - else - echo "status=failure" >> $GITHUB_OUTPUT - fi - - # Note: At this point, the documentation hasn't been regenerated yet, - # so this check will only detect if the PR already includes documentation changes - if git diff --quiet HEAD -- docs.html docs_manifest.json AI_REFERENCE.md; then - echo "docs_modified=false" >> $GITHUB_OUTPUT - else - echo "docs_modified=true" >> $GITHUB_OUTPUT - fi - - - name: Upload documentation check report - if: always() - uses: actions/upload-artifact@v4 - with: - name: documentation-check-report - path: packages/wasm-sdk/documentation-check-report.txt - - - name: Comment PR - if: github.event_name == 'pull_request' && steps.doc-check.outputs.status == 'failure' - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const reportPath = 'packages/wasm-sdk/documentation-check-report.txt'; - - if (fs.existsSync(reportPath)) { - const report = fs.readFileSync(reportPath, 'utf8'); - const status = '${{ steps.doc-check.outputs.status }}' === 'success' ? 'βœ…' : '❌'; - - // Find existing comment - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const botComment = comments.find(comment => - comment.user.type === 'Bot' && - comment.body.includes('WASM SDK Documentation Check') - ); - - const body = `### ${status} WASM SDK Documentation Check\n\n\`\`\`\n${report}\n\`\`\`\n\n**To fix documentation issues:**\n\`\`\`bash\ncd packages/wasm-sdk\npython3 generate_docs.py\n\`\`\``; - - if (botComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - body: body - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: body - }); - } - } - - - name: Auto-update documentation (non-protected branches) - if: github.event_name == 'push' && !contains(github.ref, 'master') && !contains(github.ref, 'main') && steps.doc-check.outputs.status == 'failure' - working-directory: packages/wasm-sdk - run: | - # Generate updated documentation - python3 generate_docs.py - # Check if there are changes - if ! git diff --quiet docs.html docs_manifest.json AI_REFERENCE.md; then - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git add docs.html docs_manifest.json AI_REFERENCE.md - git commit -m "chore: update WASM SDK documentation [skip ci]" - git push - fi - - - name: Create documentation update PR (protected branches) - if: github.event_name == 'push' && (contains(github.ref, 'master') || contains(github.ref, 'main')) && steps.doc-check.outputs.status == 'failure' - uses: peter-evans/create-pull-request@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "chore: update WASM SDK documentation" - title: "chore: update WASM SDK documentation" - body: | - This PR updates the WASM SDK documentation to match the current implementation. - - Auto-generated by GitHub Actions. - branch: auto-update-wasm-docs-${{ github.run_number }} - base: ${{ github.ref_name }} - path: packages/wasm-sdk - - - name: Fail if documentation is out of date - if: steps.doc-check.outputs.status == 'failure' && github.event_name == 'pull_request' - run: | - echo "Documentation is out of date. Please run 'python3 generate_docs.py' in packages/wasm-sdk/" - exit 1 \ No newline at end of file diff --git a/.github/workflows/wasm-sdk-ui-tests.yml b/.github/workflows/wasm-sdk-ui-tests.yml deleted file mode 100644 index 238d0f5a1fa..00000000000 --- a/.github/workflows/wasm-sdk-ui-tests.yml +++ /dev/null @@ -1,236 +0,0 @@ -name: WASM SDK UI Automation Tests - -on: - # Trigger after wasm-sdk-build workflow completes successfully - # TODO: Temporarily disabled - uncomment to re-enable automatic triggering - # workflow_run: - # workflows: ["Build WASM SDK"] - # types: - # - completed - # branches: - # - master - # - 'v[0-9]+.[0-9]+-dev' - - # Manual trigger for standalone testing - workflow_dispatch: - inputs: - test_type: - description: 'Type of tests to run' - required: true - default: 'all' - type: choice - options: - - smoke - - queries - - parameterized - - all - browser: - description: 'Browser to use for testing' - required: false - default: 'chromium' - type: choice - options: - - chromium - - firefox - - webkit - headed: - description: 'Run tests in headed mode (visible browser)' - required: false - default: false - type: boolean - debug: - description: 'Enable debug output' - required: false - default: false - type: boolean - workflow_run_id: - description: 'Workflow run ID to download WASM SDK build from (for manual runs)' - required: false - type: string - -jobs: - ui-tests: - name: Run WASM SDK UI Tests - runs-on: ubuntu-latest - timeout-minutes: 20 - # Only run if build workflow succeeded (for workflow_run) or if manually triggered - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} - - env: - CI: true - DEBUG: ${{ inputs.debug || 'false' }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Download WASM SDK build artifacts - uses: actions/download-artifact@v4 - with: - # For workflow_run, download from the triggering workflow - # For manual dispatch, use provided workflow_run_id or latest - run-id: ${{ github.event.workflow_run.id || inputs.workflow_run_id }} - name: wasm-sdk-build - path: packages/wasm-sdk/pkg/ - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Verify WASM SDK artifacts - working-directory: packages/wasm-sdk - run: | - echo "Verifying downloaded WASM SDK artifacts..." - ls -lah pkg/ - - # Verify all required files exist - required_files=( - "pkg/wasm_sdk_bg.wasm" - "pkg/optimized.wasm" - "pkg/wasm_sdk.js" - "pkg/wasm_sdk.d.ts" - "pkg/package.json" - ) - - for file in "${required_files[@]}"; do - if [ ! -f "$file" ]; then - echo "❌ Missing required file: $file" - exit 1 - else - echo "βœ… Found: $file" - fi - done - - echo "πŸŽ‰ All WASM SDK artifacts verified successfully!" - - - name: Install test dependencies - working-directory: packages/wasm-sdk/test/ui-automation - run: | - echo "Installing UI test dependencies..." - npm install - - - name: Cache Playwright browsers - uses: actions/cache@v4 - with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ hashFiles('packages/wasm-sdk/test/ui-automation/package.json') }} - restore-keys: | - ${{ runner.os }}-playwright- - - - name: Install Playwright browsers - working-directory: packages/wasm-sdk/test/ui-automation - run: | - echo "Installing Playwright browsers..." - npx playwright install --with-deps ${{ inputs.browser || 'chromium' }} - - - name: Run smoke tests - if: (inputs.test_type == 'smoke' || inputs.test_type == 'all') || github.event_name == 'workflow_run' - working-directory: packages/wasm-sdk/test/ui-automation - run: | - echo "πŸ§ͺ Running smoke tests..." - if [ "${{ inputs.headed }}" == "true" ]; then - npm run test:headed -- tests/basic-smoke.spec.js - else - npm run test:smoke - fi - - - name: Run query execution tests - if: (inputs.test_type == 'queries' || inputs.test_type == 'all') || github.event_name == 'workflow_run' - working-directory: packages/wasm-sdk/test/ui-automation - run: | - echo "πŸ” Running query execution tests..." - if [ "${{ inputs.headed }}" == "true" ]; then - npm run test:headed -- tests/query-execution.spec.js - else - npm run test:queries - fi - - - name: Run parameterized tests - if: (inputs.test_type == 'parameterized' || inputs.test_type == 'all') || github.event_name == 'workflow_run' - working-directory: packages/wasm-sdk/test/ui-automation - run: | - echo "βš™οΈ Running parameterized tests..." - if [ "${{ inputs.headed }}" == "true" ]; then - npm run test:headed -- tests/parameterized-queries.spec.js - else - npm run test:parameterized - fi - - - name: Upload Playwright Report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report-${{ inputs.test_type || 'all' }}-${{ inputs.browser || 'chromium' }}-${{ github.run_number }} - path: packages/wasm-sdk/test/ui-automation/playwright-report/ - retention-days: 30 - - - name: Upload Test Results - uses: actions/upload-artifact@v4 - if: always() - with: - name: test-results-${{ inputs.test_type || 'all' }}-${{ inputs.browser || 'chromium' }}-${{ github.run_number }} - path: | - packages/wasm-sdk/test/ui-automation/test-results/ - packages/wasm-sdk/test/ui-automation/test-results.json - retention-days: 30 - - - name: Upload Screenshots and Videos - uses: actions/upload-artifact@v4 - if: failure() - with: - name: test-failures-${{ inputs.test_type || 'all' }}-${{ inputs.browser || 'chromium' }}-${{ github.run_number }} - path: | - packages/wasm-sdk/test/ui-automation/test-results/**/*.png - packages/wasm-sdk/test/ui-automation/test-results/**/*.webm - retention-days: 30 - - - name: Display Test Summary - if: always() - working-directory: packages/wasm-sdk/test/ui-automation - run: | - echo "## WASM SDK UI Test Results" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - - if [ "${{ github.event_name }}" == "workflow_run" ]; then - echo "- **Triggered by**: WASM SDK Build (workflow_run)" >> "$GITHUB_STEP_SUMMARY" - echo "- **Build Workflow**: ${{ github.event.workflow_run.html_url }}" >> "$GITHUB_STEP_SUMMARY" - else - echo "- **Triggered by**: Manual dispatch" >> "$GITHUB_STEP_SUMMARY" - echo "- **Test Type**: ${{ inputs.test_type }}" >> "$GITHUB_STEP_SUMMARY" - echo "- **Browser**: ${{ inputs.browser }}" >> "$GITHUB_STEP_SUMMARY" - echo "- **Headed Mode**: ${{ inputs.headed }}" >> "$GITHUB_STEP_SUMMARY" - fi - - echo "- **Debug**: ${{ env.DEBUG }}" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - - # Show test results if available - if [ -f "test-results.json" ]; then - echo "### Test Results" >> "$GITHUB_STEP_SUMMARY" - echo '```json' >> "$GITHUB_STEP_SUMMARY" - jq '.stats' test-results.json >> "$GITHUB_STEP_SUMMARY" 2>/dev/null || echo "Test results available in artifacts" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - fi - - # List available artifacts - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "### Available Artifacts" >> "$GITHUB_STEP_SUMMARY" - echo "- **Playwright Report**: Detailed HTML report with test results" >> "$GITHUB_STEP_SUMMARY" - echo "- **Test Results**: JSON results and raw output files" >> "$GITHUB_STEP_SUMMARY" - if [ "${{ job.status }}" == "failure" ]; then - echo "- **Test Failures**: Screenshots and videos of failed tests" >> "$GITHUB_STEP_SUMMARY" - fi - - # Add quick links - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "### Quick Links" >> "$GITHUB_STEP_SUMMARY" - echo "- [WASM SDK](https://github.com/${{ github.repository }}/tree/${{ github.sha }}/packages/wasm-sdk)" >> "$GITHUB_STEP_SUMMARY" - echo "- [UI Tests](https://github.com/${{ github.repository }}/tree/${{ github.sha }}/packages/wasm-sdk/test/ui-automation)" >> "$GITHUB_STEP_SUMMARY" - \ No newline at end of file diff --git a/.pnp.cjs b/.pnp.cjs index 8635a8ef372..bc6d0661bd8 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -53,6 +53,10 @@ const RAW_RUNTIME_STATE = "name": "dash",\ "reference": "workspace:packages/js-dash-sdk"\ },\ + {\ + "name": "@dashevo/evo-sdk",\ + "reference": "workspace:packages/js-evo-sdk"\ + },\ {\ "name": "@dashevo/grpc-common",\ "reference": "workspace:packages/js-grpc-common"\ @@ -89,6 +93,10 @@ const RAW_RUNTIME_STATE = "name": "wasm-drive-verify",\ "reference": "workspace:packages/wasm-drive-verify"\ },\ + {\ + "name": "@dashevo/wasm-sdk",\ + "reference": "workspace:packages/wasm-sdk"\ + },\ {\ "name": "@dashevo/withdrawals-contract",\ "reference": "workspace:packages/withdrawals-contract"\ @@ -104,6 +112,7 @@ const RAW_RUNTIME_STATE = ["@dashevo/dash-spv", ["workspace:packages/dash-spv"]],\ ["@dashevo/dashpay-contract", ["workspace:packages/dashpay-contract"]],\ ["@dashevo/dpns-contract", ["workspace:packages/dpns-contract"]],\ + ["@dashevo/evo-sdk", ["workspace:packages/js-evo-sdk"]],\ ["@dashevo/feature-flags-contract", ["workspace:packages/feature-flags-contract"]],\ ["@dashevo/grpc-common", ["workspace:packages/js-grpc-common"]],\ ["@dashevo/keyword-search-contract", ["workspace:packages/keyword-search-contract"]],\ @@ -114,6 +123,7 @@ const RAW_RUNTIME_STATE = ["@dashevo/wallet-lib", ["workspace:packages/wallet-lib"]],\ ["@dashevo/wallet-utils-contract", ["workspace:packages/wallet-utils-contract"]],\ ["@dashevo/wasm-dpp", ["workspace:packages/wasm-dpp"]],\ + ["@dashevo/wasm-sdk", ["workspace:packages/wasm-sdk"]],\ ["@dashevo/withdrawals-contract", ["workspace:packages/withdrawals-contract"]],\ ["dash", ["workspace:packages/js-dash-sdk"]],\ ["dashmate", ["workspace:packages/dashmate"]],\ @@ -2715,6 +2725,49 @@ const RAW_RUNTIME_STATE = "linkType": "SOFT"\ }]\ ]],\ + ["@dashevo/evo-sdk", [\ + ["workspace:packages/js-evo-sdk", {\ + "packageLocation": "./packages/js-evo-sdk/",\ + "packageDependencies": [\ + ["@dashevo/evo-sdk", "workspace:packages/js-evo-sdk"],\ + ["@dashevo/wasm-sdk", "workspace:packages/wasm-sdk"],\ + ["@typescript-eslint/eslint-plugin", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.55.0"],\ + ["@typescript-eslint/parser", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.55.0"],\ + ["assert", "npm:2.0.0"],\ + ["buffer", "npm:6.0.3"],\ + ["chai", "npm:4.3.10"],\ + ["chai-as-promised", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:7.1.1"],\ + ["dirty-chai", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:2.0.1"],\ + ["eslint", "npm:8.53.0"],\ + ["eslint-config-airbnb-base", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:15.0.0"],\ + ["eslint-config-airbnb-typescript", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:17.0.0"],\ + ["eslint-plugin-import", "virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:2.29.0"],\ + ["eslint-plugin-jsdoc", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:46.9.0"],\ + ["events", "npm:3.3.0"],\ + ["karma", "npm:6.4.3"],\ + ["karma-chai", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:0.1.0"],\ + ["karma-chrome-launcher", "npm:3.1.0"],\ + ["karma-firefox-launcher", "npm:2.1.2"],\ + ["karma-mocha", "npm:2.0.1"],\ + ["karma-mocha-reporter", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:2.2.5"],\ + ["karma-webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.0.0"],\ + ["mocha", "npm:11.1.0"],\ + ["path-browserify", "npm:1.0.1"],\ + ["process", "npm:0.11.10"],\ + ["sinon", "npm:17.0.1"],\ + ["sinon-chai", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:3.7.0"],\ + ["string_decoder", "npm:1.3.0"],\ + ["terser-webpack-plugin", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.3.11"],\ + ["ts-loader", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:9.5.0"],\ + ["typescript", "patch:typescript@npm%3A3.9.10#optional!builtin::version=3.9.10&hash=3bd3d3"],\ + ["url", "npm:0.11.3"],\ + ["util", "npm:0.12.4"],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"],\ + ["webpack-cli", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1"]\ + ],\ + "linkType": "SOFT"\ + }]\ + ]],\ ["@dashevo/feature-flags-contract", [\ ["workspace:packages/feature-flags-contract", {\ "packageLocation": "./packages/feature-flags-contract/",\ @@ -3064,6 +3117,40 @@ const RAW_RUNTIME_STATE = "linkType": "SOFT"\ }]\ ]],\ + ["@dashevo/wasm-sdk", [\ + ["workspace:packages/wasm-sdk", {\ + "packageLocation": "./packages/wasm-sdk/",\ + "packageDependencies": [\ + ["@dashevo/wasm-sdk", "workspace:packages/wasm-sdk"],\ + ["assert", "npm:2.0.0"],\ + ["buffer", "npm:6.0.3"],\ + ["chai", "npm:4.3.10"],\ + ["chai-as-promised", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:7.1.1"],\ + ["dirty-chai", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:2.0.1"],\ + ["eslint", "npm:8.53.0"],\ + ["eslint-config-airbnb-base", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:15.0.0"],\ + ["eslint-plugin-import", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:2.29.0"],\ + ["eslint-plugin-jsdoc", "virtual:8f25fc90e0fb5fd89843707863857591fa8c52f9f33eadced4bf404b1871d91959f7bb86948ae0e1b53ee94d491ef8fde9c0b58b39c9490c0d0fa6c931945f97#npm:46.9.0"],\ + ["events", "npm:3.3.0"],\ + ["karma", "npm:6.4.3"],\ + ["karma-chai", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:0.1.0"],\ + ["karma-chrome-launcher", "npm:3.1.0"],\ + ["karma-firefox-launcher", "npm:2.1.2"],\ + ["karma-mocha", "npm:2.0.1"],\ + ["karma-mocha-reporter", "virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:2.2.5"],\ + ["karma-webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.0.0"],\ + ["mocha", "npm:11.1.0"],\ + ["path-browserify", "npm:1.0.1"],\ + ["process", "npm:0.11.10"],\ + ["string_decoder", "npm:1.3.0"],\ + ["url", "npm:0.11.3"],\ + ["util", "npm:0.12.4"],\ + ["webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0"],\ + ["webpack-cli", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1"]\ + ],\ + "linkType": "SOFT"\ + }]\ + ]],\ ["@dashevo/withdrawals-contract", [\ ["workspace:packages/withdrawals-contract", {\ "packageLocation": "./packages/withdrawals-contract/",\ @@ -5392,6 +5479,40 @@ const RAW_RUNTIME_STATE = "webpack"\ ],\ "linkType": "HARD"\ + }],\ + ["virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.1.0", {\ + "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-1656ebbd4e/0/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/",\ + "packageDependencies": [\ + ["@webpack-cli/configtest", "virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.1.0"],\ + ["@types/webpack", null],\ + ["@types/webpack-cli", null],\ + ["webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0"],\ + ["webpack-cli", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1"]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "@types/webpack",\ + "webpack-cli",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ + ["virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.1.0", {\ + "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-efd83b3cc1/0/cache/@webpack-cli-configtest-npm-1.1.0-2b6b2ef3d7-69e7816b5b.zip/node_modules/@webpack-cli/configtest/",\ + "packageDependencies": [\ + ["@webpack-cli/configtest", "virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.1.0"],\ + ["@types/webpack", null],\ + ["@types/webpack-cli", null],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"],\ + ["webpack-cli", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1"]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "@types/webpack",\ + "webpack-cli",\ + "webpack"\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@webpack-cli/info", [\ @@ -5457,6 +5578,34 @@ const RAW_RUNTIME_STATE = "webpack-cli"\ ],\ "linkType": "HARD"\ + }],\ + ["virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.4.0", {\ + "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-f31931ce84/0/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/",\ + "packageDependencies": [\ + ["@webpack-cli/info", "virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.4.0"],\ + ["@types/webpack-cli", null],\ + ["envinfo", "npm:7.8.1"],\ + ["webpack-cli", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1"]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "webpack-cli"\ + ],\ + "linkType": "HARD"\ + }],\ + ["virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.4.0", {\ + "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-87ac1b2a37/0/cache/@webpack-cli-info-npm-1.4.0-4a26ccee64-6385b1e2c5.zip/node_modules/@webpack-cli/info/",\ + "packageDependencies": [\ + ["@webpack-cli/info", "virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.4.0"],\ + ["@types/webpack-cli", null],\ + ["envinfo", "npm:7.8.1"],\ + ["webpack-cli", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1"]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "webpack-cli"\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@webpack-cli/serve", [\ @@ -5534,6 +5683,40 @@ const RAW_RUNTIME_STATE = "webpack-dev-server"\ ],\ "linkType": "HARD"\ + }],\ + ["virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.6.0", {\ + "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-4711a17f04/0/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-3fd2e5f365.zip/node_modules/@webpack-cli/serve/",\ + "packageDependencies": [\ + ["@webpack-cli/serve", "virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.6.0"],\ + ["@types/webpack-cli", null],\ + ["@types/webpack-dev-server", null],\ + ["webpack-cli", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1"],\ + ["webpack-dev-server", null]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "@types/webpack-dev-server",\ + "webpack-cli",\ + "webpack-dev-server"\ + ],\ + "linkType": "HARD"\ + }],\ + ["virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.6.0", {\ + "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-330db39c94/0/cache/@webpack-cli-serve-npm-1.6.0-c7b35aa4ef-3fd2e5f365.zip/node_modules/@webpack-cli/serve/",\ + "packageDependencies": [\ + ["@webpack-cli/serve", "virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.6.0"],\ + ["@types/webpack-cli", null],\ + ["@types/webpack-dev-server", null],\ + ["webpack-cli", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1"],\ + ["webpack-dev-server", null]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "@types/webpack-dev-server",\ + "webpack-cli",\ + "webpack-dev-server"\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@xtuc/ieee754", [\ @@ -13305,6 +13488,22 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.0.0", {\ + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-3f3d78beea/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-9bd565adbc.zip/node_modules/karma-webpack/",\ + "packageDependencies": [\ + ["karma-webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.0.0"],\ + ["@types/webpack", null],\ + ["glob", "npm:7.2.3"],\ + ["minimatch", "npm:3.1.2"],\ + ["webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0"],\ + ["webpack-merge", "npm:4.2.2"]\ + ],\ + "packagePeers": [\ + "@types/webpack",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.0.0", {\ "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-94955efe11/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-9bd565adbc.zip/node_modules/karma-webpack/",\ "packageDependencies": [\ @@ -13321,6 +13520,22 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.0.0", {\ + "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-c873655e3d/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-9bd565adbc.zip/node_modules/karma-webpack/",\ + "packageDependencies": [\ + ["karma-webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.0.0"],\ + ["@types/webpack", null],\ + ["glob", "npm:7.2.3"],\ + ["minimatch", "npm:3.1.2"],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"],\ + ["webpack-merge", "npm:4.2.2"]\ + ],\ + "packagePeers": [\ + "@types/webpack",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:5.0.0", {\ "packageLocation": "./.yarn/__virtual__/karma-webpack-virtual-fa03a91744/0/cache/karma-webpack-npm-5.0.0-d7c66b2a8a-9bd565adbc.zip/node_modules/karma-webpack/",\ "packageDependencies": [\ @@ -18599,6 +18814,36 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:78d5ff6fde5ea52c420e709e60c10ff99b2616c779333c323baa448d23fb3675eeb1eb1a1f40b6a9a0120ae591e10ee81fb9bed979e75f077ef3680845d7e170#npm:5.3.10", {\ + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-bd54e97fee/0/cache/terser-webpack-plugin-npm-5.3.10-3bde1920fb-fb1c2436ae.zip/node_modules/terser-webpack-plugin/",\ + "packageDependencies": [\ + ["terser-webpack-plugin", "virtual:78d5ff6fde5ea52c420e709e60c10ff99b2616c779333c323baa448d23fb3675eeb1eb1a1f40b6a9a0120ae591e10ee81fb9bed979e75f077ef3680845d7e170#npm:5.3.10"],\ + ["@jridgewell/trace-mapping", "npm:0.3.25"],\ + ["@swc/core", null],\ + ["@types/esbuild", null],\ + ["@types/swc__core", null],\ + ["@types/uglify-js", null],\ + ["@types/webpack", null],\ + ["esbuild", null],\ + ["jest-worker", "npm:27.5.1"],\ + ["schema-utils", "npm:3.1.1"],\ + ["serialize-javascript", "npm:6.0.2"],\ + ["terser", "npm:5.31.6"],\ + ["uglify-js", null],\ + ["webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0"]\ + ],\ + "packagePeers": [\ + "@swc/core",\ + "@types/esbuild",\ + "@types/swc__core",\ + "@types/uglify-js",\ + "@types/webpack",\ + "esbuild",\ + "uglify-js",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:7bd93570c5d84736c13a223c581c6a110a422284c96923702acd4a2b154b5a6d0e0cc886101d925773c05b4b1eddf96701f1308dc290c0e99695f881c63c6570#npm:5.3.10", {\ "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-f7b5f4e5a8/0/cache/terser-webpack-plugin-npm-5.3.10-3bde1920fb-fb1c2436ae.zip/node_modules/terser-webpack-plugin/",\ "packageDependencies": [\ @@ -18629,6 +18874,36 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:7e88e6d1177d78c4a64f6848ece7e13553149bb6071435f19c64ec2c1b1fbe91f57551ab6f375e41113f114d3ee2a79359a4c9654cb074fdee0559a93f0d1cd2#npm:5.3.10", {\ + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-a70b93acf4/0/cache/terser-webpack-plugin-npm-5.3.10-3bde1920fb-fb1c2436ae.zip/node_modules/terser-webpack-plugin/",\ + "packageDependencies": [\ + ["terser-webpack-plugin", "virtual:7e88e6d1177d78c4a64f6848ece7e13553149bb6071435f19c64ec2c1b1fbe91f57551ab6f375e41113f114d3ee2a79359a4c9654cb074fdee0559a93f0d1cd2#npm:5.3.10"],\ + ["@jridgewell/trace-mapping", "npm:0.3.25"],\ + ["@swc/core", null],\ + ["@types/esbuild", null],\ + ["@types/swc__core", null],\ + ["@types/uglify-js", null],\ + ["@types/webpack", null],\ + ["esbuild", null],\ + ["jest-worker", "npm:27.5.1"],\ + ["schema-utils", "npm:3.1.1"],\ + ["serialize-javascript", "npm:6.0.2"],\ + ["terser", "npm:5.31.6"],\ + ["uglify-js", null],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"]\ + ],\ + "packagePeers": [\ + "@swc/core",\ + "@types/esbuild",\ + "@types/swc__core",\ + "@types/uglify-js",\ + "@types/webpack",\ + "esbuild",\ + "uglify-js",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:905383bbe64c71ce6c95c9f55f98098af8f091dba1addd3de3a70c380414a85254622f9263413882da6f88f8a0bb953a82859605007084e0a6da8d5f8e3376f7#npm:5.3.10", {\ "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-9f3e20f6e2/0/cache/terser-webpack-plugin-npm-5.3.10-3bde1920fb-fb1c2436ae.zip/node_modules/terser-webpack-plugin/",\ "packageDependencies": [\ @@ -18748,6 +19023,36 @@ const RAW_RUNTIME_STATE = "webpack"\ ],\ "linkType": "HARD"\ + }],\ + ["virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.3.11", {\ + "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-94d9a56ac4/0/cache/terser-webpack-plugin-npm-5.3.11-1a5bba0883-a8f7c92c75.zip/node_modules/terser-webpack-plugin/",\ + "packageDependencies": [\ + ["terser-webpack-plugin", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.3.11"],\ + ["@jridgewell/trace-mapping", "npm:0.3.25"],\ + ["@swc/core", null],\ + ["@types/esbuild", null],\ + ["@types/swc__core", null],\ + ["@types/uglify-js", null],\ + ["@types/webpack", null],\ + ["esbuild", null],\ + ["jest-worker", "npm:27.5.1"],\ + ["schema-utils", "npm:4.3.0"],\ + ["serialize-javascript", "npm:6.0.2"],\ + ["terser", "npm:5.39.0"],\ + ["uglify-js", null],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"]\ + ],\ + "packagePeers": [\ + "@swc/core",\ + "@types/esbuild",\ + "@types/swc__core",\ + "@types/uglify-js",\ + "@types/webpack",\ + "esbuild",\ + "uglify-js",\ + "webpack"\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["test-exclude", [\ @@ -19029,6 +19334,28 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:9.5.0", {\ + "packageLocation": "./.yarn/__virtual__/ts-loader-virtual-35b23e0e2c/0/cache/ts-loader-npm-9.5.0-9514617263-8ffc6411ec.zip/node_modules/ts-loader/",\ + "packageDependencies": [\ + ["ts-loader", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:9.5.0"],\ + ["@types/typescript", null],\ + ["@types/webpack", null],\ + ["chalk", "npm:4.1.2"],\ + ["enhanced-resolve", "npm:5.15.0"],\ + ["micromatch", "npm:4.0.7"],\ + ["semver", "npm:7.5.3"],\ + ["source-map", "npm:0.7.4"],\ + ["typescript", "patch:typescript@npm%3A3.9.10#optional!builtin::version=3.9.10&hash=3bd3d3"],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"]\ + ],\ + "packagePeers": [\ + "@types/typescript",\ + "@types/webpack",\ + "typescript",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:9.5.0", {\ "packageLocation": "./.yarn/__virtual__/ts-loader-virtual-a9f8677903/0/cache/ts-loader-npm-9.5.0-9514617263-8ffc6411ec.zip/node_modules/ts-loader/",\ "packageDependencies": [\ @@ -20126,6 +20453,42 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0", {\ + "packageLocation": "./.yarn/__virtual__/webpack-virtual-78d5ff6fde/0/cache/webpack-npm-5.94.0-d1e43de389-648449c5fb.zip/node_modules/webpack/",\ + "packageDependencies": [\ + ["webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0"],\ + ["@types/estree", "npm:1.0.5"],\ + ["@types/webpack-cli", null],\ + ["@webassemblyjs/ast", "npm:1.12.1"],\ + ["@webassemblyjs/wasm-edit", "npm:1.12.1"],\ + ["@webassemblyjs/wasm-parser", "npm:1.12.1"],\ + ["acorn", "npm:8.11.2"],\ + ["acorn-import-attributes", "virtual:9644477017df2e32be56d4a1c7fe5ac5a3e402b2dbf0f12e92d05353a79eef964913f8eba76831dcc035bd99e7745cec85de81787c8c846ec7e2635108519296#npm:1.9.5"],\ + ["browserslist", "npm:4.23.3"],\ + ["chrome-trace-event", "npm:1.0.3"],\ + ["enhanced-resolve", "npm:5.17.1"],\ + ["es-module-lexer", "npm:1.5.4"],\ + ["eslint-scope", "npm:5.1.1"],\ + ["events", "npm:3.3.0"],\ + ["glob-to-regexp", "npm:0.4.1"],\ + ["graceful-fs", "npm:4.2.11"],\ + ["json-parse-even-better-errors", "npm:2.3.1"],\ + ["loader-runner", "npm:4.2.0"],\ + ["mime-types", "npm:2.1.34"],\ + ["neo-async", "npm:2.6.2"],\ + ["schema-utils", "npm:3.3.0"],\ + ["tapable", "npm:2.2.1"],\ + ["terser-webpack-plugin", "virtual:78d5ff6fde5ea52c420e709e60c10ff99b2616c779333c323baa448d23fb3675eeb1eb1a1f40b6a9a0120ae591e10ee81fb9bed979e75f077ef3680845d7e170#npm:5.3.10"],\ + ["watchpack", "npm:2.4.2"],\ + ["webpack-cli", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1"],\ + ["webpack-sources", "npm:3.2.3"]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "webpack-cli"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:5.94.0", {\ "packageLocation": "./.yarn/__virtual__/webpack-virtual-7bd93570c5/0/cache/webpack-npm-5.94.0-d1e43de389-648449c5fb.zip/node_modules/webpack/",\ "packageDependencies": [\ @@ -20162,6 +20525,42 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0", {\ + "packageLocation": "./.yarn/__virtual__/webpack-virtual-7e88e6d117/0/cache/webpack-npm-5.94.0-d1e43de389-648449c5fb.zip/node_modules/webpack/",\ + "packageDependencies": [\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"],\ + ["@types/estree", "npm:1.0.5"],\ + ["@types/webpack-cli", null],\ + ["@webassemblyjs/ast", "npm:1.12.1"],\ + ["@webassemblyjs/wasm-edit", "npm:1.12.1"],\ + ["@webassemblyjs/wasm-parser", "npm:1.12.1"],\ + ["acorn", "npm:8.11.2"],\ + ["acorn-import-attributes", "virtual:9644477017df2e32be56d4a1c7fe5ac5a3e402b2dbf0f12e92d05353a79eef964913f8eba76831dcc035bd99e7745cec85de81787c8c846ec7e2635108519296#npm:1.9.5"],\ + ["browserslist", "npm:4.23.3"],\ + ["chrome-trace-event", "npm:1.0.3"],\ + ["enhanced-resolve", "npm:5.17.1"],\ + ["es-module-lexer", "npm:1.5.4"],\ + ["eslint-scope", "npm:5.1.1"],\ + ["events", "npm:3.3.0"],\ + ["glob-to-regexp", "npm:0.4.1"],\ + ["graceful-fs", "npm:4.2.11"],\ + ["json-parse-even-better-errors", "npm:2.3.1"],\ + ["loader-runner", "npm:4.2.0"],\ + ["mime-types", "npm:2.1.34"],\ + ["neo-async", "npm:2.6.2"],\ + ["schema-utils", "npm:3.3.0"],\ + ["tapable", "npm:2.2.1"],\ + ["terser-webpack-plugin", "virtual:7e88e6d1177d78c4a64f6848ece7e13553149bb6071435f19c64ec2c1b1fbe91f57551ab6f375e41113f114d3ee2a79359a4c9654cb074fdee0559a93f0d1cd2#npm:5.3.10"],\ + ["watchpack", "npm:2.4.2"],\ + ["webpack-cli", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1"],\ + ["webpack-sources", "npm:3.2.3"]\ + ],\ + "packagePeers": [\ + "@types/webpack-cli",\ + "webpack-cli"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:5.94.0", {\ "packageLocation": "./.yarn/__virtual__/webpack-virtual-9644477017/0/cache/webpack-npm-5.94.0-d1e43de389-648449c5fb.zip/node_modules/webpack/",\ "packageDependencies": [\ @@ -20289,6 +20688,47 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1", {\ + "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-b65947dc40/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-14eb69cec6.zip/node_modules/webpack-cli/",\ + "packageDependencies": [\ + ["webpack-cli", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:4.9.1"],\ + ["@discoveryjs/json-ext", "npm:0.5.5"],\ + ["@types/webpack", null],\ + ["@types/webpack-bundle-analyzer", null],\ + ["@types/webpack-cli__generators", null],\ + ["@types/webpack-cli__migrate", null],\ + ["@types/webpack-dev-server", null],\ + ["@webpack-cli/configtest", "virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.1.0"],\ + ["@webpack-cli/generators", null],\ + ["@webpack-cli/info", "virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.4.0"],\ + ["@webpack-cli/migrate", null],\ + ["@webpack-cli/serve", "virtual:b65947dc409e3c56a3ffe1d9845d7e7551f7c6a754af3941e9848fc9815659e0053c64fd038dddc29d956633aad694f761d0d61fe40b22419d9d10b8b52633c6#npm:1.6.0"],\ + ["colorette", "npm:2.0.20"],\ + ["commander", "npm:7.2.0"],\ + ["execa", "npm:5.1.1"],\ + ["fastest-levenshtein", "npm:1.0.12"],\ + ["import-local", "npm:3.0.3"],\ + ["interpret", "npm:2.2.0"],\ + ["rechoir", "npm:0.7.1"],\ + ["webpack", "virtual:98d1afeac78a19485e4cb7428abff692e58b6fc468d8040035b560ed49383fc95857be6b5014af27e53063e6f08b654690c2b945f3443c22dd60c6b083684b3c#npm:5.94.0"],\ + ["webpack-bundle-analyzer", null],\ + ["webpack-dev-server", null],\ + ["webpack-merge", "npm:5.8.0"]\ + ],\ + "packagePeers": [\ + "@types/webpack-bundle-analyzer",\ + "@types/webpack-cli__generators",\ + "@types/webpack-cli__migrate",\ + "@types/webpack-dev-server",\ + "@types/webpack",\ + "@webpack-cli/generators",\ + "@webpack-cli/migrate",\ + "webpack-bundle-analyzer",\ + "webpack-dev-server",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:ad53cff31b1dbd4927a99e71702e3b8b10338636eaff010987c27c9ccea2d52af36900a9e36a4231cbb6e5464248ccc9c1da5d1d24d9b0f4f95660296b1060a6#npm:4.9.1", {\ "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-7fc88da9d0/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-14eb69cec6.zip/node_modules/webpack-cli/",\ "packageDependencies": [\ @@ -20330,6 +20770,47 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ + ["virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1", {\ + "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-f4270a75ca/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-14eb69cec6.zip/node_modules/webpack-cli/",\ + "packageDependencies": [\ + ["webpack-cli", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:4.9.1"],\ + ["@discoveryjs/json-ext", "npm:0.5.5"],\ + ["@types/webpack", null],\ + ["@types/webpack-bundle-analyzer", null],\ + ["@types/webpack-cli__generators", null],\ + ["@types/webpack-cli__migrate", null],\ + ["@types/webpack-dev-server", null],\ + ["@webpack-cli/configtest", "virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.1.0"],\ + ["@webpack-cli/generators", null],\ + ["@webpack-cli/info", "virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.4.0"],\ + ["@webpack-cli/migrate", null],\ + ["@webpack-cli/serve", "virtual:f4270a75ca3f51d5d57ce1c0c3b74e65b10913a5a0b06541c2f52f2aad9e67168bc048e9844fb54347cc51a032bcf16d307891fea0455e500af556ee4cbae024#npm:1.6.0"],\ + ["colorette", "npm:2.0.20"],\ + ["commander", "npm:7.2.0"],\ + ["execa", "npm:5.1.1"],\ + ["fastest-levenshtein", "npm:1.0.12"],\ + ["import-local", "npm:3.0.3"],\ + ["interpret", "npm:2.2.0"],\ + ["rechoir", "npm:0.7.1"],\ + ["webpack", "virtual:da12f6bb23b671689b7f4f5cfff69cabf980ba04aff1ffd96860c787f7d5d08d32b0db765d9f16463de0d1af2c01fa6987b861cf5df2362e38e0ef415f29b51c#npm:5.94.0"],\ + ["webpack-bundle-analyzer", null],\ + ["webpack-dev-server", null],\ + ["webpack-merge", "npm:5.8.0"]\ + ],\ + "packagePeers": [\ + "@types/webpack-bundle-analyzer",\ + "@types/webpack-cli__generators",\ + "@types/webpack-cli__migrate",\ + "@types/webpack-dev-server",\ + "@types/webpack",\ + "@webpack-cli/generators",\ + "@webpack-cli/migrate",\ + "webpack-bundle-analyzer",\ + "webpack-dev-server",\ + "webpack"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:e2d057e7cc143d3cb9bec864f4a2d862441b5a09f81f8e6c46e7a098cbc89e4d07017cc6e2e2142d5704bb55da853cbec2d025ebc0b30e8696c31380c00f2c7d#npm:4.9.1", {\ "packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-16885aa844/0/cache/webpack-cli-npm-4.9.1-1b8a5f360f-14eb69cec6.zip/node_modules/webpack-cli/",\ "packageDependencies": [\ diff --git a/Cargo.lock b/Cargo.lock index e142a722201..025e59931bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "cipher", "cpufeatures", ] @@ -45,7 +45,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "getrandom 0.3.3", "once_cell", "serde", @@ -68,12 +68,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -236,9 +230,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core 0.4.5", @@ -256,7 +250,7 @@ dependencies = [ "rustversion", "serde", "sync_wrapper", - "tower 0.4.13", + "tower 0.5.2", "tower-layer", "tower-service", ] @@ -364,7 +358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.3", "libc", "miniz_oxide", "object", @@ -480,11 +474,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.72.0" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.13.0", @@ -513,6 +507,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d193de1f7487df1914d3a568b772458861d33f9c54249612cc2893d6915054" dependencies = [ "bitcoin_hashes 0.13.0", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", "zeroize", @@ -579,9 +575,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.2" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "bitvec" @@ -604,7 +600,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if", + "cfg-if 1.0.3", "constant_time_eq 0.3.1", ] @@ -657,7 +653,7 @@ dependencies = [ "sha2", "sha3", "subtle", - "thiserror 2.0.15", + "thiserror 2.0.16", "uint-zigzag", "vsss-rs", "zeroize", @@ -710,7 +706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", "syn 2.0.106", @@ -809,7 +805,7 @@ checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" dependencies = [ "clap", "heck 0.4.1", - "indexmap 2.10.0", + "indexmap 2.11.3", "log", "proc-macro2", "quote", @@ -828,7 +824,7 @@ checksum = "975982cdb7ad6a142be15bdf84aea7ec6a9e5d4d797c004d43185b24cfe4e684" dependencies = [ "clap", "heck 0.5.0", - "indexmap 2.10.0", + "indexmap 2.11.3", "log", "proc-macro2", "quote", @@ -841,10 +837,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.33" +version = "1.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -861,9 +858,15 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -880,17 +883,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -965,9 +967,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.45" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -975,9 +977,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.44" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -987,9 +989,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.45" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1072,7 +1074,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "wasm-bindgen", ] @@ -1144,7 +1146,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", ] [[package]] @@ -1229,7 +1231,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "crossterm_winapi", "libc", "mio 0.8.11", @@ -1283,7 +1285,7 @@ version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "cpufeatures", "curve25519-dalek-derive", "digest", @@ -1441,7 +1443,7 @@ dependencies = [ "serde", "serde_json", "test-case", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tokio-test", "tokio-util", @@ -1465,7 +1467,7 @@ dependencies = [ "dashcore_hashes", "hex", "hickory-resolver", - "indexmap 2.10.0", + "indexmap 2.11.3", "key-wallet", "key-wallet-manager", "log", @@ -1524,7 +1526,7 @@ dependencies = [ "rustversion", "secp256k1", "serde", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -1579,7 +1581,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -1594,7 +1596,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", "token-history-contract", "wallet-utils-contract", "withdrawals-contract", @@ -1629,9 +1631,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -1736,7 +1738,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -1763,7 +1765,7 @@ dependencies = [ "env_logger 0.11.8", "getrandom 0.2.16", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "integer-encoding", "itertools 0.13.0", "json-schema-compatibility-validator", @@ -1790,7 +1792,7 @@ dependencies = [ "serde_repr", "sha2", "strum 0.26.3", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tracing", ] @@ -1818,7 +1820,7 @@ dependencies = [ "grovedb-storage", "grovedb-version", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "integer-encoding", "intmap", "itertools 0.13.0", @@ -1832,7 +1834,7 @@ dependencies = [ "serde_json", "sqlparser", "tempfile", - "thiserror 2.0.15", + "thiserror 2.0.16", "tracing", ] @@ -1861,7 +1863,7 @@ dependencies = [ "envy", "file-rotate", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "integer-encoding", "itertools 0.13.0", "lazy_static", @@ -1901,13 +1903,13 @@ dependencies = [ "dpp", "drive", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "platform-serialization", "platform-serialization-derive", "serde", "serde_json", "tenderdash-abci", - "thiserror 2.0.15", + "thiserror 2.0.16", "tracing", ] @@ -2010,7 +2012,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", ] [[package]] @@ -2098,12 +2100,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2134,8 +2136,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" dependencies = [ "bit-set", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] @@ -2151,7 +2153,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -2181,6 +2183,12 @@ dependencies = [ "flate2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -2210,6 +2218,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2227,9 +2241,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -2353,12 +2367,12 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" dependencies = [ "cc", - "cfg-if", + "cfg-if 1.0.3", "libc", "log", "rustversion", @@ -2392,7 +2406,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", @@ -2405,11 +2419,11 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", "wasm-bindgen", ] @@ -2468,14 +2482,14 @@ dependencies = [ "grovedbg-types", "hex", "hex-literal", - "indexmap 2.10.0", + "indexmap 2.11.3", "integer-encoding", "intmap", "itertools 0.14.0", "reqwest", "sha2", "tempfile", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tokio-util", "tower-http", @@ -2489,7 +2503,7 @@ source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607 dependencies = [ "integer-encoding", "intmap", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -2501,7 +2515,7 @@ dependencies = [ "hex", "integer-encoding", "intmap", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -2521,11 +2535,11 @@ dependencies = [ "grovedb-version", "grovedb-visualize", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "integer-encoding", "num_cpus", "rand 0.8.5", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -2552,7 +2566,7 @@ dependencies = [ "rocksdb 0.24.0", "strum 0.27.2", "tempfile", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -2560,7 +2574,7 @@ name = "grovedb-version" version = "3.0.0" source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" dependencies = [ - "thiserror 2.0.15", + "thiserror 2.0.16", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2594,7 +2608,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.10.0", + "indexmap 2.11.3", "slab", "tokio", "tokio-util", @@ -2607,7 +2621,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "crunchy", ] @@ -2631,22 +2645,22 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "ahash 0.8.12", + "allocator-api2", + "equivalent", + "foldhash 0.1.5", ] [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -2733,7 +2747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", - "cfg-if", + "cfg-if 1.0.3", "data-encoding", "enum-as-inner", "futures-channel", @@ -2744,7 +2758,7 @@ dependencies = [ "once_cell", "rand 0.9.2", "ring", - "thiserror 2.0.15", + "thiserror 2.0.16", "tinyvec", "tokio", "tracing", @@ -2757,7 +2771,7 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "futures-util", "hickory-proto", "ipconfig", @@ -2767,7 +2781,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tracing", ] @@ -2863,9 +2877,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" @@ -2939,9 +2953,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64 0.22.1", "bytes", @@ -2965,9 +2979,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2975,7 +2989,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.0", ] [[package]] @@ -3081,9 +3095,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -3113,13 +3127,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "92119844f513ffa41556430369ab02c295a3578af21cf945caa3e9e0c2481ac3" dependencies = [ "equivalent", "hashbrown 0.15.5", "serde", + "serde_core", ] [[package]] @@ -3148,12 +3163,12 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.2", - "cfg-if", + "bitflags 2.9.4", + "cfg-if 1.0.3", "libc", ] @@ -3270,9 +3285,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -3308,7 +3323,7 @@ dependencies = [ "json-schema-compatibility-validator", "once_cell", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -3368,7 +3383,7 @@ dependencies = [ "bincode 2.0.0-rc.3", "bincode_derive", "bip39", - "bitflags 2.9.2", + "bitflags 2.9.4", "bs58", "dash-network", "dashcore", @@ -3423,7 +3438,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -3456,7 +3471,7 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "windows-targets 0.53.3", ] @@ -3466,7 +3481,7 @@ version = "0.17.3+10.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cef2a00ee60fe526157c9023edab23943fae1ce2ab6f4abb2a807c1746835de9" dependencies = [ - "bindgen 0.72.0", + "bindgen 0.72.1", "bzip2-sys", "cc", "libc", @@ -3503,9 +3518,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -3525,9 +3540,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -3535,7 +3550,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "generator", "scoped-tls", "tracing", @@ -3574,16 +3589,16 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -3604,6 +3619,12 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + [[package]] name = "merlin" version = "3.0.0" @@ -3636,7 +3657,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "indexmap 2.10.0", + "indexmap 2.11.3", "ipnet", "metrics", "metrics-util", @@ -3732,7 +3753,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "downcast", "fragile", "mockall_derive", @@ -3746,7 +3767,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "proc-macro2", "quote", "syn 2.0.106", @@ -3843,12 +3864,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -3999,7 +4019,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", "syn 2.0.106", @@ -4042,8 +4062,8 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.2", - "cfg-if", + "bitflags 2.9.4", + "cfg-if 1.0.3", "foreign-types", "libc", "once_cell", @@ -4080,12 +4100,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "pairing" version = "0.23.0" @@ -4117,7 +4131,7 @@ version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "libc", "redox_syscall", "smallvec", @@ -4180,9 +4194,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -4191,7 +4205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.10.0", + "indexmap 2.11.3", ] [[package]] @@ -4307,13 +4321,13 @@ dependencies = [ "bs58", "ciborium", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "platform-serialization", "platform-version", "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", "treediff", ] @@ -4332,7 +4346,7 @@ dependencies = [ "bincode 2.0.0-rc.3", "grovedb-version", "once_cell", - "thiserror 2.0.15", + "thiserror 2.0.16", "versioned-feature-core 1.0.0 (git+https://github.com/dashpay/versioned-feature-core)", ] @@ -4351,7 +4365,7 @@ version = "0.1.0" dependencies = [ "dashcore", "dpp", - "indexmap 2.10.0", + "indexmap 2.11.3", "key-wallet", "key-wallet-manager", "serde", @@ -4403,9 +4417,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -4483,11 +4497,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.22.27", + "toml_edit 0.23.5", ] [[package]] @@ -4611,7 +4625,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "memchr", "unicase", ] @@ -4642,9 +4656,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -4653,8 +4667,8 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2 0.5.10", - "thiserror 2.0.15", + "socket2 0.6.0", + "thiserror 2.0.16", "tokio", "tracing", "web-time", @@ -4662,9 +4676,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "getrandom 0.3.3", @@ -4675,7 +4689,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.15", + "thiserror 2.0.16", "tinyvec", "tracing", "web-time", @@ -4683,16 +4697,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.0", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4795,11 +4809,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.5.0" +version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", ] [[package]] @@ -4828,7 +4842,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", ] [[package]] @@ -4853,47 +4867,32 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rend" @@ -4962,9 +4961,9 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" [[package]] name = "ring" @@ -4973,7 +4972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if", + "cfg-if 1.0.3", "getrandom 0.2.16", "libc", "untrusted", @@ -5059,7 +5058,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tonic-web-wasm-client", "tower-service", @@ -5090,7 +5089,7 @@ dependencies = [ "serde", "serde_json", "simple-signer", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tracing", "zeroize", @@ -5110,7 +5109,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tokio-test", "tracing", @@ -5140,9 +5139,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.2" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" +checksum = "c8975fc98059f365204d635119cf9c5a60ae67b841ed49b5422a9a7e56cdfac0" dependencies = [ "arrayvec", "borsh", @@ -5156,9 +5155,9 @@ dependencies = [ [[package]] name = "rust_decimal_macros" -version = "1.37.1" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6268b74858287e1a062271b988a0c534bf85bbeb567fe09331bf40ed78113d5" +checksum = "6dae310b657d2d686616e215c84c3119c675450d64c4b9f9e3467209191c3bcf" dependencies = [ "quote", "syn 2.0.106", @@ -5197,7 +5196,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5206,15 +5205,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.0", ] [[package]] @@ -5241,7 +5240,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.3.0", + "security-framework 3.4.0", ] [[package]] @@ -5265,9 +5264,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" dependencies = [ "ring", "rustls-pki-types", @@ -5315,11 +5314,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -5416,7 +5415,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5425,11 +5424,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5438,9 +5437,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -5448,16 +5447,17 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" dependencies = [ + "serde_core", "serde_derive", ] @@ -5493,18 +5493,28 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.17" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.225" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +dependencies = [ + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" dependencies = [ "proc-macro2", "quote", @@ -5513,25 +5523,27 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.3", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_path_to_error" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" dependencies = [ "itoa", "serde", + "serde_core", ] [[package]] @@ -5592,7 +5604,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.11.3", "schemars 0.9.0", "schemars 1.0.4", "serde", @@ -5642,7 +5654,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "cpufeatures", "digest", ] @@ -5653,7 +5665,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "cpufeatures", "digest", ] @@ -5791,9 +5803,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" [[package]] name = "spki" @@ -5822,11 +5834,12 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "std-shims" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ade0decb9133b9d3cc0e7d99129c3bedabc92553736545cc4979800eaf8c21" +checksum = "227c4f8561598188d0df96dbe749824576174bba278b5b6bb2eacff1066067d0" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.16.0", + "rustversion", "spin", ] @@ -5961,7 +5974,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5990,15 +6003,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.0", ] [[package]] @@ -6012,7 +6025,7 @@ dependencies = [ "lhash", "semver", "tenderdash-proto", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tokio-util", "tracing", @@ -6035,7 +6048,7 @@ dependencies = [ "serde", "subtle-encoding", "tenderdash-proto-compiler", - "thiserror 2.0.15", + "thiserror 2.0.16", "time", "tonic 0.14.2", "tonic-prost", @@ -6053,7 +6066,7 @@ dependencies = [ "tonic-prost-build", "ureq", "walkdir", - "zip 5.0.1", + "zip 5.1.1", ] [[package]] @@ -6086,7 +6099,7 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "proc-macro2", "quote", "syn 2.0.106", @@ -6115,11 +6128,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.15", + "thiserror-impl 2.0.16", ] [[package]] @@ -6135,9 +6148,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -6150,7 +6163,7 @@ version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", ] [[package]] @@ -6164,12 +6177,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", @@ -6179,15 +6191,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -6235,7 +6247,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -6335,7 +6347,7 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] @@ -6348,14 +6360,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.10.0", - "toml_datetime", + "indexmap 2.11.3", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -6365,12 +6386,33 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.3", "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.12", + "winnow 0.7.13", +] + +[[package]] +name = "toml_edit" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9" +dependencies = [ + "indexmap 2.11.3", + "toml_datetime 0.7.1", + "toml_parser", + "winnow 0.7.13", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow 0.7.13", ] [[package]] @@ -6387,7 +6429,7 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum 0.7.5", + "axum 0.7.9", "base64 0.22.1", "bytes", "h2", @@ -6496,7 +6538,7 @@ dependencies = [ "httparse", "js-sys", "pin-project", - "thiserror 2.0.15", + "thiserror 2.0.16", "tonic 0.14.2", "tower-service", "wasm-bindgen", @@ -6533,7 +6575,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap 2.10.0", + "indexmap 2.11.3", "pin-project-lite", "slab", "sync_wrapper", @@ -6550,7 +6592,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.4", "bytes", "futures-core", "futures-util", @@ -6640,14 +6682,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "serde", "serde_json", "sharded-slab", @@ -6659,6 +6701,17 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + [[package]] name = "treediff" version = "5.0.0" @@ -6694,9 +6747,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-normalization" @@ -6721,9 +6774,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00432f493971db5d8e47a65aeb3b02f8226b9b11f1450ff86bb772776ebadd70" +checksum = "99ba1025f18a4a3fc3e9b48c868e9beb4f24f4b4b1a325bada26bd4119f46537" dependencies = [ "base64 0.22.1", "flate2", @@ -6739,9 +6792,9 @@ dependencies = [ [[package]] name = "ureq-proto" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b6cabebbecc4c45189ab06b52f956206cea7d8c8a20851c35a85cb169224cc" +checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" dependencies = [ "base64 0.22.1", "http", @@ -6751,13 +6804,14 @@ dependencies = [ [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -6780,9 +6834,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -6867,7 +6921,7 @@ dependencies = [ "platform-value", "platform-version", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -6887,11 +6941,20 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -6900,7 +6963,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -6926,7 +6989,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "js-sys", "once_cell", "wasm-bindgen", @@ -7006,7 +7069,7 @@ dependencies = [ "serde", "serde-wasm-bindgen 0.5.0", "serde_json", - "thiserror 2.0.15", + "thiserror 2.0.16", "wasm-bindgen", "wasm-bindgen-futures", "wasm-logger", @@ -7026,7 +7089,7 @@ dependencies = [ "dpp", "drive", "hex", - "indexmap 2.10.0", + "indexmap 2.11.3", "js-sys", "nohash-hasher", "serde", @@ -7048,6 +7111,42 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-sdk" +version = "2.0.0" +dependencies = [ + "base64 0.22.1", + "bip39", + "bs58", + "console_error_panic_hook", + "dapi-grpc", + "dash-sdk", + "drive", + "drive-proof-verifier", + "getrandom 0.2.16", + "hex", + "hmac", + "js-sys", + "once_cell", + "platform-value", + "rand 0.8.5", + "rs-dapi-client", + "rs-sdk-trusted-context-provider", + "serde", + "serde-wasm-bindgen 0.6.5", + "serde_json", + "sha2", + "simple-signer", + "thiserror 2.0.16", + "tracing", + "tracing-subscriber", + "tracing-wasm", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wee_alloc", +] + [[package]] name = "wasm-streams" version = "0.4.2" @@ -7090,6 +7189,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + [[package]] name = "which" version = "4.4.2" @@ -7126,11 +7237,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -7146,9 +7257,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core", + "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -7158,7 +7269,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -7169,9 +7280,22 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.0", + "windows-result 0.4.0", + "windows-strings 0.5.0", ] [[package]] @@ -7180,8 +7304,8 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", "windows-threading", ] @@ -7213,14 +7337,20 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-numerics" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", ] [[package]] @@ -7229,9 +7359,9 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -7240,7 +7370,16 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -7249,7 +7388,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -7288,6 +7436,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -7325,7 +7482,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7342,7 +7499,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -7494,9 +7651,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -7507,18 +7664,15 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.3", "windows-sys 0.48.0", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.2", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "withdrawals-contract" @@ -7530,7 +7684,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "thiserror 2.0.15", + "thiserror 2.0.16", ] [[package]] @@ -7580,18 +7734,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -7695,14 +7849,14 @@ dependencies = [ [[package]] name = "zip" -version = "5.0.1" +version = "5.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7bd5b91aa407cb977468d108ca3fede2c992ea44bb1d989e21756749d7dccff" +checksum = "2f852905151ac8d4d06fdca66520a661c09730a74c6d4e2b0f27b436b382e532" dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.10.0", + "indexmap 2.11.3", "memchr", "zopfli", ] @@ -7755,9 +7909,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index d1e2045c4de..0041d4db196 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,11 +36,10 @@ members = [ "packages/rs-sdk-ffi", "packages/wasm-drive-verify", "packages/dash-platform-balance-checker", - "packages/rs-platform-wallet" + "packages/rs-platform-wallet", + "packages/wasm-sdk", ] -exclude = ["packages/wasm-sdk"] # This one is experimental and not ready for use - [workspace.package] rust-version = "1.89" diff --git a/Dockerfile b/Dockerfile index c3d9f5eda45..5560419149b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -397,6 +397,7 @@ COPY --parents \ packages/rs-sdk-ffi \ packages/check-features \ packages/dash-platform-balance-checker \ + packages/wasm-sdk \ /platform/ RUN --mount=type=secret,id=AWS \ @@ -485,6 +486,7 @@ COPY --parents \ packages/rs-sdk-ffi \ packages/check-features \ packages/dash-platform-balance-checker \ + packages/wasm-sdk \ /platform/ RUN mkdir /artifacts diff --git a/package.json b/package.json index bba77165207..92a04d68fbd 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,9 @@ "packages/withdrawals-contract", "packages/token-history-contract", "packages/keyword-search-contract", - "packages/wasm-drive-verify" + "packages/wasm-drive-verify", + "packages/wasm-sdk", + "packages/js-evo-sdk" ], "resolutions": { "pbkdf2": "^3.1.3", diff --git a/packages/js-evo-sdk/.gitignore b/packages/js-evo-sdk/.gitignore new file mode 100644 index 00000000000..1521c8b7652 --- /dev/null +++ b/packages/js-evo-sdk/.gitignore @@ -0,0 +1 @@ +dist diff --git a/packages/js-evo-sdk/.mocharc.yml b/packages/js-evo-sdk/.mocharc.yml new file mode 100644 index 00000000000..3acc6b84406 --- /dev/null +++ b/packages/js-evo-sdk/.mocharc.yml @@ -0,0 +1,4 @@ +require: + - tests/bootstrap.cjs +recursive: true +timeout: 8000 diff --git a/packages/js-evo-sdk/package.json b/packages/js-evo-sdk/package.json new file mode 100644 index 00000000000..f79365c7c9c --- /dev/null +++ b/packages/js-evo-sdk/package.json @@ -0,0 +1,70 @@ +{ + "name": "@dashevo/evo-sdk", + "version": "2.0.0", + "type": "module", + "main": "./dist/sdk.js", + "types": "./dist/sdk.d.ts", + "exports": { + ".": { + "types": "./dist/sdk.d.ts", + "import": "./dist/sdk.js" + }, + "./module": { + "import": "./dist/sdk.js" + } + }, + "sideEffects": false, + "engines": { + "node": ">=18.18" + }, + "files": [ + "dist/**", + "README.md" + ], + "dependencies": { + "@dashevo/wasm-sdk": "workspace:*" + }, + "scripts": { + "build": "rm -rf dist && tsc -p tsconfig.json && webpack --config webpack.config.cjs", + "lint": "eslint \"src/**/*.ts\" \"tests/**/*.*js\"", + "test": "yarn run test:unit && yarn run test:functional", + "test:unit": "mocha tests/unit/**/*.spec.mjs && karma start ./tests/karma/karma.conf.cjs --single-run", + "test:functional": "mocha tests/functional/**/*.spec.mjs --exit --timeout 90000 && karma start ./tests/karma/karma.functional.conf.cjs --single-run", + "prepack": "yarn build" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.55.0", + "@typescript-eslint/parser": "^5.55.0", + "assert": "^2.0.0", + "buffer": "^6.0.3", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", + "dirty-chai": "^2.0.1", + "eslint": "^8.53.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jsdoc": "^46.9.0", + "events": "^3.3.0", + "karma": "^6.4.3", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^2.1.2", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-webpack": "^5.0.0", + "mocha": "^11.1.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "sinon": "^17.0.1", + "sinon-chai": "^3.7.0", + "string_decoder": "^1.3.0", + "terser-webpack-plugin": "^5.3.11", + "ts-loader": "^9.5.0", + "typescript": "^3.9.5", + "url": "^0.11.3", + "util": "^0.12.4", + "webpack": "^5.94.0", + "webpack-cli": "^4.9.1" + } +} diff --git a/packages/js-evo-sdk/src/.eslintrc.cjs b/packages/js-evo-sdk/src/.eslintrc.cjs new file mode 100644 index 00000000000..d226e396249 --- /dev/null +++ b/packages/js-evo-sdk/src/.eslintrc.cjs @@ -0,0 +1,33 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: ['../tsconfig.json'], + tsconfigRootDir: __dirname, + }, + env: { + es2020: true, + browser: true, + node: true, + }, + extends: [ + 'airbnb-base', + 'airbnb-typescript/base', + ], + plugins: [ + '@typescript-eslint', + ], + rules: { + 'import/extensions': 'off', + 'import/prefer-default-export': 'off', + 'object-curly-newline': 'off', + 'class-methods-use-this': 'off', + 'max-len': 'off', + 'no-restricted-exports': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/lines-between-class-members': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', ignoreRestSiblings: true }], + }, + ignorePatterns: [ + '*.d.ts', + ], +}; diff --git a/packages/js-evo-sdk/src/contracts/facade.ts b/packages/js-evo-sdk/src/contracts/facade.ts new file mode 100644 index 00000000000..a05811b5654 --- /dev/null +++ b/packages/js-evo-sdk/src/contracts/facade.ts @@ -0,0 +1,65 @@ +import * as wasm from '../wasm.js'; +import { asJsonString } from '../util.js'; +import type { EvoSDK } from '../sdk.js'; + +export class ContractsFacade { + private sdk: EvoSDK; + + constructor(sdk: EvoSDK) { + this.sdk = sdk; + } + + async fetch(contractId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDataContract(contractId); + } + + async fetchWithProof(contractId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDataContractWithProofInfo(contractId); + } + + async getHistory(args: { contractId: string; limit?: number; startAtMs?: number | bigint }): Promise { + const { contractId, limit, startAtMs } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.getDataContractHistory( + contractId, + limit ?? null, + null, + startAtMs != null ? BigInt(startAtMs) : null, + ); + } + + async getHistoryWithProof(args: { contractId: string; limit?: number; startAtMs?: number | bigint }): Promise { + const { contractId, limit, startAtMs } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.getDataContractHistoryWithProofInfo( + contractId, + limit ?? null, + null, + startAtMs != null ? BigInt(startAtMs) : null, + ); + } + + async getMany(contractIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDataContracts(contractIds); + } + + async getManyWithProof(contractIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDataContractsWithProofInfo(contractIds); + } + + async create(args: { ownerId: string; definition: unknown; privateKeyWif: string; keyId?: number }): Promise { + const { ownerId, definition, privateKeyWif, keyId } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.contractCreate(ownerId, asJsonString(definition)!, privateKeyWif, keyId ?? null); + } + + async update(args: { contractId: string; ownerId: string; updates: unknown; privateKeyWif: string; keyId?: number }): Promise { + const { contractId, ownerId, updates, privateKeyWif, keyId } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.contractUpdate(contractId, ownerId, asJsonString(updates)!, privateKeyWif, keyId ?? null); + } +} diff --git a/packages/js-evo-sdk/src/documents/facade.ts b/packages/js-evo-sdk/src/documents/facade.ts new file mode 100644 index 00000000000..b5061e4c5b4 --- /dev/null +++ b/packages/js-evo-sdk/src/documents/facade.ts @@ -0,0 +1,135 @@ +import { asJsonString } from '../util.js'; +import type { EvoSDK } from '../sdk.js'; + +export class DocumentsFacade { + private sdk: EvoSDK; + + constructor(sdk: EvoSDK) { + this.sdk = sdk; + } + + // Query many documents + async query(params: { + contractId: string; + type: string; + where?: unknown; + orderBy?: unknown; + limit?: number; + startAfter?: string; + startAt?: string; + }): Promise { + const { contractId, type, where, orderBy, limit, startAfter, startAt } = params; + const whereJson = asJsonString(where); + const orderJson = asJsonString(orderBy); + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocuments( + contractId, + type, + whereJson ?? null, + orderJson ?? null, + limit ?? null, + startAfter ?? null, + startAt ?? null, + ); + } + + async queryWithProof(params: { + contractId: string; + type: string; + where?: unknown; + orderBy?: unknown; + limit?: number; + startAfter?: string; + startAt?: string; + }): Promise { + const { contractId, type, where, orderBy, limit, startAfter, startAt } = params; + const whereJson = asJsonString(where); + const orderJson = asJsonString(orderBy); + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsWithProofInfo( + contractId, + type, + whereJson ?? null, + orderJson ?? null, + limit ?? null, + startAfter ?? null, + startAt ?? null, + ); + } + + async get(contractId: string, type: string, documentId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocument(contractId, type, documentId); + } + + async getWithProof(contractId: string, type: string, documentId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentWithProofInfo(contractId, type, documentId); + } + + async create(args: { + contractId: string; + type: string; + ownerId: string; + data: unknown; + entropyHex: string; + privateKeyWif: string; + }): Promise { + const { contractId, type, ownerId, data, entropyHex, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.documentCreate( + contractId, + type, + ownerId, + asJsonString(data)!, + entropyHex, + privateKeyWif, + ); + } + + async replace(args: { + contractId: string; + type: string; + documentId: string; + ownerId: string; + data: unknown; + revision: number | bigint; + privateKeyWif: string; + }): Promise { + const { contractId, type, documentId, ownerId, data, revision, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.documentReplace( + contractId, + type, + documentId, + ownerId, + asJsonString(data)!, + BigInt(revision), + privateKeyWif, + ); + } + + async delete(args: { contractId: string; type: string; documentId: string; ownerId: string; privateKeyWif: string }): Promise { + const { contractId, type, documentId, ownerId, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.documentDelete(contractId, type, documentId, ownerId, privateKeyWif); + } + + async transfer(args: { contractId: string; type: string; documentId: string; ownerId: string; recipientId: string; privateKeyWif: string }): Promise { + const { contractId, type, documentId, ownerId, recipientId, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.documentTransfer(contractId, type, documentId, ownerId, recipientId, privateKeyWif); + } + + async purchase(args: { contractId: string; type: string; documentId: string; buyerId: string; price: number | bigint | string; privateKeyWif: string }): Promise { + const { contractId, type, documentId, buyerId, price, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.documentPurchase(contractId, type, documentId, buyerId, BigInt(price), privateKeyWif); + } + + async setPrice(args: { contractId: string; type: string; documentId: string; ownerId: string; price: number | bigint | string; privateKeyWif: string }): Promise { + const { contractId, type, documentId, ownerId, price, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.documentSetPrice(contractId, type, documentId, ownerId, BigInt(price), privateKeyWif); + } +} diff --git a/packages/js-evo-sdk/src/dpns/facade.ts b/packages/js-evo-sdk/src/dpns/facade.ts new file mode 100644 index 00000000000..a4bcf83038b --- /dev/null +++ b/packages/js-evo-sdk/src/dpns/facade.ts @@ -0,0 +1,70 @@ +import * as wasm from '../wasm.js'; +import type { EvoSDK } from '../sdk.js'; + +export class DpnsFacade { + private sdk: EvoSDK; + + constructor(sdk: EvoSDK) { + this.sdk = sdk; + } + + convertToHomographSafe(input: string): string { + return wasm.WasmSdk.dpnsConvertToHomographSafe(input); + } + + isValidUsername(label: string): boolean { + return wasm.WasmSdk.dpnsIsValidUsername(label); + } + + isContestedUsername(label: string): boolean { + return wasm.WasmSdk.dpnsIsContestedUsername(label); + } + + async isNameAvailable(label: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.dpnsIsNameAvailable(label); + } + + async resolveName(name: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.dpnsResolveName(name); + } + + async registerName(args: { label: string; identityId: string; publicKeyId: number; privateKeyWif: string; onPreorder?: Function }): Promise { + const { label, identityId, publicKeyId, privateKeyWif, onPreorder } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.dpnsRegisterName(label, identityId, publicKeyId, privateKeyWif, onPreorder ?? null); + } + + async usernames(identityId: string, opts: { limit?: number } = {}): Promise { + const { limit } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getDpnsUsernames(identityId, limit ?? null); + } + + async username(identityId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDpnsUsername(identityId); + } + + async usernamesWithProof(identityId: string, opts: { limit?: number } = {}): Promise { + const { limit } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getDpnsUsernamesWithProofInfo(identityId, limit ?? null); + } + + async usernameWithProof(identityId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDpnsUsernameWithProofInfo(identityId); + } + + async getUsernameByName(username: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDpnsUsernameByName(username); + } + + async getUsernameByNameWithProof(username: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDpnsUsernameByNameWithProofInfo(username); + } +} diff --git a/packages/js-evo-sdk/src/epoch/facade.ts b/packages/js-evo-sdk/src/epoch/facade.ts new file mode 100644 index 00000000000..9134f100341 --- /dev/null +++ b/packages/js-evo-sdk/src/epoch/facade.ts @@ -0,0 +1,55 @@ +import type { EvoSDK } from '../sdk.js'; + +export class EpochFacade { + private sdk: EvoSDK; + constructor(sdk: EvoSDK) { this.sdk = sdk; } + + async epochsInfo(params: { startEpoch?: number; count?: number; ascending?: boolean } = {}): Promise { + const { startEpoch, count, ascending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getEpochsInfo(startEpoch ?? null, count ?? null, ascending ?? null); + } + + async epochsInfoWithProof(params: { startEpoch?: number; count?: number; ascending?: boolean } = {}): Promise { + const { startEpoch, count, ascending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getEpochsInfoWithProofInfo(startEpoch ?? null, count ?? null, ascending ?? null); + } + + async finalizedInfos(params: { startEpoch?: number; count?: number; ascending?: boolean } = {}): Promise { + const { startEpoch, count, ascending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getFinalizedEpochInfos(startEpoch ?? null, count ?? null, ascending ?? null); + } + + async finalizedInfosWithProof(params: { startEpoch?: number; count?: number; ascending?: boolean } = {}): Promise { + const { startEpoch, count, ascending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getFinalizedEpochInfosWithProofInfo(startEpoch ?? null, count ?? null, ascending ?? null); + } + + async current(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getCurrentEpoch(); } + async currentWithProof(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getCurrentEpochWithProofInfo(); } + + async evonodesProposedBlocksByIds(epoch: number, ids: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getEvonodesProposedEpochBlocksByIds(epoch, ids); + } + + async evonodesProposedBlocksByIdsWithProof(epoch: number, ids: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getEvonodesProposedEpochBlocksByIdsWithProofInfo(epoch, ids); + } + + async evonodesProposedBlocksByRange(epoch: number, opts: { limit?: number; startAfter?: string; orderAscending?: boolean } = {}): Promise { + const { limit, startAfter, orderAscending } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getEvonodesProposedEpochBlocksByRange(epoch, limit ?? null, startAfter ?? null, orderAscending ?? null); + } + + async evonodesProposedBlocksByRangeWithProof(epoch: number, opts: { limit?: number; startAfter?: string; orderAscending?: boolean } = {}): Promise { + const { limit, startAfter, orderAscending } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getEvonodesProposedEpochBlocksByRangeWithProofInfo(epoch, limit ?? null, startAfter ?? null, orderAscending ?? null); + } +} diff --git a/packages/js-evo-sdk/src/group/facade.ts b/packages/js-evo-sdk/src/group/facade.ts new file mode 100644 index 00000000000..3f8093f7104 --- /dev/null +++ b/packages/js-evo-sdk/src/group/facade.ts @@ -0,0 +1,30 @@ +import type { EvoSDK } from '../sdk.js'; + +export class GroupFacade { + private sdk: EvoSDK; + constructor(sdk: EvoSDK) { this.sdk = sdk; } + + async contestedResources(params: { documentTypeName: string; contractId: string; indexName: string; startAtValue?: Uint8Array; limit?: number; orderAscending?: boolean }): Promise { + const { documentTypeName, contractId, indexName, startAtValue, limit, orderAscending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResources(documentTypeName, contractId, indexName, startAtValue ?? null, limit ?? null, null, orderAscending ?? null); + } + + async contestedResourcesWithProof(params: { documentTypeName: string; contractId: string; indexName: string; startAtValue?: Uint8Array; limit?: number; orderAscending?: boolean }): Promise { + const { documentTypeName, contractId, indexName, startAtValue, limit, orderAscending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourcesWithProofInfo(documentTypeName, contractId, indexName, startAtValue ?? null, limit ?? null, null, orderAscending ?? null); + } + + async contestedResourceVotersForIdentity(params: { contractId: string; documentTypeName: string; indexName: string; indexValues: any[]; contestantId: string; startAtVoterInfo?: string; limit?: number; orderAscending?: boolean }): Promise { + const { contractId, documentTypeName, indexName, indexValues, contestantId, startAtVoterInfo, limit, orderAscending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourceVotersForIdentity(contractId, documentTypeName, indexName, indexValues, contestantId, startAtVoterInfo ?? null, limit ?? null, orderAscending ?? null); + } + + async contestedResourceVotersForIdentityWithProof(params: { contractId: string; documentTypeName: string; indexName: string; indexValues: any[]; contestantId: string; startAtIdentifierInfo?: string; count?: number; orderAscending?: boolean }): Promise { + const { contractId, documentTypeName, indexName, indexValues, contestantId, startAtIdentifierInfo, count, orderAscending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourceVotersForIdentityWithProofInfo(contractId, documentTypeName, indexName, indexValues, contestantId, startAtIdentifierInfo ?? null, count ?? null, orderAscending ?? null); + } +} diff --git a/packages/js-evo-sdk/src/identities/facade.ts b/packages/js-evo-sdk/src/identities/facade.ts new file mode 100644 index 00000000000..94a448ccd11 --- /dev/null +++ b/packages/js-evo-sdk/src/identities/facade.ts @@ -0,0 +1,75 @@ +import * as wasm from '../wasm.js'; +import { asJsonString } from '../util.js'; +import type { EvoSDK } from '../sdk.js'; + +export class IdentitiesFacade { + private sdk: EvoSDK; + + constructor(sdk: EvoSDK) { + this.sdk = sdk; + } + + async fetch(identityId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentity(identityId); + } + + async fetchWithProof(identityId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentityWithProofInfo(identityId); + } + + async fetchUnproved(identityId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentityUnproved(identityId); + } + + async getKeys(args: { identityId: string; keyRequestType: 'all' | 'specific' | 'search'; specificKeyIds?: number[]; searchPurposeMap?: unknown; limit?: number; offset?: number }): Promise { + const { identityId, keyRequestType, specificKeyIds, searchPurposeMap, limit, offset } = args; + const mapJson = asJsonString(searchPurposeMap); + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentityKeys( + identityId, + keyRequestType, + specificKeyIds ? Uint32Array.from(specificKeyIds) : null, + mapJson ?? null, + limit ?? null, + offset ?? null, + ); + } + + async create(args: { assetLockProof: unknown; assetLockPrivateKeyWif: string; publicKeys: unknown[] }): Promise { + const { assetLockProof, assetLockPrivateKeyWif, publicKeys } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.identityCreate(asJsonString(assetLockProof)!, assetLockPrivateKeyWif, asJsonString(publicKeys)!); + } + + async topUp(args: { identityId: string; assetLockProof: unknown; assetLockPrivateKeyWif: string }): Promise { + const { identityId, assetLockProof, assetLockPrivateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.identityTopUp(identityId, asJsonString(assetLockProof)!, assetLockPrivateKeyWif); + } + + async creditTransfer(args: { senderId: string; recipientId: string; amount: number | bigint | string; privateKeyWif: string; keyId?: number }): Promise { + const { senderId, recipientId, amount, privateKeyWif, keyId } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.identityCreditTransfer(senderId, recipientId, BigInt(amount), privateKeyWif, keyId ?? null); + } + + async creditWithdrawal(args: { identityId: string; toAddress: string; amount: number | bigint | string; coreFeePerByte?: number; privateKeyWif: string; keyId?: number }): Promise { + const { identityId, toAddress, amount, coreFeePerByte = 1, privateKeyWif, keyId } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.identityCreditWithdrawal(identityId, toAddress, BigInt(amount), coreFeePerByte ?? null, privateKeyWif, keyId ?? null); + } + + async update(args: { identityId: string; addPublicKeys?: unknown[]; disablePublicKeyIds?: number[]; privateKeyWif: string }): Promise { + const { identityId, addPublicKeys, disablePublicKeyIds, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.identityUpdate( + identityId, + addPublicKeys ? asJsonString(addPublicKeys)! : null, + disablePublicKeyIds ? Uint32Array.from(disablePublicKeyIds) : null, + privateKeyWif, + ); + } +} diff --git a/packages/js-evo-sdk/src/protocol/facade.ts b/packages/js-evo-sdk/src/protocol/facade.ts new file mode 100644 index 00000000000..1eb9f5dcdac --- /dev/null +++ b/packages/js-evo-sdk/src/protocol/facade.ts @@ -0,0 +1,21 @@ +import type { EvoSDK } from '../sdk.js'; + +export class ProtocolFacade { + private sdk: EvoSDK; + constructor(sdk: EvoSDK) { this.sdk = sdk; } + + async versionUpgradeState(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getProtocolVersionUpgradeState(); } + async versionUpgradeStateWithProof(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getProtocolVersionUpgradeStateWithProofInfo(); } + + async versionUpgradeVoteStatus(params: { startProTxHash: string; count: number }): Promise { + const { startProTxHash, count } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getProtocolVersionUpgradeVoteStatus(startProTxHash, count); + } + + async versionUpgradeVoteStatusWithProof(params: { startProTxHash: string; count: number }): Promise { + const { startProTxHash, count } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getProtocolVersionUpgradeVoteStatusWithProofInfo(startProTxHash, count); + } +} diff --git a/packages/js-evo-sdk/src/sdk.ts b/packages/js-evo-sdk/src/sdk.ts new file mode 100644 index 00000000000..992d3d21825 --- /dev/null +++ b/packages/js-evo-sdk/src/sdk.ts @@ -0,0 +1,133 @@ +import * as wasm from './wasm.js'; +import { ensureInitialized as initWasm } from './wasm.js'; +import { DocumentsFacade } from './documents/facade.js'; +import { IdentitiesFacade } from './identities/facade.js'; +import { ContractsFacade } from './contracts/facade.js'; +import { TokensFacade } from './tokens/facade.js'; +import { DpnsFacade } from './dpns/facade.js'; +import { EpochFacade } from './epoch/facade.js'; +import { ProtocolFacade } from './protocol/facade.js'; +import { SystemFacade } from './system/facade.js'; +import { GroupFacade } from './group/facade.js'; +import { VotingFacade } from './voting/facade.js'; + +export interface ConnectionOptions { + version?: number; + proofs?: boolean; + // Configure tracing/logging emitted from the underlying Wasm SDK. + // Accepts simple levels: 'off' | 'error' | 'warn' | 'info' | 'debug' | 'trace' + // or a full EnvFilter string like: 'wasm_sdk=debug,rs_dapi_client=warn' + logs?: string; + settings?: { + connectTimeoutMs?: number; + timeoutMs?: number; + retries?: number; + banFailedAddress?: boolean; + }; +} + +export interface EvoSDKOptions extends ConnectionOptions { + network?: 'testnet' | 'mainnet'; + trusted?: boolean; +} + +export class EvoSDK { + private wasmSdk?: wasm.WasmSdk; + private options: Required> & ConnectionOptions; + + public documents!: DocumentsFacade; + public identities!: IdentitiesFacade; + public contracts!: ContractsFacade; + public tokens!: TokensFacade; + public dpns!: DpnsFacade; + public epoch!: EpochFacade; + public protocol!: ProtocolFacade; + public system!: SystemFacade; + public group!: GroupFacade; + public voting!: VotingFacade; + + constructor(options: EvoSDKOptions = {}) { + // Apply defaults while preserving any future connection options + const { network = 'testnet', trusted = false, ...connection } = options; + this.options = { network, trusted, ...connection }; + + this.documents = new DocumentsFacade(this); + this.identities = new IdentitiesFacade(this); + this.contracts = new ContractsFacade(this); + this.tokens = new TokensFacade(this); + this.dpns = new DpnsFacade(this); + this.epoch = new EpochFacade(this); + this.protocol = new ProtocolFacade(this); + this.system = new SystemFacade(this); + this.group = new GroupFacade(this); + this.voting = new VotingFacade(this); + } + + get wasm(): wasm.WasmSdk { + if (!this.wasmSdk) throw new Error('SDK is not connected. Call EvoSDK#connect() first.'); + return this.wasmSdk; + } + + get isConnected(): boolean { return !!this.wasmSdk; } + + async getWasmSdkConnected(): Promise { + if (!this.wasmSdk) { + await this.connect(); + } + return this.wasmSdk as wasm.WasmSdk; + } + + async connect(): Promise { + if (this.wasmSdk) return; // idempotent + await initWasm(); + + const { network, trusted, version, proofs, settings, logs } = this.options; + + let builder: wasm.WasmSdkBuilder; + if (network === 'mainnet') { + await wasm.WasmSdk.prefetchTrustedQuorumsMainnet(); + + builder = trusted ? wasm.WasmSdkBuilder.mainnetTrusted() : wasm.WasmSdkBuilder.mainnet(); + } else if (network === 'testnet') { + await wasm.WasmSdk.prefetchTrustedQuorumsTestnet(); + + builder = trusted ? wasm.WasmSdkBuilder.testnetTrusted() : wasm.WasmSdkBuilder.testnet(); + } else { + throw new Error(`Unknown network: ${network}`); + } + + if (version) builder = builder.withVersion(version); + if (typeof proofs === 'boolean') builder = builder.withProofs(proofs); + if (logs) builder = builder.withLogs(logs); + if (settings) { + const { connectTimeoutMs, timeoutMs, retries, banFailedAddress } = settings; + builder = builder.withSettings(connectTimeoutMs ?? null, timeoutMs ?? null, retries ?? null, banFailedAddress ?? null); + } + + this.wasmSdk = builder.build(); + } + + static fromWasm(wasmSdk: wasm.WasmSdk): EvoSDK { + const sdk = new EvoSDK(); + (sdk as any).wasmSdk = wasmSdk; + return sdk; + } + + // Factory helpers that return configured instances (not connected) + static testnet(options: ConnectionOptions = {}): EvoSDK { return new EvoSDK({ network: 'testnet', ...options }); } + static mainnet(options: ConnectionOptions = {}): EvoSDK { return new EvoSDK({ network: 'mainnet', ...options }); } + static testnetTrusted(options: ConnectionOptions = {}): EvoSDK { return new EvoSDK({ network: 'testnet', trusted: true, ...options }); } + static mainnetTrusted(options: ConnectionOptions = {}): EvoSDK { return new EvoSDK({ network: 'mainnet', trusted: true, ...options }); } +} + +export { DocumentsFacade } from './documents/facade.js'; +export { IdentitiesFacade } from './identities/facade.js'; +export { ContractsFacade } from './contracts/facade.js'; +export { TokensFacade } from './tokens/facade.js'; +export { DpnsFacade } from './dpns/facade.js'; +export { EpochFacade } from './epoch/facade.js'; +export { ProtocolFacade } from './protocol/facade.js'; +export { SystemFacade } from './system/facade.js'; +export { GroupFacade } from './group/facade.js'; +export { VotingFacade } from './voting/facade.js'; +// For error types, import directly from '@dashevo/wasm-sdk/errors' diff --git a/packages/js-evo-sdk/src/system/facade.ts b/packages/js-evo-sdk/src/system/facade.ts new file mode 100644 index 00000000000..1c93695bae3 --- /dev/null +++ b/packages/js-evo-sdk/src/system/facade.ts @@ -0,0 +1,16 @@ +import type { EvoSDK } from '../sdk.js'; + +export class SystemFacade { + private sdk: EvoSDK; + constructor(sdk: EvoSDK) { this.sdk = sdk; } + + async status(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getStatus(); } + async currentQuorumsInfo(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getCurrentQuorumsInfo(); } + async totalCreditsInPlatform(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getTotalCreditsInPlatform(); } + async totalCreditsInPlatformWithProof(): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getTotalCreditsInPlatformWithProofInfo(); } + async prefundedSpecializedBalance(identityId: string): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getPrefundedSpecializedBalance(identityId); } + async prefundedSpecializedBalanceWithProof(identityId: string): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getPrefundedSpecializedBalanceWithProofInfo(identityId); } + async waitForStateTransitionResult(stateTransitionHash: string): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.waitForStateTransitionResult(stateTransitionHash); } + async pathElements(path: string[], keys: string[]): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getPathElements(path, keys); } + async pathElementsWithProof(path: string[], keys: string[]): Promise { const w = await this.sdk.getWasmSdkConnected(); return w.getPathElementsWithProofInfo(path, keys); } +} diff --git a/packages/js-evo-sdk/src/tokens/facade.ts b/packages/js-evo-sdk/src/tokens/facade.ts new file mode 100644 index 00000000000..853b16c6fc2 --- /dev/null +++ b/packages/js-evo-sdk/src/tokens/facade.ts @@ -0,0 +1,157 @@ +import { asJsonString } from '../util.js'; +import type { EvoSDK } from '../sdk.js'; + +export class TokensFacade { + private sdk: EvoSDK; + + constructor(sdk: EvoSDK) { + this.sdk = sdk; + } + + // Queries + async priceByContract(contractId: string, tokenPosition: number): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenPriceByContract(contractId, tokenPosition); + } + + async totalSupply(tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenTotalSupply(tokenId); + } + + async totalSupplyWithProof(tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenTotalSupplyWithProofInfo(tokenId); + } + + async statuses(tokenIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenStatuses(tokenIds); + } + + async statusesWithProof(tokenIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenStatusesWithProofInfo(tokenIds); + } + + async balances(identityIds: string[], tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentitiesTokenBalances(identityIds, tokenId); + } + + async balancesWithProof(identityIds: string[], tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentitiesTokenBalancesWithProofInfo(identityIds, tokenId); + } + + async identityTokenInfos(identityId: string, tokenIds: string[], _opts: { limit?: number; offset?: number } = {}): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentityTokenInfos(identityId, tokenIds); + } + + async identitiesTokenInfos(identityIds: string[], tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentitiesTokenInfos(identityIds, tokenId); + } + + async identityTokenInfosWithProof(identityId: string, tokenIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentityTokenInfosWithProofInfo(identityId, tokenIds); + } + + async identitiesTokenInfosWithProof(identityIds: string[], tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getIdentitiesTokenInfosWithProofInfo(identityIds, tokenId); + } + + async directPurchasePrices(tokenIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenDirectPurchasePrices(tokenIds); + } + + async directPurchasePricesWithProof(tokenIds: string[]): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenDirectPurchasePricesWithProofInfo(tokenIds); + } + + async contractInfo(contractId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenContractInfo(contractId); + } + + async contractInfoWithProof(contractId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenContractInfoWithProofInfo(contractId); + } + + async perpetualDistributionLastClaim(identityId: string, tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenPerpetualDistributionLastClaim(identityId, tokenId); + } + + async perpetualDistributionLastClaimWithProof(identityId: string, tokenId: string): Promise { + const w = await this.sdk.getWasmSdkConnected(); + return w.getTokenPerpetualDistributionLastClaimWithProofInfo(identityId, tokenId); + } + + // Transitions + async mint(args: { contractId: string; tokenPosition: number; amount: number | string | bigint; identityId: string; privateKeyWif: string; recipientId?: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, amount, identityId, privateKeyWif, recipientId, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenMint(contractId, tokenPosition, String(amount), identityId, privateKeyWif, recipientId ?? null, publicNote ?? null); + } + + async burn(args: { contractId: string; tokenPosition: number; amount: number | string | bigint; identityId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, amount, identityId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenBurn(contractId, tokenPosition, String(amount), identityId, privateKeyWif, publicNote ?? null); + } + + async transfer(args: { contractId: string; tokenPosition: number; amount: number | string | bigint; senderId: string; recipientId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, amount, senderId, recipientId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenTransfer(contractId, tokenPosition, String(amount), senderId, recipientId, privateKeyWif, publicNote ?? null); + } + + async freeze(args: { contractId: string; tokenPosition: number; identityToFreeze: string; freezerId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, identityToFreeze, freezerId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenFreeze(contractId, tokenPosition, identityToFreeze, freezerId, privateKeyWif, publicNote ?? null); + } + + async unfreeze(args: { contractId: string; tokenPosition: number; identityToUnfreeze: string; unfreezerId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, identityToUnfreeze, unfreezerId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenUnfreeze(contractId, tokenPosition, identityToUnfreeze, unfreezerId, privateKeyWif, publicNote ?? null); + } + + async destroyFrozen(args: { contractId: string; tokenPosition: number; identityId: string; destroyerId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, identityId, destroyerId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenDestroyFrozen(contractId, tokenPosition, identityId, destroyerId, privateKeyWif, publicNote ?? null); + } + + async setPriceForDirectPurchase(args: { contractId: string; tokenPosition: number; identityId: string; priceType: string; priceData: unknown; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, identityId, priceType, priceData, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenSetPriceForDirectPurchase(contractId, tokenPosition, identityId, priceType, asJsonString(priceData)!, privateKeyWif, publicNote ?? null); + } + + async directPurchase(args: { contractId: string; tokenPosition: number; amount: number | string | bigint; identityId: string; totalAgreedPrice?: number | string | bigint | null; privateKeyWif: string }): Promise { + const { contractId, tokenPosition, amount, identityId, totalAgreedPrice, privateKeyWif } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenDirectPurchase(contractId, tokenPosition, String(amount), identityId, totalAgreedPrice != null ? String(totalAgreedPrice) : null, privateKeyWif); + } + + async claim(args: { contractId: string; tokenPosition: number; distributionType: string; identityId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, distributionType, identityId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenClaim(contractId, tokenPosition, distributionType, identityId, privateKeyWif, publicNote ?? null); + } + + async configUpdate(args: { contractId: string; tokenPosition: number; configItemType: string; configValue: unknown; identityId: string; privateKeyWif: string; publicNote?: string }): Promise { + const { contractId, tokenPosition, configItemType, configValue, identityId, privateKeyWif, publicNote } = args; + const w = await this.sdk.getWasmSdkConnected(); + return w.tokenConfigUpdate(contractId, tokenPosition, configItemType, asJsonString(configValue)!, identityId, privateKeyWif, publicNote ?? null); + } +} diff --git a/packages/js-evo-sdk/src/util.ts b/packages/js-evo-sdk/src/util.ts new file mode 100644 index 00000000000..36d5f27d2fb --- /dev/null +++ b/packages/js-evo-sdk/src/util.ts @@ -0,0 +1,5 @@ +export function asJsonString(value: unknown): string | undefined { + if (value == null) return undefined; + if (typeof value === 'string') return value; + return JSON.stringify(value); +} diff --git a/packages/js-evo-sdk/src/voting/facade.ts b/packages/js-evo-sdk/src/voting/facade.ts new file mode 100644 index 00000000000..dcc9e615a8e --- /dev/null +++ b/packages/js-evo-sdk/src/voting/facade.ts @@ -0,0 +1,52 @@ +import { asJsonString } from '../util.js'; +import type { EvoSDK } from '../sdk.js'; + +export class VotingFacade { + private sdk: EvoSDK; + constructor(sdk: EvoSDK) { this.sdk = sdk; } + + async contestedResourceVoteState(params: { contractId: string; documentTypeName: string; indexName: string; indexValues: any[]; resultType: string; allowIncludeLockedAndAbstainingVoteTally?: boolean; startAtIdentifierInfo?: string; count?: number; orderAscending?: boolean }): Promise { + const { contractId, documentTypeName, indexName, indexValues, resultType, allowIncludeLockedAndAbstainingVoteTally, startAtIdentifierInfo, count, orderAscending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourceVoteState(contractId, documentTypeName, indexName, indexValues, resultType, allowIncludeLockedAndAbstainingVoteTally ?? null, startAtIdentifierInfo ?? null, count ?? null, orderAscending ?? null); + } + + async contestedResourceVoteStateWithProof(params: { contractId: string; documentTypeName: string; indexName: string; indexValues: any[]; resultType: string; allowIncludeLockedAndAbstainingVoteTally?: boolean; startAtIdentifierInfo?: string; count?: number; orderAscending?: boolean }): Promise { + const { contractId, documentTypeName, indexName, indexValues, resultType, allowIncludeLockedAndAbstainingVoteTally, startAtIdentifierInfo, count, orderAscending } = params; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourceVoteStateWithProofInfo(contractId, documentTypeName, indexName, indexValues, resultType, allowIncludeLockedAndAbstainingVoteTally ?? null, startAtIdentifierInfo ?? null, count ?? null, orderAscending ?? null); + } + + async contestedResourceIdentityVotes(identityId: string, opts: { limit?: number; startAtVotePollIdInfo?: string; orderAscending?: boolean } = {}): Promise { + const { limit, startAtVotePollIdInfo, orderAscending } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourceIdentityVotes(identityId, limit ?? null, startAtVotePollIdInfo ?? null, orderAscending ?? null); + } + + async contestedResourceIdentityVotesWithProof(identityId: string, opts: { limit?: number; offset?: number; orderAscending?: boolean } = {}): Promise { + const { limit, offset, orderAscending } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getContestedResourceIdentityVotesWithProofInfo(identityId, limit ?? null, offset ?? null, orderAscending ?? null); + } + + async votePollsByEndDate(opts: { startTimeInfo?: string; endTimeInfo?: string; limit?: number; orderAscending?: boolean } = {}): Promise { + const { startTimeInfo, endTimeInfo, limit, orderAscending } = opts; + const w = await this.sdk.getWasmSdkConnected(); + return w.getVotePollsByEndDate(startTimeInfo ?? null, endTimeInfo ?? null, limit ?? null, orderAscending ?? null); + } + + async votePollsByEndDateWithProof(opts: { startTimeMs?: number | bigint | null; endTimeMs?: number | bigint | null; limit?: number; offset?: number; orderAscending?: boolean } = {}): Promise { + const { startTimeMs, endTimeMs, limit, offset, orderAscending } = opts; + const start = startTimeMs != null ? BigInt(startTimeMs) : null; + const end = endTimeMs != null ? BigInt(endTimeMs) : null; + const w = await this.sdk.getWasmSdkConnected(); + return w.getVotePollsByEndDateWithProofInfo(start ?? null, end ?? null, limit ?? null, offset ?? null, orderAscending ?? null); + } + + async masternodeVote(args: { masternodeProTxHash: string; contractId: string; documentTypeName: string; indexName: string; indexValues: string | any[]; voteChoice: string; votingKeyWif: string }): Promise { + const { masternodeProTxHash, contractId, documentTypeName, indexName, indexValues, voteChoice, votingKeyWif } = args; + const indexValuesStr = typeof indexValues === 'string' ? indexValues : asJsonString(indexValues)!; + const w = await this.sdk.getWasmSdkConnected(); + return w.masternodeVote(masternodeProTxHash, contractId, documentTypeName, indexName, indexValuesStr, voteChoice, votingKeyWif); + } +} diff --git a/packages/js-evo-sdk/src/wasm.ts b/packages/js-evo-sdk/src/wasm.ts new file mode 100644 index 00000000000..167e742d5ff --- /dev/null +++ b/packages/js-evo-sdk/src/wasm.ts @@ -0,0 +1,15 @@ +// ESM wrapper around @dashevo/wasm-sdk with one-time init +import initWasmSdk, * as wasm from '@dashevo/wasm-sdk'; + +let initPromise: Promise | undefined; + +export async function ensureInitialized(): Promise { + if (!initPromise) { + initPromise = initWasmSdk().then(() => wasm); + } + return initPromise; +} + +// Re-export all wasm SDK symbols for convenience +export * from '@dashevo/wasm-sdk'; +export { default } from '@dashevo/wasm-sdk'; diff --git a/packages/js-evo-sdk/tests/.eslintrc.yml b/packages/js-evo-sdk/tests/.eslintrc.yml new file mode 100644 index 00000000000..509d508a284 --- /dev/null +++ b/packages/js-evo-sdk/tests/.eslintrc.yml @@ -0,0 +1,25 @@ +extends: + - airbnb-base + - plugin:jsdoc/recommended +env: + es2020: true + node: true + mocha: true +rules: + eol-last: + - error + - always + import/extensions: off + class-methods-use-this: off + import/no-extraneous-dependencies: off + curly: + - error + - all + no-restricted-syntax: + - error + - selector: "LabeledStatement" + message: Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand. + - selector: "WithStatement" + message: "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." +globals: + expect: true diff --git a/packages/js-evo-sdk/tests/bootstrap.cjs b/packages/js-evo-sdk/tests/bootstrap.cjs new file mode 100644 index 00000000000..a51a4dc33f8 --- /dev/null +++ b/packages/js-evo-sdk/tests/bootstrap.cjs @@ -0,0 +1,33 @@ +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinonChai = require('sinon-chai'); +const sinon = require('sinon'); + +chai.use(sinonChai); +chai.use(chaiAsPromised); +chai.use(dirtyChai); + +const { expect } = chai; +const g = (typeof globalThis !== 'undefined') ? globalThis : global; +g.expect = expect; + +exports.mochaHooks = { + beforeEach() { + if (!this.sinon) { + this.sinon = sinon.createSandbox(); + } else { + this.sinon.restore(); + } + }, + + afterEach() { + this.sinon.restore(); + }, +}; + +// In browser (Karma), Mocha won't auto-load Node-style root hooks via exports. +// Register them explicitly when mocha global is available. +if (typeof globalThis !== 'undefined' && globalThis.mocha && typeof globalThis.mocha.setup === 'function') { + globalThis.mocha.setup({ rootHooks: exports.mochaHooks }); +} diff --git a/packages/js-evo-sdk/tests/fixtures/testnet.mjs b/packages/js-evo-sdk/tests/fixtures/testnet.mjs new file mode 100644 index 00000000000..b91b64c81cb --- /dev/null +++ b/packages/js-evo-sdk/tests/fixtures/testnet.mjs @@ -0,0 +1,27 @@ +// Shared testnet fixtures used by functional tests. +// Values are sourced from the wasm-sdk docs generator and api-definitions +// to exercise read-only queries against publicly available testnet data. + +export const TEST_IDS = { + identityId: '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', + specializedBalanceIdentityId: 'AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT', + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + tokenContractId: 'ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A', + groupContractId: '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N', + tokenId: 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv', + documentType: 'domain', + documentId: '7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r', + proTxHash: '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113', + publicKeyHashUnique: 'b7e904ce25ed97594e72f7af0e66f298031c1754', + publicKeyHashNonUnique: '518038dc858461bcee90478fd994bba8057b7531', + username: 'alice', + existingUsername: 'therealslimshaddy5', + epoch: 8635, +}; + +// Optional environment-driven secrets for state-transition tests (skipped by default). +export const TEST_SECRETS = { + identityId: process.env.EVO_IDENTITY_ID, + privateKeyWif: process.env.EVO_PRIVATE_WIF, + keyId: process.env.EVO_KEY_ID ? Number(process.env.EVO_KEY_ID) : undefined, +}; diff --git a/packages/js-evo-sdk/tests/functional/contracts.spec.mjs b/packages/js-evo-sdk/tests/functional/contracts.spec.mjs new file mode 100644 index 00000000000..50a34f580dd --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/contracts.spec.mjs @@ -0,0 +1,35 @@ +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Data Contracts', function dataContractsSuite() { + let sdk; + + this.timeout(60000); + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('fetch() returns data contract', async () => { + const res = await sdk.contracts.fetch(TEST_IDS.dataContractId); + expect(res).to.exist(); + }); + + it('fetchWithProof() returns proof info', async () => { + const res = await sdk.contracts.fetchWithProof(TEST_IDS.dataContractId); + expect(res).to.exist(); + }); + + // TODO: fix dash drive: proof: corrupted error: we did not get back an element + // for the correct path for the historical contract + it.skip('getHistory() returns history for contract', async () => { + const res = await sdk.contracts.getHistory({ contractId: TEST_IDS.tokenContractId, limit: 1 }); + expect(res).to.exist(); + }); + + it('getMany() returns multiple contracts', async () => { + const res = await sdk.contracts.getMany([TEST_IDS.dataContractId]); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/documents.spec.mjs b/packages/js-evo-sdk/tests/functional/documents.spec.mjs new file mode 100644 index 00000000000..4818548de92 --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/documents.spec.mjs @@ -0,0 +1,32 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Documents', function documentsSuite() { + this.timeout(90000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('query() returns documents by type', async () => { + const res = await sdk.documents.query({ + contractId: TEST_IDS.dataContractId, + type: TEST_IDS.documentType, + limit: 5, + orderBy: [['normalizedLabel', 'desc']], + }); + expect(res).to.exist(); + }); + + it('get() returns a single document by id', async () => { + const res = await sdk.documents.get( + TEST_IDS.dataContractId, + TEST_IDS.documentType, + TEST_IDS.documentId, + ); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/dpns.spec.mjs b/packages/js-evo-sdk/tests/functional/dpns.spec.mjs new file mode 100644 index 00000000000..9cfebd3876f --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/dpns.spec.mjs @@ -0,0 +1,33 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('DPNS', function dpnsSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('isNameAvailable() returns a boolean', async () => { + const res = await sdk.dpns.isNameAvailable(`nonexistentname${Math.floor(Math.random() * 1e6)}`); + expect(res).to.be.a('boolean'); + }); + + it('resolveName() resolves known username', async () => { + const res = await sdk.dpns.resolveName(TEST_IDS.existingUsername); + expect(res).to.exist(); + }); + + it('usernames() returns usernames for identity', async () => { + const res = await sdk.dpns.usernames(TEST_IDS.identityId, { limit: 5 }); + expect(res).to.exist(); + }); + + it('username() returns username for identity', async () => { + const res = await sdk.dpns.username(TEST_IDS.identityId); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/epoch.spec.mjs b/packages/js-evo-sdk/tests/functional/epoch.spec.mjs new file mode 100644 index 00000000000..a6cdd106c09 --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/epoch.spec.mjs @@ -0,0 +1,28 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Epoch', function epochSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('current() returns current epoch', async () => { + const res = await sdk.epoch.current(); + expect(res).to.exist(); + }); + + it('epochsInfo() returns info for range', async () => { + const res = await sdk.epoch.epochsInfo({ startEpoch: TEST_IDS.epoch, count: 1 }); + expect(res).to.exist(); + }); + + it('evonodesProposedBlocksByRange() returns results', async () => { + const res = await sdk.epoch.evonodesProposedBlocksByRange(TEST_IDS.epoch, { limit: 5 }); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/group.spec.mjs b/packages/js-evo-sdk/tests/functional/group.spec.mjs new file mode 100644 index 00000000000..1a662eab3c9 --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/group.spec.mjs @@ -0,0 +1,35 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Group', function groupSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('contestedResources() returns contested resources (may be empty)', async () => { + const res = await sdk.group.contestedResources({ + documentTypeName: 'domain', + contractId: TEST_IDS.dataContractId, + indexName: 'parentNameAndLabel', + limit: 5, + }); + expect(res).to.exist(); + }); + + it('contestedResourceVotersForIdentity() returns voters (may be empty)', async () => { + const res = await sdk.group.contestedResourceVotersForIdentity({ + contractId: TEST_IDS.dataContractId, + documentTypeName: 'domain', + indexName: 'parentNameAndLabel', + indexValues: ['dash', TEST_IDS.username], + contestantId: TEST_IDS.identityId, + limit: 5, + }); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/identities.spec.mjs b/packages/js-evo-sdk/tests/functional/identities.spec.mjs new file mode 100644 index 00000000000..32f197cbfce --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/identities.spec.mjs @@ -0,0 +1,47 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS, TEST_SECRETS } from '../fixtures/testnet.mjs'; + +describe('Identities', function identitiesSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('fetch() returns identity', async () => { + const res = await sdk.identities.fetch(TEST_IDS.identityId); + expect(res).to.exist(); + }); + + it('fetchWithProof() returns proof info', async () => { + const res = await sdk.identities.fetchWithProof(TEST_IDS.identityId); + expect(res).to.exist(); + }); + + it('getKeys({ keyRequestType: "all" }) returns keys', async () => { + const res = await sdk.identities.getKeys({ + identityId: TEST_IDS.identityId, + keyRequestType: 'all', + limit: 10, + offset: 0, + }); + expect(res).to.exist(); + }); + + it.skip('creditTransfer() executes when secrets provided (skipped by default)', async function creditTransferExecutesWhenSecretsProvided() { + if (!TEST_SECRETS.identityId || !TEST_SECRETS.privateKeyWif) { + this.skip(); + } + const res = await sdk.identities.creditTransfer({ + senderId: TEST_SECRETS.identityId, + recipientId: TEST_IDS.identityId, + amount: BigInt(1), + privateKeyWif: TEST_SECRETS.privateKeyWif, + keyId: TEST_SECRETS.keyId, + }); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/protocol.spec.mjs b/packages/js-evo-sdk/tests/functional/protocol.spec.mjs new file mode 100644 index 00000000000..75b5570c712 --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/protocol.spec.mjs @@ -0,0 +1,26 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Protocol', function protocolSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('versionUpgradeState() returns state', async () => { + const res = await sdk.protocol.versionUpgradeState(); + expect(res).to.exist(); + }); + + it('versionUpgradeVoteStatus() returns vote window status', async () => { + const res = await sdk.protocol.versionUpgradeVoteStatus({ + startProTxHash: TEST_IDS.proTxHash, + count: 1, + }); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/system.spec.mjs b/packages/js-evo-sdk/tests/functional/system.spec.mjs new file mode 100644 index 00000000000..50c60e99f17 --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/system.spec.mjs @@ -0,0 +1,33 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('System', function systemSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('status() returns basic node status', async () => { + const res = await sdk.system.status(); + expect(res).to.exist(); + }); + + it('currentQuorumsInfo() returns quorum info', async () => { + const res = await sdk.system.currentQuorumsInfo(); + expect(res).to.exist(); + }); + + it('totalCreditsInPlatform() returns value', async () => { + const res = await sdk.system.totalCreditsInPlatform(); + expect(res).to.exist(); + }); + + it('prefundedSpecializedBalance(identityId) returns value', async () => { + const res = await sdk.system.prefundedSpecializedBalance(TEST_IDS.specializedBalanceIdentityId); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/tokens.spec.mjs b/packages/js-evo-sdk/tests/functional/tokens.spec.mjs new file mode 100644 index 00000000000..624aa7b6b0d --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/tokens.spec.mjs @@ -0,0 +1,39 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Tokens', function tokensSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('totalSupply() returns supply for token', async () => { + const res = await sdk.tokens.totalSupply(TEST_IDS.tokenId); + expect(res).to.exist(); + }); + + it('statuses() returns statuses for token(s)', async () => { + const res = await sdk.tokens.statuses([TEST_IDS.tokenId]); + expect(res).to.exist(); + }); + + it('directPurchasePrices() returns prices for token(s)', async () => { + const res = await sdk.tokens.directPurchasePrices([TEST_IDS.tokenId]); + expect(res).to.exist(); + }); + + // TODO: Fix this test + it.skip('contractInfo() returns token contract info', async () => { + const res = await sdk.tokens.contractInfo(TEST_IDS.tokenContractId); + expect(res).to.exist(); + }); + + it('identitiesTokenInfos() returns token infos for identities', async () => { + const res = await sdk.tokens.identitiesTokenInfos([TEST_IDS.identityId], TEST_IDS.tokenId); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/functional/voting.spec.mjs b/packages/js-evo-sdk/tests/functional/voting.spec.mjs new file mode 100644 index 00000000000..1f20c25de04 --- /dev/null +++ b/packages/js-evo-sdk/tests/functional/voting.spec.mjs @@ -0,0 +1,29 @@ +import { expect } from 'chai'; +import { EvoSDK } from '../../dist/sdk.js'; +import { TEST_IDS } from '../fixtures/testnet.mjs'; + +describe('Voting', function votingSuite() { + this.timeout(60000); + let sdk; + + before(async () => { + sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + }); + + it('contestedResourceVoteState() returns a vote state (may be empty)', async () => { + const res = await sdk.voting.contestedResourceVoteState({ + contractId: TEST_IDS.dataContractId, + documentTypeName: 'domain', + indexName: 'parentNameAndLabel', + indexValues: ['dash', TEST_IDS.username], + resultType: 'documents', + }); + expect(res).to.exist(); + }); + + it('contestedResourceIdentityVotes() returns votes for identity (may be empty)', async () => { + const res = await sdk.voting.contestedResourceIdentityVotes(TEST_IDS.identityId, { limit: 5 }); + expect(res).to.exist(); + }); +}); diff --git a/packages/js-evo-sdk/tests/karma/karma.conf.cjs b/packages/js-evo-sdk/tests/karma/karma.conf.cjs new file mode 100644 index 00000000000..98bceeb9526 --- /dev/null +++ b/packages/js-evo-sdk/tests/karma/karma.conf.cjs @@ -0,0 +1,16 @@ +const options = require('./options.cjs'); + +module.exports = (config) => { + config.set({ + ...options, + files: [ + // Load bootstrap first to initialize chai and globals + '../bootstrap.cjs', + '../unit/**/*.spec.mjs', + ], + preprocessors: { + '../bootstrap.cjs': ['webpack'], + '../unit/**/*.spec.mjs': ['webpack'], + }, + }); +}; diff --git a/packages/js-evo-sdk/tests/karma/karma.functional.conf.cjs b/packages/js-evo-sdk/tests/karma/karma.functional.conf.cjs new file mode 100644 index 00000000000..39ac2e46ab8 --- /dev/null +++ b/packages/js-evo-sdk/tests/karma/karma.functional.conf.cjs @@ -0,0 +1,16 @@ +const options = require('./options.cjs'); + +module.exports = (config) => { + config.set({ + ...options, + files: [ + // Load bootstrap first to initialize chai and globals + '../bootstrap.cjs', + '../functional/**/*.spec.mjs', + ], + preprocessors: { + '../bootstrap.cjs': ['webpack'], + '../functional/**/*.spec.mjs': ['webpack'], + }, + }); +}; diff --git a/packages/js-evo-sdk/tests/karma/options.cjs b/packages/js-evo-sdk/tests/karma/options.cjs new file mode 100644 index 00000000000..ed64161c77c --- /dev/null +++ b/packages/js-evo-sdk/tests/karma/options.cjs @@ -0,0 +1,85 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const webpack = require('webpack'); +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaFirefoxLauncher = require('karma-firefox-launcher'); +const karmaWebpack = require('karma-webpack'); + +module.exports = { + frameworks: ['mocha', 'chai', 'webpack'], + webpack: { + mode: 'development', + devtool: 'eval', + module: { + rules: [ + { + test: /\.ts$/, + use: { + loader: 'ts-loader', + options: { + transpileOnly: true, + compilerOptions: { declaration: false, emitDeclarationOnly: false }, + }, + }, + exclude: /node_modules/, + }, + ], + }, + // No special wasm handling needed (WASM is inlined in dist) + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + ], + resolve: { + extensions: ['.mjs', '.js', '.ts'], + extensionAlias: { '.js': ['.ts', '.js'] }, + alias: { + }, + fallback: { + fs: false, + path: require.resolve('path-browserify'), + url: require.resolve('url/'), + util: require.resolve('util/'), + buffer: require.resolve('buffer/'), + events: require.resolve('events/'), + assert: require.resolve('assert/'), + string_decoder: require.resolve('string_decoder/'), + process: require.resolve('process/browser'), + }, + }, + }, + reporters: ['mocha'], + port: 9876, + colors: true, + autoWatch: false, + browsers: ['ChromeHeadlessInsecure'], + singleRun: false, + concurrency: Infinity, + browserNoActivityTimeout: 7 * 60 * 1000, + browserDisconnectTimeout: 3 * 2000, + pingTimeout: 3 * 5000, + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaFirefoxLauncher, + karmaWebpack, + ], + webpackMiddleware: { + stats: 'errors-warnings', + }, + customLaunchers: { + ChromeHeadlessInsecure: { + base: 'ChromeHeadless', + flags: [ + '--allow-insecure-localhost', + ], + displayName: 'Chrome w/o security', + }, + }, +}; diff --git a/packages/js-evo-sdk/tests/unit/facades/contracts.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/contracts.spec.mjs new file mode 100644 index 00000000000..6618dccfb6a --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/contracts.spec.mjs @@ -0,0 +1,82 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('ContractsFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + // instance methods used by ContractsFacade + this.sinon.stub(wasmSdk, 'getDataContract').resolves(true); + this.sinon.stub(wasmSdk, 'getDataContractWithProofInfo').resolves(true); + this.sinon.stub(wasmSdk, 'getDataContractHistory').resolves(true); + this.sinon.stub(wasmSdk, 'getDataContractHistoryWithProofInfo').resolves(true); + this.sinon.stub(wasmSdk, 'getDataContracts').resolves(true); + this.sinon.stub(wasmSdk, 'getDataContractsWithProofInfo').resolves(true); + this.sinon.stub(wasmSdk, 'contractCreate').resolves(true); + this.sinon.stub(wasmSdk, 'contractUpdate').resolves(true); + }); + + it('fetch() forwards to instance getDataContract', async () => { + await client.contracts.fetch('c'); + expect(wasmSdk.getDataContract).to.be.calledOnceWithExactly('c'); + }); + + it('fetchWithProof() forwards to instance getDataContractWithProofInfo', async () => { + await client.contracts.fetchWithProof('c2'); + expect(wasmSdk.getDataContractWithProofInfo).to.be.calledOnceWithExactly('c2'); + }); + + it('getHistory() converts startAtMs to BigInt and forwards', async () => { + await client.contracts.getHistory({ + contractId: 'c', + limit: 3, + startAtMs: 5, + }); + expect(wasmSdk.getDataContractHistory).to.be.calledOnce(); + const { args } = wasmSdk.getDataContractHistory.firstCall; + expect(args[0]).to.equal('c'); + expect(args[1]).to.equal(3); + expect(args[2]).to.equal(null); + expect(typeof args[3]).to.equal('bigint'); + expect(args[3]).to.equal(BigInt(5)); + }); + + it('getHistoryWithProof() forwards similarly', async () => { + await client.contracts.getHistoryWithProof({ contractId: 'c' }); + expect(wasmSdk.getDataContractHistoryWithProofInfo).to.be.calledOnceWithExactly('c', null, null, null); + }); + + it('getMany() and getManyWithProof() forward arrays', async () => { + await client.contracts.getMany(['a', 'b']); + await client.contracts.getManyWithProof(['x']); + expect(wasmSdk.getDataContracts).to.be.calledOnceWithExactly(['a', 'b']); + expect(wasmSdk.getDataContractsWithProofInfo).to.be.calledOnceWithExactly(['x']); + }); + + it('create() calls wasmSdk.contractCreate with JSON', async () => { + await client.contracts.create({ + ownerId: 'o', + definition: { d: 1 }, + privateKeyWif: 'w', + keyId: 2, + }); + expect(wasmSdk.contractCreate).to.be.calledOnceWithExactly('o', JSON.stringify({ d: 1 }), 'w', 2); + }); + + it('update() calls wasmSdk.contractUpdate with JSON', async () => { + await client.contracts.update({ + contractId: 'c', + ownerId: 'o', + updates: { u: true }, + privateKeyWif: 'w', + keyId: 4, + }); + expect(wasmSdk.contractUpdate).to.be.calledOnceWithExactly('c', 'o', JSON.stringify({ u: true }), 'w', 4); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/documents.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/documents.spec.mjs new file mode 100644 index 00000000000..1943f2d00ca --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/documents.spec.mjs @@ -0,0 +1,147 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('DocumentsFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getDocuments').resolves('ok'); + this.sinon.stub(wasmSdk, 'getDocumentsWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getDocument').resolves('ok'); + this.sinon.stub(wasmSdk, 'getDocumentWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'documentCreate').resolves('ok'); + this.sinon.stub(wasmSdk, 'documentReplace').resolves('ok'); + this.sinon.stub(wasmSdk, 'documentDelete').resolves('ok'); + this.sinon.stub(wasmSdk, 'documentTransfer').resolves('ok'); + this.sinon.stub(wasmSdk, 'documentPurchase').resolves('ok'); + this.sinon.stub(wasmSdk, 'documentSetPrice').resolves('ok'); + }); + + it('query() forwards to wasm.getDocuments with JSON and null handling', async () => { + const where = { a: 1 }; + const order = { b: 'asc' }; + await client.documents.query({ + contractId: 'c', + type: 't', + where, + orderBy: order, + limit: 5, + startAfter: 'x', + startAt: 'y', + }); + expect(wasmSdk.getDocuments).to.be.calledOnceWithExactly('c', 't', JSON.stringify(where), JSON.stringify(order), 5, 'x', 'y'); + }); + + it('queryWithProof() forwards to wasm.getDocumentsWithProofInfo', async () => { + await client.documents.queryWithProof({ contractId: 'c', type: 't' }); + expect(wasmSdk.getDocumentsWithProofInfo).to.be.calledOnceWithExactly('c', 't', null, null, null, null, null); + }); + + it('get() forwards to wasm.getDocument', async () => { + await client.documents.get('c', 't', 'id'); + expect(wasmSdk.getDocument).to.be.calledOnceWithExactly('c', 't', 'id'); + }); + + it('getWithProof() forwards to wasm.getDocumentWithProofInfo', async () => { + await client.documents.getWithProof('c', 't', 'id'); + expect(wasmSdk.getDocumentWithProofInfo).to.be.calledOnceWithExactly('c', 't', 'id'); + }); + + it('create() calls wasmSdk.documentCreate with JSON data', async () => { + const data = { foo: 'bar' }; + await client.documents.create({ + contractId: 'c', + type: 't', + ownerId: 'o', + data, + entropyHex: 'ee', + privateKeyWif: 'wif', + }); + expect(wasmSdk.documentCreate).to.be.calledOnceWithExactly('c', 't', 'o', JSON.stringify(data), 'ee', 'wif'); + }); + + it('replace() calls wasmSdk.documentReplace with BigInt revision', async () => { + await client.documents.replace({ + contractId: 'c', + type: 't', + documentId: 'id', + ownerId: 'o', + data: { n: 1 }, + revision: 2, + privateKeyWif: 'w', + }); + expect(wasmSdk.documentReplace).to.be.calledOnce(); + const [c, t, id, o, json, rev, w] = wasmSdk.documentReplace.firstCall.args; + expect([c, t, id, o, w]).to.deep.equal(['c', 't', 'id', 'o', 'w']); + expect(json).to.equal(JSON.stringify({ n: 1 })); + expect(typeof rev).to.equal('bigint'); + expect(rev).to.equal(BigInt(2)); + }); + + it('delete() calls wasmSdk.documentDelete', async () => { + await client.documents.delete({ + contractId: 'c', + type: 't', + documentId: 'id', + ownerId: 'o', + privateKeyWif: 'w', + }); + expect(wasmSdk.documentDelete).to.be.calledOnceWithExactly('c', 't', 'id', 'o', 'w'); + }); + + it('transfer() calls wasmSdk.documentTransfer', async () => { + await client.documents.transfer({ + contractId: 'c', + type: 't', + documentId: 'id', + ownerId: 'o', + recipientId: 'r', + privateKeyWif: 'w', + }); + expect(wasmSdk.documentTransfer).to.be.calledOnceWithExactly('c', 't', 'id', 'o', 'r', 'w'); + }); + + it('purchase() calls wasmSdk.documentPurchase with BigInt amount', async () => { + await client.documents.purchase({ + contractId: 'c', + type: 't', + documentId: 'id', + buyerId: 'b', + price: '7', + privateKeyWif: 'w', + }); + const { args } = wasmSdk.documentPurchase.firstCall; + expect(args[0]).to.equal('c'); + expect(args[1]).to.equal('t'); + expect(args[2]).to.equal('id'); + expect(args[3]).to.equal('b'); + expect(typeof args[4]).to.equal('bigint'); + expect(args[4]).to.equal(BigInt(7)); + expect(args[5]).to.equal('w'); + }); + + it('setPrice() calls wasmSdk.documentSetPrice with BigInt price', async () => { + await client.documents.setPrice({ + contractId: 'c', + type: 't', + documentId: 'id', + ownerId: 'o', + price: 9, + privateKeyWif: 'w', + }); + const { args } = wasmSdk.documentSetPrice.firstCall; + expect(args[0]).to.equal('c'); + expect(args[1]).to.equal('t'); + expect(args[2]).to.equal('id'); + expect(args[3]).to.equal('o'); + expect(typeof args[4]).to.equal('bigint'); + expect(args[4]).to.equal(BigInt(9)); + expect(args[5]).to.equal('w'); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/dpns.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/dpns.spec.mjs new file mode 100644 index 00000000000..4b375bdca1a --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/dpns.spec.mjs @@ -0,0 +1,57 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('DPNSFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'dpnsIsNameAvailable').resolves(true); + this.sinon.stub(wasmSdk, 'dpnsResolveName').resolves({}); + this.sinon.stub(wasmSdk, 'dpnsRegisterName').resolves({}); + this.sinon.stub(wasmSdk, 'getDpnsUsernames').resolves([]); + this.sinon.stub(wasmSdk, 'getDpnsUsername').resolves({}); + this.sinon.stub(wasmSdk, 'getDpnsUsernamesWithProofInfo').resolves({}); + this.sinon.stub(wasmSdk, 'getDpnsUsernameWithProofInfo').resolves({}); + this.sinon.stub(wasmSdk, 'getDpnsUsernameByName').resolves({}); + this.sinon.stub(wasmSdk, 'getDpnsUsernameByNameWithProofInfo').resolves({}); + }); + + it('convertToHomographSafe/isValidUsername/isContestedUsername use class statics', () => { + const out1 = wasmSDKPackage.WasmSdk.dpnsConvertToHomographSafe('abc'); + const out2 = wasmSDKPackage.WasmSdk.dpnsIsValidUsername('abc'); + const out3 = wasmSDKPackage.WasmSdk.dpnsIsContestedUsername('abc'); + expect(out1).to.be.ok(); + expect(typeof out2).to.not.equal('undefined'); + expect(typeof out3).to.not.equal('undefined'); + }); + + it('name resolution and registration forward correctly', async () => { + await client.dpns.isNameAvailable('label'); + await client.dpns.resolveName('name'); + await client.dpns.registerName({ + label: 'l', identityId: 'i', publicKeyId: 1, privateKeyWif: 'w', + }); + await client.dpns.usernames('i', { limit: 2 }); + await client.dpns.username('i'); + await client.dpns.usernamesWithProof('i', { limit: 3 }); + await client.dpns.usernameWithProof('i'); + await client.dpns.getUsernameByName('u'); + await client.dpns.getUsernameByNameWithProof('u'); + + expect(wasmSdk.dpnsIsNameAvailable).to.be.calledOnceWithExactly('label'); + expect(wasmSdk.dpnsResolveName).to.be.calledOnceWithExactly('name'); + expect(wasmSdk.dpnsRegisterName).to.be.calledOnce(); + expect(wasmSdk.getDpnsUsernames).to.be.calledOnceWithExactly('i', 2); + expect(wasmSdk.getDpnsUsername).to.be.calledOnceWithExactly('i'); + expect(wasmSdk.getDpnsUsernamesWithProofInfo).to.be.calledOnceWithExactly('i', 3); + expect(wasmSdk.getDpnsUsernameWithProofInfo).to.be.calledOnceWithExactly('i'); + expect(wasmSdk.getDpnsUsernameByName).to.be.calledOnceWithExactly('u'); + expect(wasmSdk.getDpnsUsernameByNameWithProofInfo).to.be.calledOnceWithExactly('u'); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/epoch.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/epoch.spec.mjs new file mode 100644 index 00000000000..6536c64ed86 --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/epoch.spec.mjs @@ -0,0 +1,55 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('EpochFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getEpochsInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getEpochsInfoWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getFinalizedEpochInfos').resolves('ok'); + this.sinon.stub(wasmSdk, 'getFinalizedEpochInfosWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getCurrentEpoch').resolves('ok'); + this.sinon.stub(wasmSdk, 'getCurrentEpochWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getEvonodesProposedEpochBlocksByIds').resolves('ok'); + this.sinon.stub(wasmSdk, 'getEvonodesProposedEpochBlocksByIdsWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getEvonodesProposedEpochBlocksByRange').resolves('ok'); + this.sinon.stub(wasmSdk, 'getEvonodesProposedEpochBlocksByRangeWithProofInfo').resolves('ok'); + }); + + it('epochsInfo and finalizedInfos forward with null handling', async () => { + await client.epoch.epochsInfo({ startEpoch: 1, count: 2, ascending: true }); + await client.epoch.epochsInfoWithProof({}); + await client.epoch.finalizedInfos({ startEpoch: 3 }); + await client.epoch.finalizedInfosWithProof({ count: 4 }); + expect(wasmSdk.getEpochsInfo).to.be.calledOnceWithExactly(1, 2, true); + expect(wasmSdk.getEpochsInfoWithProofInfo).to.be.calledOnceWithExactly(null, null, null); + expect(wasmSdk.getFinalizedEpochInfos).to.be.calledOnceWithExactly(3, null, null); + expect(wasmSdk.getFinalizedEpochInfosWithProofInfo).to.be.calledOnceWithExactly(null, 4, null); + }); + + it('current and currentWithProof forward', async () => { + await client.epoch.current(); + await client.epoch.currentWithProof(); + expect(wasmSdk.getCurrentEpoch).to.be.calledOnce(); + expect(wasmSdk.getCurrentEpochWithProofInfo).to.be.calledOnce(); + }); + + it('evonodesProposedBlocks* forward with args', async () => { + await client.epoch.evonodesProposedBlocksByIds(10, ['a', 'b']); + await client.epoch.evonodesProposedBlocksByIdsWithProof(11, ['x']); + await client.epoch.evonodesProposedBlocksByRange(12, { limit: 2, startAfter: 's', orderAscending: false }); + await client.epoch.evonodesProposedBlocksByRangeWithProof(13, {}); + expect(wasmSdk.getEvonodesProposedEpochBlocksByIds).to.be.calledOnceWithExactly(10, ['a', 'b']); + expect(wasmSdk.getEvonodesProposedEpochBlocksByIdsWithProofInfo).to.be.calledOnceWithExactly(11, ['x']); + expect(wasmSdk.getEvonodesProposedEpochBlocksByRange).to.be.calledOnceWithExactly(12, 2, 's', false); + expect(wasmSdk.getEvonodesProposedEpochBlocksByRangeWithProofInfo) + .to.be.calledOnceWithExactly(13, null, null, null); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/group.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/group.spec.mjs new file mode 100644 index 00000000000..0573cd4ec6f --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/group.spec.mjs @@ -0,0 +1,36 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('GroupFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getContestedResources').resolves('ok'); + this.sinon.stub(wasmSdk, 'getContestedResourcesWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getContestedResourceVotersForIdentity').resolves('ok'); + this.sinon.stub(wasmSdk, 'getContestedResourceVotersForIdentityWithProofInfo').resolves('ok'); + }); + + it('forwards contestedResources and voters queries', async () => { + await client.group.contestedResources({ + documentTypeName: 'dt', contractId: 'c', indexName: 'i', startAtValue: new Uint8Array([1]), limit: 2, orderAscending: false, + }); + await client.group.contestedResourcesWithProof({ documentTypeName: 'dt', contractId: 'c', indexName: 'i' }); + await client.group.contestedResourceVotersForIdentity({ + contractId: 'c', documentTypeName: 'dt', indexName: 'i', indexValues: ['v1'], contestantId: 'id', startAtVoterInfo: 's', limit: 3, orderAscending: true, + }); + await client.group.contestedResourceVotersForIdentityWithProof({ + contractId: 'c', documentTypeName: 'dt', indexName: 'i', indexValues: ['v2'], contestantId: 'id', + }); + expect(wasmSdk.getContestedResources).to.be.calledOnce(); + expect(wasmSdk.getContestedResourcesWithProofInfo).to.be.calledOnce(); + expect(wasmSdk.getContestedResourceVotersForIdentity).to.be.calledOnce(); + expect(wasmSdk.getContestedResourceVotersForIdentityWithProofInfo).to.be.calledOnce(); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/identities.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/identities.spec.mjs new file mode 100644 index 00000000000..b934d83f237 --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/identities.spec.mjs @@ -0,0 +1,126 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('IdentitiesFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getIdentity').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentityWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentityUnproved').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentityKeys').resolves('ok'); + this.sinon.stub(wasmSdk, 'identityCreate').resolves('ok'); + this.sinon.stub(wasmSdk, 'identityTopUp').resolves('ok'); + this.sinon.stub(wasmSdk, 'identityCreditTransfer').resolves('ok'); + this.sinon.stub(wasmSdk, 'identityCreditWithdrawal').resolves('ok'); + this.sinon.stub(wasmSdk, 'identityUpdate').resolves('ok'); + }); + + it('fetch() and fetchWithProof() forward to instance methods', async () => { + await client.identities.fetch('id'); + await client.identities.fetchWithProof('id2'); + expect(wasmSdk.getIdentity).to.be.calledOnceWithExactly('id'); + expect(wasmSdk.getIdentityWithProofInfo).to.be.calledOnceWithExactly('id2'); + }); + + it('fetchUnproved() forwards to getIdentityUnproved', async () => { + await client.identities.fetchUnproved('id'); + expect(wasmSdk.getIdentityUnproved).to.be.calledOnceWithExactly('id'); + }); + + it('getKeys() forwards with Uint32Array and JSON mapping', async () => { + await client.identities.getKeys({ + identityId: 'id', + keyRequestType: 'specific', + specificKeyIds: [1, 2], + searchPurposeMap: { a: 1 }, + limit: 10, + offset: 2, + }); + const { args } = wasmSdk.getIdentityKeys.firstCall; + expect(args[0]).to.equal('id'); + expect(args[1]).to.equal('specific'); + expect(args[2]).to.be.instanceOf(Uint32Array); + expect(Array.from(args[2])).to.deep.equal([1, 2]); + expect(args[3]).to.equal(JSON.stringify({ a: 1 })); + expect(args[4]).to.equal(10); + expect(args[5]).to.equal(2); + }); + + it('create() calls wasmSdk.identityCreate with JSON proof and keys', async () => { + await client.identities.create({ + assetLockProof: { p: true }, + assetLockPrivateKeyWif: 'w', + publicKeys: [{ k: 1 }], + }); + expect(wasmSdk.identityCreate).to.be.calledOnce(); + const [proofJson, wif, keysJson] = wasmSdk.identityCreate.firstCall.args; + expect(proofJson).to.equal(JSON.stringify({ p: true })); + expect(wif).to.equal('w'); + expect(keysJson).to.equal(JSON.stringify([{ k: 1 }])); + }); + + it('topUp() calls wasmSdk.identityTopUp with JSON proof', async () => { + await client.identities.topUp({ + identityId: 'id', + assetLockProof: { p: 1 }, + assetLockPrivateKeyWif: 'w', + }); + expect(wasmSdk.identityTopUp).to.be.calledOnceWithExactly('id', JSON.stringify({ p: 1 }), 'w'); + }); + + it('creditTransfer() converts amount to BigInt', async () => { + await client.identities.creditTransfer({ + senderId: 's', + recipientId: 'r', + amount: '5', + privateKeyWif: 'w', + keyId: 3, + }); + const { args } = wasmSdk.identityCreditTransfer.firstCall; + expect(args[0]).to.equal('s'); + expect(args[1]).to.equal('r'); + expect(typeof args[2]).to.equal('bigint'); + expect(args[2]).to.equal(BigInt(5)); + expect(args.slice(3)).to.deep.equal(['w', 3]); + }); + + it('creditWithdrawal() converts amount to BigInt and passes coreFeePerByte', async () => { + await client.identities.creditWithdrawal({ + identityId: 'i', + toAddress: 'addr', + amount: 7, + coreFeePerByte: 2, + privateKeyWif: 'w', + keyId: 4, + }); + const { args } = wasmSdk.identityCreditWithdrawal.firstCall; + expect(args[0]).to.equal('i'); + expect(args[1]).to.equal('addr'); + expect(args[2]).to.equal(BigInt(7)); + expect(args[3]).to.equal(2); + expect(args[4]).to.equal('w'); + expect(args[5]).to.equal(4); + }); + + it('update() passes JSON for keys and Uint32Array for disabled key ids', async () => { + await client.identities.update({ + identityId: 'i', + addPublicKeys: [{ k: 1 }], + disablePublicKeyIds: [10, 20], + privateKeyWif: 'w', + }); + const { args } = wasmSdk.identityUpdate.firstCall; + expect(args[0]).to.equal('i'); + expect(args[1]).to.equal(JSON.stringify([{ k: 1 }])); + expect(args[2]).to.be.instanceOf(Uint32Array); + expect(Array.from(args[2])).to.deep.equal([10, 20]); + expect(args[3]).to.equal('w'); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/protocol.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/protocol.spec.mjs new file mode 100644 index 00000000000..8c3eebd1938 --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/protocol.spec.mjs @@ -0,0 +1,33 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('ProtocolFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getProtocolVersionUpgradeState').resolves('ok'); + this.sinon.stub(wasmSdk, 'getProtocolVersionUpgradeStateWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getProtocolVersionUpgradeVoteStatus').resolves('ok'); + this.sinon.stub(wasmSdk, 'getProtocolVersionUpgradeVoteStatusWithProofInfo').resolves('ok'); + }); + + it('versionUpgradeState and versionUpgradeStateWithProof forward', async () => { + await client.protocol.versionUpgradeState(); + await client.protocol.versionUpgradeStateWithProof(); + expect(wasmSdk.getProtocolVersionUpgradeState).to.be.calledOnce(); + expect(wasmSdk.getProtocolVersionUpgradeStateWithProofInfo).to.be.calledOnce(); + }); + + it('versionUpgradeVoteStatus and withProof forward with args', async () => { + await client.protocol.versionUpgradeVoteStatus({ startProTxHash: 'h', count: 5 }); + await client.protocol.versionUpgradeVoteStatusWithProof({ startProTxHash: 'g', count: 3 }); + expect(wasmSdk.getProtocolVersionUpgradeVoteStatus).to.be.calledOnceWithExactly('h', 5); + expect(wasmSdk.getProtocolVersionUpgradeVoteStatusWithProofInfo).to.be.calledOnceWithExactly('g', 3); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/system.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/system.spec.mjs new file mode 100644 index 00000000000..3b5c3e83431 --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/system.spec.mjs @@ -0,0 +1,45 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('SystemFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getStatus').resolves('ok'); + this.sinon.stub(wasmSdk, 'getCurrentQuorumsInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTotalCreditsInPlatform').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTotalCreditsInPlatformWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getPrefundedSpecializedBalance').resolves('ok'); + this.sinon.stub(wasmSdk, 'getPrefundedSpecializedBalanceWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'waitForStateTransitionResult').resolves('ok'); + this.sinon.stub(wasmSdk, 'getPathElements').resolves('ok'); + this.sinon.stub(wasmSdk, 'getPathElementsWithProofInfo').resolves('ok'); + }); + + it('forwards all methods to instance methods', async () => { + await client.system.status(); + await client.system.currentQuorumsInfo(); + await client.system.totalCreditsInPlatform(); + await client.system.totalCreditsInPlatformWithProof(); + await client.system.prefundedSpecializedBalance('i'); + await client.system.prefundedSpecializedBalanceWithProof('i'); + await client.system.waitForStateTransitionResult('h'); + await client.system.pathElements(['p'], ['k']); + await client.system.pathElementsWithProof(['p2'], ['k2']); + expect(wasmSdk.getStatus).to.be.calledOnce(); + expect(wasmSdk.getCurrentQuorumsInfo).to.be.calledOnce(); + expect(wasmSdk.getTotalCreditsInPlatform).to.be.calledOnce(); + expect(wasmSdk.getTotalCreditsInPlatformWithProofInfo).to.be.calledOnce(); + expect(wasmSdk.getPrefundedSpecializedBalance).to.be.calledOnce(); + expect(wasmSdk.getPrefundedSpecializedBalanceWithProofInfo).to.be.calledOnce(); + expect(wasmSdk.waitForStateTransitionResult).to.be.calledOnce(); + expect(wasmSdk.getPathElements).to.be.calledOnce(); + expect(wasmSdk.getPathElementsWithProofInfo).to.be.calledOnce(); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/tokens.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/tokens.spec.mjs new file mode 100644 index 00000000000..438009b70ad --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/tokens.spec.mjs @@ -0,0 +1,180 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('TokensFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + // query methods + this.sinon.stub(wasmSdk, 'getTokenPriceByContract').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenTotalSupply').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenTotalSupplyWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenStatuses').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenStatusesWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentitiesTokenBalances').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentitiesTokenBalancesWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentityTokenInfos').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentitiesTokenInfos').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentityTokenInfosWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getIdentitiesTokenInfosWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenDirectPurchasePrices').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenDirectPurchasePricesWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenContractInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenContractInfoWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenPerpetualDistributionLastClaim').resolves('ok'); + this.sinon.stub(wasmSdk, 'getTokenPerpetualDistributionLastClaimWithProofInfo').resolves('ok'); + + // tx methods + this.sinon.stub(wasmSdk, 'tokenMint').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenBurn').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenTransfer').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenFreeze').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenUnfreeze').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenDestroyFrozen').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenSetPriceForDirectPurchase').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenDirectPurchase').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenClaim').resolves('ok'); + this.sinon.stub(wasmSdk, 'tokenConfigUpdate').resolves('ok'); + }); + + it('query functions forward to instance methods', async () => { + await client.tokens.priceByContract('c', 1); + await client.tokens.totalSupply('t'); + await client.tokens.totalSupplyWithProof('t'); + await client.tokens.statuses(['a', 'b']); + await client.tokens.statusesWithProof(['a']); + await client.tokens.balances(['i1', 'i2'], 't'); + await client.tokens.balancesWithProof(['i'], 't'); + await client.tokens.identityTokenInfos('id', ['t1', 't2']); + await client.tokens.identitiesTokenInfos(['i1'], 't'); + await client.tokens.identityTokenInfosWithProof('id', ['t']); + await client.tokens.identitiesTokenInfosWithProof(['i'], 't'); + await client.tokens.directPurchasePrices(['t']); + await client.tokens.directPurchasePricesWithProof(['t']); + await client.tokens.contractInfo('c'); + await client.tokens.contractInfoWithProof('c'); + await client.tokens.perpetualDistributionLastClaim('i', 't'); + await client.tokens.perpetualDistributionLastClaimWithProof('i', 't'); + expect(wasmSdk.getTokenPriceByContract).to.be.calledOnceWithExactly('c', 1); + expect(wasmSdk.getTokenTotalSupply).to.be.calledOnceWithExactly('t'); + expect(wasmSdk.getTokenTotalSupplyWithProofInfo).to.be.calledOnceWithExactly('t'); + }); + + it('mint() stringifies amount and passes nullables', async () => { + await client.tokens.mint({ + contractId: 'c', + tokenPosition: 1, + amount: BigInt(3), + identityId: 'i', + privateKeyWif: 'w', + recipientId: 'r', + publicNote: 'n', + }); + expect(wasmSdk.tokenMint).to.be.calledOnceWithExactly('c', 1, '3', 'i', 'w', 'r', 'n'); + }); + + it('burn() stringifies amount', async () => { + await client.tokens.burn({ + contractId: 'c', + tokenPosition: 1, + amount: 5, + identityId: 'i', + privateKeyWif: 'w', + }); + expect(wasmSdk.tokenBurn).to.be.calledOnceWithExactly('c', 1, '5', 'i', 'w', null); + }); + + it('transfer() stringifies amount and forwards', async () => { + await client.tokens.transfer({ + contractId: 'c', + tokenPosition: 2, + amount: '7', + senderId: 's', + recipientId: 'r', + privateKeyWif: 'w', + publicNote: 'n', + }); + expect(wasmSdk.tokenTransfer).to.be.calledOnceWithExactly('c', 2, '7', 's', 'r', 'w', 'n'); + }); + + it('freeze()/unfreeze()/destroyFrozen() pass through args', async () => { + await client.tokens.freeze({ + contractId: 'c', + tokenPosition: 1, + identityToFreeze: 'i', + freezerId: 'f', + privateKeyWif: 'w', + publicNote: 'n', + }); + await client.tokens.unfreeze({ + contractId: 'c', + tokenPosition: 1, + identityToUnfreeze: 'i', + unfreezerId: 'u', + privateKeyWif: 'w', + publicNote: 'n2', + }); + await client.tokens.destroyFrozen({ + contractId: 'c', + tokenPosition: 1, + identityId: 'i', + destroyerId: 'd', + privateKeyWif: 'w', + }); + expect(wasmSdk.tokenFreeze).to.be.calledOnceWithExactly('c', 1, 'i', 'f', 'w', 'n'); + expect(wasmSdk.tokenUnfreeze).to.be.calledOnceWithExactly('c', 1, 'i', 'u', 'w', 'n2'); + expect(wasmSdk.tokenDestroyFrozen).to.be.calledOnceWithExactly('c', 1, 'i', 'd', 'w', null); + }); + + it('setPriceForDirectPurchase() JSON stringifies priceData', async () => { + await client.tokens.setPriceForDirectPurchase({ + contractId: 'c', + tokenPosition: 1, + identityId: 'i', + priceType: 't', + priceData: { p: 1 }, + privateKeyWif: 'w', + publicNote: 'n', + }); + expect(wasmSdk.tokenSetPriceForDirectPurchase).to.be.calledOnceWithExactly('c', 1, 'i', 't', JSON.stringify({ p: 1 }), 'w', 'n'); + }); + + it('directPurchase() stringifies amount and totalAgreedPrice', async () => { + await client.tokens.directPurchase({ + contractId: 'c', + tokenPosition: 1, + amount: 2, + identityId: 'i', + totalAgreedPrice: 10, + privateKeyWif: 'w', + }); + expect(wasmSdk.tokenDirectPurchase).to.be.calledOnceWithExactly('c', 1, '2', 'i', '10', 'w'); + }); + + it('claim() and configUpdate() forward with JSON where needed', async () => { + await client.tokens.claim({ + contractId: 'c', + tokenPosition: 1, + distributionType: 'd', + identityId: 'i', + privateKeyWif: 'w', + publicNote: 'n', + }); + await client.tokens.configUpdate({ + contractId: 'c', + tokenPosition: 1, + configItemType: 't', + configValue: { v: true }, + identityId: 'i', + privateKeyWif: 'w', + }); + expect(wasmSdk.tokenClaim).to.be.calledOnceWithExactly('c', 1, 'd', 'i', 'w', 'n'); + expect(wasmSdk.tokenConfigUpdate).to.be.calledOnceWithExactly('c', 1, 't', JSON.stringify({ v: true }), 'i', 'w', null); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/facades/voting.spec.mjs b/packages/js-evo-sdk/tests/unit/facades/voting.spec.mjs new file mode 100644 index 00000000000..e0d7af74e55 --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/facades/voting.spec.mjs @@ -0,0 +1,53 @@ +import init, * as wasmSDKPackage from '@dashevo/wasm-sdk'; +import { EvoSDK } from '../../../dist/sdk.js'; + +describe('VotingFacade', () => { + let wasmSdk; + let client; + + beforeEach(async function setup() { + await init(); + const builder = wasmSDKPackage.WasmSdkBuilder.testnetTrusted(); + wasmSdk = builder.build(); + client = EvoSDK.fromWasm(wasmSdk); + + this.sinon.stub(wasmSdk, 'getContestedResourceVoteState').resolves('ok'); + this.sinon.stub(wasmSdk, 'getContestedResourceVoteStateWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getContestedResourceIdentityVotes').resolves('ok'); + this.sinon.stub(wasmSdk, 'getContestedResourceIdentityVotesWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'getVotePollsByEndDate').resolves('ok'); + this.sinon.stub(wasmSdk, 'getVotePollsByEndDateWithProofInfo').resolves('ok'); + this.sinon.stub(wasmSdk, 'masternodeVote').resolves('ok'); + }); + + it('contestedResourceVoteState and related queries forward correctly', async () => { + await client.voting.contestedResourceVoteState({ + contractId: 'c', documentTypeName: 'dt', indexName: 'i', indexValues: ['v1'], resultType: 'rt', + }); + await client.voting.contestedResourceVoteStateWithProof({ + contractId: 'c', documentTypeName: 'dt', indexName: 'i', indexValues: ['v1'], resultType: 'rt', allowIncludeLockedAndAbstainingVoteTally: true, startAtIdentifierInfo: 's', count: 2, orderAscending: false, + }); + await client.voting.contestedResourceIdentityVotes('id', { limit: 3, startAtVotePollIdInfo: 's', orderAscending: true }); + await client.voting.contestedResourceIdentityVotesWithProof('id', { limit: 4, offset: 1, orderAscending: false }); + await client.voting.votePollsByEndDate({ + startTimeInfo: 'a', endTimeInfo: 'b', limit: 2, orderAscending: true, + }); + await client.voting.votePollsByEndDateWithProof({ + startTimeMs: 10, endTimeMs: 20, limit: 1, offset: 0, orderAscending: false, + }); + expect(wasmSdk.getContestedResourceVoteState).to.be.calledOnce(); + expect(wasmSdk.getContestedResourceVoteStateWithProofInfo).to.be.calledOnce(); + expect(wasmSdk.getContestedResourceIdentityVotes).to.be.calledOnce(); + expect(wasmSdk.getContestedResourceIdentityVotesWithProofInfo).to.be.calledOnce(); + expect(wasmSdk.getVotePollsByEndDate).to.be.calledOnce(); + expect(wasmSdk.getVotePollsByEndDateWithProofInfo).to.be.calledOnce(); + }); + + it('masternodeVote() stringifies array indexValues and forwards', async () => { + await client.voting.masternodeVote({ + masternodeProTxHash: 'h', contractId: 'c', documentTypeName: 'dt', indexName: 'i', indexValues: ['x', 'y'], voteChoice: 'yes', votingKeyWif: 'w', + }); + const { args } = wasmSdk.masternodeVote.firstCall; + expect(args).to.deep.equal(['h', 'c', 'dt', 'i', JSON.stringify(['x', 'y']), 'yes', 'w']); + }); +}); diff --git a/packages/js-evo-sdk/tests/unit/sdk.spec.mjs b/packages/js-evo-sdk/tests/unit/sdk.spec.mjs new file mode 100644 index 00000000000..95ad7887530 --- /dev/null +++ b/packages/js-evo-sdk/tests/unit/sdk.spec.mjs @@ -0,0 +1,20 @@ +import { EvoSDK } from '../../dist/sdk.js'; + +describe('EvoSDK', () => { + it('exposes constructor and factories', () => { + expect(EvoSDK).to.be.a('function'); + expect(EvoSDK.testnet).to.be.a('function'); + expect(EvoSDK.mainnet).to.be.a('function'); + expect(EvoSDK.testnetTrusted).to.be.a('function'); + expect(EvoSDK.mainnetTrusted).to.be.a('function'); + }); + + it('connects and sets isConnected', async function connectsAndSetsIsConnected() { + this.timeout(90000); + + const sdk = new EvoSDK(); + expect(sdk.isConnected).to.equal(false); + await sdk.connect(); + expect(sdk.isConnected).to.equal(true); + }); +}); diff --git a/packages/js-evo-sdk/tsconfig.json b/packages/js-evo-sdk/tsconfig.json new file mode 100644 index 00000000000..77478f516b9 --- /dev/null +++ b/packages/js-evo-sdk/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "esnext", + "moduleResolution": "node", + "declaration": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "./dist", + "rootDir": "./src", + "resolveJsonModule": true, + "baseUrl": ".", + "paths": { + "@dashevo/wasm-sdk": ["../wasm-sdk/dist/sdk.d.ts"], + "@dashevo/wasm-sdk/*": ["../wasm-sdk/dist/*"] + } + }, + "include": ["./src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/js-evo-sdk/webpack.config.cjs b/packages/js-evo-sdk/webpack.config.cjs new file mode 100644 index 00000000000..3b32e2fdf7c --- /dev/null +++ b/packages/js-evo-sdk/webpack.config.cjs @@ -0,0 +1,37 @@ +const path = require('path'); +const TerserPlugin = require('terser-webpack-plugin'); + +const base = { + entry: path.resolve(__dirname, 'src/sdk.ts'), + mode: 'production', + target: ['web', 'es2020'], + devtool: 'source-map', + module: { + parser: { javascript: { url: false } }, + rules: [ + { test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ }, + ], + }, + resolve: { + extensions: ['.ts', '.js', '.json'], + extensionAlias: { '.js': ['.ts', '.js'] }, + }, + optimization: { + splitChunks: false, + runtimeChunk: false, + minimize: true, + minimizer: [new TerserPlugin({ terserOptions: { keep_classnames: true } })], + }, +}; + +const esm = { + ...base, + experiments: { outputModule: true }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'sdk.js', + library: { type: 'module' }, + module: true, + }, +}; +module.exports = esm; diff --git a/packages/scripts/build-wasm.sh b/packages/scripts/build-wasm.sh index aec9e25846b..26cc313e131 100755 --- a/packages/scripts/build-wasm.sh +++ b/packages/scripts/build-wasm.sh @@ -96,17 +96,18 @@ cd "$PACKAGE_DIR" echo "Building $PACKAGE_NAME..." -# Create pkg directory if it doesn't exist +# Create a new clean directory package directory +rm -rf pkg mkdir -p pkg if [ "$USE_WASM_PACK" = true ]; then # Build using wasm-pack echo "Building with wasm-pack..." - + # Disable LTO for wasm-pack builds to avoid conflicts export CARGO_PROFILE_RELEASE_LTO=false export RUSTFLAGS="-C lto=off" - + # Add features if specified FEATURES_ARG="" if [ -n "${CARGO_BUILD_FEATURES:-}" ]; then @@ -117,19 +118,19 @@ if [ "$USE_WASM_PACK" = true ]; then # Explicitly pass default features to ensure they're used FEATURES_ARG="--features default" fi - + echo "Running: wasm-pack build --target $TARGET_TYPE --release --no-opt $FEATURES_ARG" wasm-pack build --target "$TARGET_TYPE" --release --no-opt $FEATURES_ARG else # Build using cargo directly echo "Building with cargo..." - + # Add features if specified FEATURES_ARG="" if [ -n "${CARGO_BUILD_FEATURES:-}" ]; then FEATURES_ARG="--features $CARGO_BUILD_FEATURES" fi - + cargo build --target wasm32-unknown-unknown --release $FEATURES_ARG \ --config 'profile.release.panic="abort"' \ --config 'profile.release.strip=true' \ @@ -138,7 +139,7 @@ else --config 'profile.release.lto=true' \ --config 'profile.release.opt-level="z"' \ --config 'profile.release.codegen-units=1' - + # Run wasm-snip if available if command -v wasm-snip &> /dev/null; then wasm-snip "../../target/wasm32-unknown-unknown/release/${PACKAGE_NAME//-/_}.wasm" \ @@ -146,14 +147,14 @@ else --snip-rust-fmt-code \ --snip-rust-panicking-code fi - + # Run wasm-bindgen echo "Running wasm-bindgen..." if ! command -v wasm-bindgen &> /dev/null; then echo "Error: 'wasm-bindgen' not found. Install via 'cargo install wasm-bindgen-cli'." >&2 exit 1 fi - + wasm-bindgen \ --typescript \ --out-dir=pkg \ @@ -165,13 +166,13 @@ fi # Optimize the WASM file if [ "$OPT_LEVEL" != "none" ] && command -v wasm-opt &> /dev/null; then echo "Optimizing wasm using Binaryen (level: $OPT_LEVEL)..." - + WASM_PATH="pkg/$WASM_FILE" - + if [ "$OPT_LEVEL" = "full" ]; then # Check wasm-opt version to determine available options WASM_OPT_VERSION=$(wasm-opt --version 2>/dev/null || echo "") - + # Core optimization flags that should work with most versions CORE_FLAGS=( --strip-producers @@ -191,7 +192,7 @@ if [ "$OPT_LEVEL" != "none" ] && command -v wasm-opt &> /dev/null; then -Oz -Oz ) - + # Additional flags to test for compatibility OPTIONAL_FLAGS=( "--code-folding" @@ -209,7 +210,7 @@ if [ "$OPT_LEVEL" != "none" ] && command -v wasm-opt &> /dev/null; then "--generate-global-effects" "--abstract-type-refining" ) - + # Test which optional flags are supported SUPPORTED_FLAGS=() for flag in "${OPTIONAL_FLAGS[@]}"; do @@ -219,7 +220,7 @@ if [ "$OPT_LEVEL" != "none" ] && command -v wasm-opt &> /dev/null; then echo "Note: $flag not supported by this wasm-opt version, skipping..." fi done - + # Run optimization with core flags and any supported optional flags wasm-opt \ "${CORE_FLAGS[@]}" \ @@ -227,7 +228,7 @@ if [ "$OPT_LEVEL" != "none" ] && command -v wasm-opt &> /dev/null; then "$WASM_PATH" \ -o \ "$WASM_PATH" - + # Create optimized version for wasm-sdk if [ "$PACKAGE_NAME" = "wasm-sdk" ]; then cp "$WASM_PATH" "pkg/optimized.wasm" diff --git a/packages/wasm-sdk/.gitignore b/packages/wasm-sdk/.gitignore index 755421dbad0..0073538b9dc 100644 --- a/packages/wasm-sdk/.gitignore +++ b/packages/wasm-sdk/.gitignore @@ -1,6 +1,2 @@ -playwright-report/ -test-results/ -test/test-report.html - -# Environment variables with sensitive test data -test/ui-automation/.env +dist +pkg diff --git a/packages/wasm-sdk/.mocharc.yml b/packages/wasm-sdk/.mocharc.yml new file mode 100644 index 00000000000..e82067f393b --- /dev/null +++ b/packages/wasm-sdk/.mocharc.yml @@ -0,0 +1,4 @@ +require: tests/bootstrap.cjs +recursive: true +timeout: 8000 + diff --git a/packages/wasm-sdk/AI_REFERENCE.md b/packages/wasm-sdk/AI_REFERENCE.md deleted file mode 100644 index 562a3d7216a..00000000000 --- a/packages/wasm-sdk/AI_REFERENCE.md +++ /dev/null @@ -1,1287 +0,0 @@ -# Dash Platform WASM JS SDK - AI Reference - -## Overview -The Dash Platform WASM JS SDK provides WebAssembly bindings for interacting with Dash Platform from JavaScript/TypeScript. This reference is optimized for AI understanding and quick implementation. - -## Quick Setup -```javascript -// Import and initialize -import init, { WasmSdk } from './pkg/wasm_sdk.js'; - -await init(); -const transport = { - url: "https://52.12.176.90:1443/", // testnet - network: "testnet" -}; -const proofs = true; // Enable proof verification -const sdk = await WasmSdk.new(transport, proofs); -``` - -## Authentication -Most state transitions require authentication: -```javascript -const identityHex = "hex_encoded_identity"; -const privateKeyHex = "hex_encoded_private_key"; -``` - -## Query Operations - -### Pattern -All queries follow this pattern: -```javascript -const result = await sdk.{query_name}(param1, param2, ...); -``` - -### Available Queries - -#### Identity Queries - -**Get Identity** - `getIdentity` -*Fetch an identity by its identifier* - -Parameters: -- `id` (text, required) - Identity ID - -Example: -```javascript -const identity = await sdk.getIdentity("GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"); -``` - -**Get Identity Keys** - `getIdentityKeys` -*Retrieve keys associated with an identity* - -Parameters: -- `identityId` (text, required) - Identity ID -- `keyRequestType` (select, optional) - Key Request Type - - Options: `all` (All Keys (AllKeys {}) - Get all keys for the identity), `specific` (Specific Keys (SpecificKeys with key_ids) - Get specific keys by ID [🚧 Work in Progress]), `search` (Search Keys (SearchKey with purpose_map) - Search by purpose and security level [🚧 Work in Progress]) -- `specificKeyIds` (array, optional) - Specific Key IDs (required for 'specific' type) - - Example: `0,1,2` -- `searchPurposeMap` (text, optional) - Search Purpose Map JSON (required for 'search' type) - - Example: `{"0": {"0": "current"}, "1": {"0": "all"}}` -- `limit` (number, optional) - Limit -- `offset` (number, optional) - Offset - -Example: -```javascript -const result = await sdk.getIdentityKeys("identityId"); -``` - -**Get Identities Contract Keys** - `getIdentitiesContractKeys` -*Get keys for multiple identities related to a specific contract* - -Parameters: -- `identitiesIds` (array, required) - Identity IDs -- `contractId` (text, required) - Contract ID -- `purposes` (multiselect, optional) - Key Purposes - - Options: `0` (Authentication), `1` (Encryption), `2` (Decryption), `3` (Transfer), `5` (Voting) - -Example: -```javascript -const result = await sdk.getIdentitiesContractKeys([], "contractId"); -``` - -**Get Identity Nonce** - `getIdentityNonce` -*Get the current nonce for an identity* - -Parameters: -- `identityId` (text, required) - Identity ID - -Example: -```javascript -const result = await sdk.getIdentityNonce("identityId"); -``` - -**Get Identity Contract Nonce** - `getIdentityContractNonce` -*Get the nonce for an identity in relation to a specific contract* - -Parameters: -- `identityId` (text, required) - Identity ID -- `contractId` (text, required) - Contract ID - -Example: -```javascript -const result = await sdk.getIdentityContractNonce("identityId", "contractId"); -``` - -**Get Identity Balance** - `getIdentityBalance` -*Get the credit balance of an identity* - -Parameters: -- `id` (text, required) - Identity ID - -Example: -```javascript -const balance = await sdk.getIdentityBalance(identityId); -``` - -**Get Identities Balances** - `getIdentitiesBalances` -*Get balances for multiple identities* - -Parameters: -- `ids` (array, required) - Identity IDs - -Example: -```javascript -const result = await sdk.getIdentitiesBalances([]); -``` - -**Get Identity Balance and Revision** - `getIdentityBalanceAndRevision` -*Get both balance and revision number for an identity* - -Parameters: -- `id` (text, required) - Identity ID - -Example: -```javascript -const result = await sdk.getIdentityBalanceAndRevision("id"); -``` - -**Get Identity by Unique Public Key Hash** - `getIdentityByPublicKeyHash` -*Find an identity by its unique public key hash* - -Parameters: -- `publicKeyHash` (text, required) - Public Key Hash - - Example: `b7e904ce25ed97594e72f7af0e66f298031c1754` - -Example: -```javascript -const result = await sdk.getIdentityByPublicKeyHash("publicKeyHash"); -``` - -**Get Identity by Non-Unique Public Key Hash** - `getIdentityByNonUniquePublicKeyHash` -*Find identities by non-unique public key hash* - -Parameters: -- `publicKeyHash` (text, required) - Public Key Hash - - Example: `518038dc858461bcee90478fd994bba8057b7531` -- `startAfter` (text, optional) - Start After - -Example: -```javascript -const result = await sdk.getIdentityByNonUniquePublicKeyHash("publicKeyHash"); -``` - -**Get Identity Token Balances** - `getIdentityTokenBalances` -*Get token balances for an identity* - -Parameters: -- `identityId` (text, required) - Identity ID -- `tokenIds` (array, required) - Token IDs - -Example: -```javascript -const result = await sdk.getIdentityTokenBalances("identityId", []); -``` - -**Get Identities Token Balances** - `getIdentitiesTokenBalances` -*Get token balance for multiple identities* - -Parameters: -- `identityIds` (array, required) - Identity IDs -- `tokenId` (text, required) - Token ID - - Example: `Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv` - -Example: -```javascript -const result = await sdk.getIdentitiesTokenBalances([], "tokenId"); -``` - -**Get Identity Token Info** - `getIdentityTokenInfos` -*Get token information for an identity's tokens* - -Parameters: -- `identityId` (text, required) - Identity ID -- `tokenIds` (array, optional) - Token IDs (optional) - - Example: `["Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv"]` -- `limit` (number, optional) - Limit -- `offset` (number, optional) - Offset - -Example: -```javascript -const result = await sdk.getIdentityTokenInfos("identityId"); -``` - -**Get Identities Token Info** - `getIdentitiesTokenInfos` -*Get token information for multiple identities with a specific token* - -Parameters: -- `identityIds` (array, required) - Identity IDs -- `tokenId` (text, required) - Token ID - - Example: `Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv` - -Example: -```javascript -const result = await sdk.getIdentitiesTokenInfos([], "tokenId"); -``` - -#### Data Contract Queries - -**Get Data Contract** - `getDataContract` -*Fetch a data contract by its identifier* - -Parameters: -- `id` (text, required) - Data Contract ID - - Example: `GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec` - -Example: -```javascript -const result = await sdk.getDataContract("id"); -``` - -**Get Data Contract History** - `getDataContractHistory` -*Get the version history of a data contract* - -Parameters: -- `id` (text, required) - Data Contract ID - - Example: `HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM` -- `limit` (number, optional) - Limit -- `offset` (number, optional) - Offset -- `startAtMs` (number, optional) - Start At Timestamp (ms) - -Example: -```javascript -const result = await sdk.getDataContractHistory("id"); -``` - -**Get Data Contracts** - `getDataContracts` -*Fetch multiple data contracts by their identifiers* - -Parameters: -- `ids` (array, required) - Data Contract IDs - - Example: `["GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", "ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A"]` - -Example: -```javascript -const result = await sdk.getDataContracts([]); -``` - -#### Document Queries - -**Get Documents** - `getDocuments` -*Query documents from a data contract* - -Parameters: -- `dataContractId` (text, required) - Data Contract ID - - Example: `GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec` -- `documentType` (text, required) - Document Type - - Example: `domain` -- `where` (json, optional) - Where Clause (JSON) - - Example: `[["normalizedParentDomainName", "==", "dash"], ["normalizedLabel", "==", "therea1s11mshaddy5"]]` -- `orderBy` (json, optional) - Order By (JSON) - - Example: `[["$createdAt", "desc"]]` -- `limit` (number, optional) - Limit - -Example: -```javascript -const docs = await sdk.getDocuments( - contractId, - "note", - JSON.stringify([["$ownerId", "==", identityId]]), - JSON.stringify([["$createdAt", "desc"]]), - 10 -); -``` - -**Get Document** - `getDocument` -*Fetch a specific document by ID* - -Parameters: -- `dataContractId` (text, required) - Data Contract ID - - Example: `GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec` -- `documentType` (text, required) - Document Type - - Example: `domain` -- `documentId` (text, required) - Document ID - - Example: `7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r` - -Example: -```javascript -const result = await sdk.getDocument("dataContractId", "documentType", "documentId"); -``` - -#### DPNS Queries - -**Get DPNS Usernames** - `getDpnsUsername` -*Get DPNS usernames for an identity* - -Parameters: -- `identityId` (text, required) - Identity ID - -Example: -```javascript -const result = await sdk.getDpnsUsername("identityId"); -``` - -**DPNS Check Availability** - `dpnsCheckAvailability` -*Check if a DPNS username is available* - -Parameters: -- `label` (text, required) - Label (Username) - -Example: -```javascript -const result = await sdk.dpnsCheckAvailability("label"); -``` - -**DPNS Resolve Name** - `dpnsResolve` -*Resolve a DPNS name to an identity ID* - -Parameters: -- `name` (text, required) - Name - -Example: -```javascript -const result = await sdk.dpnsResolve("name"); -``` - -**DPNS Search Name** - `dpnsSearch` -*Search for DPNS names that start with a given prefix* - -Parameters: -- `prefix` (text, required) - Name Prefix - - Example: `Enter prefix (e.g., ali)` -- `limit` (number, optional) - Limit - - Example: `Default: 10` - -Example: -```javascript -const result = await sdk.dpnsSearch("prefix"); -``` - -#### Voting & Contested Resources - -**Get Contested Resources** - `getContestedResources` -*Get list of contested resources* - -Parameters: -- `documentTypeName` (text, required) - Document Type -- `dataContractId` (text, required) - Data Contract ID -- `indexName` (text, required) - Index Name -- `startAtValue` (text, optional) - Start At Value -- `limit` (number, optional) - Limit -- `offset` (number, optional) - Offset -- `orderAscending` (checkbox, optional) - Order Ascending - -Example: -```javascript -const result = await sdk.getContestedResources("documentTypeName", "dataContractId", "indexName"); -``` - -**Get Contested Resource Vote State** - `getContestedResourceVoteState` -*Get the current vote state for a contested resource* - -Parameters: -- `dataContractId` (text, required) - Data Contract ID -- `documentTypeName` (text, required) - Document Type -- `indexName` (text, required) - Index Name -- `indexValues` (array, required) - Index Values - - Example: `["dash", "alice"]` -- `resultType` (text, required) - Result Type -- `allowIncludeLockedAndAbstainingVoteTally` (checkbox, optional) - Allow Include Locked and Abstaining Vote Tally -- `startAtIdentifierInfo` (text, optional) - Start At Identifier Info -- `count` (number, optional) - Count -- `orderAscending` (checkbox, optional) - Order Ascending - -Example: -```javascript -const result = await sdk.getContestedResourceVoteState("dataContractId", "documentTypeName", "indexName", [], "resultType"); -``` - -**Get Contested Resource Voters for Identity** - `getContestedResourceVotersForIdentity` -*Get voters who voted for a specific identity in a contested resource* - -Parameters: -- `dataContractId` (text, required) - Contract ID -- `documentTypeName` (text, required) - Document Type -- `indexName` (text, required) - Index Name -- `indexValues` (array, required) - Index Values - - Example: `["dash", "alice"]` -- `contestantId` (text, required) - Contestant Identity ID -- `startAtIdentifierInfo` (text, optional) - Start At Identifier Info -- `count` (number, optional) - Count - - Example: `Default: 100` -- `orderAscending` (checkbox, optional) - Order Ascending - -Example: -```javascript -const result = await sdk.getContestedResourceVotersForIdentity("dataContractId", "documentTypeName", "indexName", [], "contestantId"); -``` - -**Get Contested Resource Identity Votes** - `getContestedResourceIdentityVotes` -*Get all votes cast by a specific identity* - -Parameters: -- `identityId` (text, required) - Identity ID -- `limit` (number, optional) - Limit -- `offset` (number, optional) - Offset -- `orderAscending` (checkbox, optional) - Order Ascending - -Example: -```javascript -const result = await sdk.getContestedResourceIdentityVotes("identityId"); -``` - -**Get Vote Polls by End Date** - `getVotePollsByEndDate` -*Get vote polls within a time range* - -Parameters: -- `startTimeMs` (number, optional) - Start Time (ms) - - Example: `Timestamp in milliseconds as string (e.g., 1650000000000)` -- `endTimeMs` (number, optional) - End Time (ms) - - Example: `Timestamp in milliseconds as string (e.g., 1650086400000)` -- `limit` (number, optional) - Limit -- `offset` (number, optional) - Offset -- `orderAscending` (checkbox, optional) - Order Ascending - -Example: -```javascript -const result = await sdk.getVotePollsByEndDate(); -``` - -#### Protocol & Version - -**Get Protocol Version Upgrade State** - `getProtocolVersionUpgradeState` -*Get the current state of protocol version upgrades* - -No parameters required. - -Example: -```javascript -const result = await sdk.getProtocolVersionUpgradeState(); -``` - -**Get Protocol Version Upgrade Vote Status** - `getProtocolVersionUpgradeVoteStatus` -*Get voting status for protocol version upgrades* - -Parameters: -- `startProTxHash` (text, required) - Start ProTx Hash - - Example: `143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113` -- `count` (number, required) - Count - -Example: -```javascript -const result = await sdk.getProtocolVersionUpgradeVoteStatus("startProTxHash", 100); -``` - -#### Epoch & Block - -**Get Epochs Info** - `getEpochsInfo` -*Get information about epochs* - -Parameters: -- `startEpoch` (number, required) - Start Epoch -- `count` (number, required) - Count -- `ascending` (checkbox, optional) - Ascending Order - -Example: -```javascript -const result = await sdk.getEpochsInfo(100, 100); -``` - -**Get Current Epoch** - `getCurrentEpoch` -*Get information about the current epoch* - -No parameters required. - -Example: -```javascript -const result = await sdk.getCurrentEpoch(); -``` - -**Get Finalized Epoch Info** - `getFinalizedEpochInfos` -*Get information about finalized epochs* - -Parameters: -- `startEpoch` (number, required) - Start Epoch -- `count` (number, required) - Count -- `ascending` (checkbox, optional) - Ascending Order - -Example: -```javascript -const result = await sdk.getFinalizedEpochInfos(100, 100); -``` - -**Get Evonodes Proposed Epoch Blocks by IDs** - `getEvonodesProposedEpochBlocksByIds` -*Get proposed blocks by evonode IDs* - -Parameters: -- `epoch` (number, required) - Epoch -- `ids` (array, required) - ProTx Hashes - - Example: `["143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113"]` - -Example: -```javascript -const result = await sdk.getEvonodesProposedEpochBlocksByIds(100, []); -``` - -**Get Evonodes Proposed Epoch Blocks by Range** - `getEvonodesProposedEpochBlocksByRange` -*Get proposed blocks by range* - -Parameters: -- `epoch` (number, required) - Epoch -- `limit` (number, optional) - Limit -- `startAfter` (text, optional) - Start After (Evonode ID) - - Example: `143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113` -- `orderAscending` (checkbox, optional) - Order Ascending - -Example: -```javascript -const result = await sdk.getEvonodesProposedEpochBlocksByRange(100); -``` - -#### Token Queries - -**Get Token Statuses** - `getTokenStatuses` -*Get token statuses* - -Parameters: -- `tokenIds` (array, required) - Token IDs - -Example: -```javascript -const result = await sdk.getTokenStatuses([]); -``` - -**Get Token Direct Purchase Prices** - `getTokenDirectPurchasePrices` -*Get direct purchase prices for tokens* - -Parameters: -- `tokenIds` (array, required) - Token IDs - -Example: -```javascript -const result = await sdk.getTokenDirectPurchasePrices([]); -``` - -**Get Token Contract Info** - `getTokenContractInfo` -*Get information about a token contract* - -Parameters: -- `dataContractId` (text, required) - Data Contract ID - - Example: `EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta` - -Example: -```javascript -const result = await sdk.getTokenContractInfo("dataContractId"); -``` - -**Get Token Perpetual Distribution Last Claim** - `getTokenPerpetualDistributionLastClaim` -*Get last claim information for perpetual distribution* - -Parameters: -- `identityId` (text, required) - Identity ID -- `tokenId` (text, required) - Token ID - -Example: -```javascript -const result = await sdk.getTokenPerpetualDistributionLastClaim("identityId", "tokenId"); -``` - -**Get Token Total Supply** - `getTokenTotalSupply` -*Get total supply of a token* - -Parameters: -- `tokenId` (text, required) - Token ID - - Example: `Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv` - -Example: -```javascript -const result = await sdk.getTokenTotalSupply("tokenId"); -``` - -#### Group Queries - -**Get Group Info** - `getGroupInfo` -*Get information about a group* - -Parameters: -- `contractId` (text, required) - Contract ID -- `groupContractPosition` (number, required) - Group Contract Position - -Example: -```javascript -const result = await sdk.getGroupInfo("contractId", 100); -``` - -**Get Group Infos** - `getGroupInfos` -*Get information about multiple groups* - -Parameters: -- `contractId` (text, required) - Contract ID -- `startAtGroupContractPosition` (number, optional) - Start at Position -- `startGroupContractPositionIncluded` (checkbox, optional) - Include Start Position -- `count` (number, optional) - Count - -Example: -```javascript -const result = await sdk.getGroupInfos("contractId"); -``` - -**Get Group Actions** - `getGroupActions` -*Get actions for a group* - -Parameters: -- `contractId` (text, required) - Contract ID -- `groupContractPosition` (number, required) - Group Contract Position -- `status` (select, required) - Status - - Options: `ACTIVE` (Active), `CLOSED` (Closed) -- `startActionId` (text, optional) - Start Action ID -- `startActionIdIncluded` (checkbox, optional) - Include Start Action -- `count` (number, optional) - Count - -Example: -```javascript -const result = await sdk.getGroupActions("contractId", 100, "status"); -``` - -**Get Group Action Signers** - `getGroupActionSigners` -*Get signers for a group action* - -Parameters: -- `contractId` (text, required) - Contract ID -- `groupContractPosition` (number, required) - Group Contract Position -- `status` (select, required) - Status - - Options: `ACTIVE` (Active), `CLOSED` (Closed) -- `actionId` (text, required) - Action ID - -Example: -```javascript -const result = await sdk.getGroupActionSigners("contractId", 100, "status", "actionId"); -``` - -#### System & Utility - -**Get Status** - `getStatus` -*Get system status* - -No parameters required. - -Example: -```javascript -const result = await sdk.getStatus(); -``` - -**Get Current Quorums Info** - `getCurrentQuorumsInfo` -*Get information about current quorums* - -No parameters required. - -Example: -```javascript -const result = await sdk.getCurrentQuorumsInfo(); -``` - -**Get Prefunded Specialized Balance** - `getPrefundedSpecializedBalance` -*Get prefunded specialized balance* - -Parameters: -- `identityId` (text, required) - Specialized Balance ID - - Example: `AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT` - -Example: -```javascript -const result = await sdk.getPrefundedSpecializedBalance("identityId"); -``` - -**Get Total Credits in Platform** - `getTotalCreditsInPlatform` -*Get total credits in the platform* - -No parameters required. - -Example: -```javascript -const result = await sdk.getTotalCreditsInPlatform(); -``` - -**Get Path Elements** - `getPathElements` -*Access any data in the Dash Platform state tree. This low-level query allows direct access to GroveDB storage by specifying a path through the tree structure and keys to retrieve at that path. Common paths include: Identities (32), Tokens (96), DataContractDocuments (64), Balances (16), Votes (80), and more.* - -Parameters: -- `path` (array, required) - Path -- `keys` (array, required) - Keys - -Example: -```javascript -// Access any data in the Dash Platform state tree -// Common root paths: -// - DataContractDocuments: 64 -// - Identities: 32 -// - UniquePublicKeyHashesToIdentities: 24 -// - NonUniquePublicKeyKeyHashesToIdentities: 8 -// - Tokens: 16 -// - Pools: 48 -// - PreFundedSpecializedBalances: 40 -// - SpentAssetLockTransactions: 72 -// - WithdrawalTransactions: 80 -// - GroupActions: 88 -// - Balances: 96 -// - Misc: 104 -// - Votes: 112 -// - Versions: 120 - -// Example: Get identity balance -const result = await sdk.getPathElements(['96'], ['identityId']); - -// Example: Get identity info -const identityKeys = await sdk.getPathElements(['32'], ['identityId']); - -// Example: Get contract documents -const documents = await sdk.getPathElements(['64'], ['contractId', '1', 'documentType']); -``` - -**Wait for State Transition Result** - `waitForStateTransitionResult` -*Internal query to wait for and retrieve the result of a previously submitted state transition* - -Parameters: -- `stateTransitionHash` (text, required) - State Transition Hash - -Example: -```javascript -const result = await sdk.waitForStateTransitionResult("stateTransitionHash"); -``` - -## State Transition Operations - -### Pattern -All state transitions require authentication and follow this pattern: -```javascript -const result = await sdk.{transition_name}(identityHex, ...params, privateKeyHex); -``` - -### Available State Transitions - -#### Identity Transitions - -**Identity Create** - `identityCreate` -*Create a new identity with initial credits* - -Parameters: -- `assetLockProof` (string, required) - Asset Lock Proof - - Hex-encoded JSON asset lock proof -- `assetLockProofPrivateKey` (string, required) - Asset Lock Proof Private Key - - WIF format private key -- `publicKeys` (string, required) - Public Keys - - JSON array of public keys. Key requirements: ECDSA_SECP256K1 requires privateKeyHex or privateKeyWif for signing, BLS12_381 requires privateKeyHex for signing, ECDSA_HASH160 requires either the data field (base64-encoded 20-byte public key hash) or privateKeyHex (produces empty signatures). - -Example: -```javascript -// Asset lock proof is a hex-encoded JSON object -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -// Public keys array with proper key types and private keys for signing/hashing -const publicKeys = JSON.stringify([ - { - id: 0, - keyType: "ECDSA_SECP256K1", - purpose: "AUTHENTICATION", - securityLevel: "MASTER", - data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key - readOnly: false, - privateKeyWif: "XBrZJKcW4ajWVNAU6yP87WQog6CjFnpbqyAKgNTZRqmhYvPgMNV2" - }, - { - id: 1, - keyType: "ECDSA_HASH160", - purpose: "AUTHENTICATION", - securityLevel: "HIGH", - data: "ripemd160hash20bytes1234", // Base64-encoded 20-byte RIPEMD160 hash - readOnly: false, - // ECDSA_HASH160 keys produce empty signatures (not required/used for signing) - } -]); - -const result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys); -``` - -**Identity Top Up** - `identityTopUp` -*Add credits to an existing identity* - -Parameters: -- `identityId` (string, required) - Identity ID - - Base58 format identity ID -- `assetLockProof` (string, required) - Asset Lock Proof - - Hex-encoded JSON asset lock proof -- `assetLockProofPrivateKey` (string, required) - Asset Lock Proof Private Key - - WIF format private key - -Example: -```javascript -const identityId = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; // base58 -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWve1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey); -``` - -**Identity Update** - `identityUpdate` -*Update identity keys (add or disable)* - -Parameters (in addition to identity/key): -- `addPublicKeys` (textarea, optional) - Keys to Add (JSON array) - - Example: `[{"keyType":"ECDSA_HASH160","purpose":"AUTHENTICATION","data":"base64_key_data"}]` -- `disablePublicKeys` (text, optional) - Key IDs to Disable (comma-separated) - - Example: `2,3,5` - -Example: -```javascript -const result = await sdk.identityUpdate(identityHex, /* params */, privateKeyHex); -``` - -**Identity Credit Transfer** - `identityCreditTransfer` -*Transfer credits between identities* - -Parameters (in addition to identity/key): -- `recipientId` (text, required) - Recipient Identity ID -- `amount` (number, required) - Amount (credits) - -Example: -```javascript -const result = await sdk.identityCreditTransfer(identityHex, /* params */, privateKeyHex); -``` - -**Identity Credit Withdrawal** - `identityCreditWithdrawal` -*Withdraw credits from identity to Dash address* - -Parameters (in addition to identity/key): -- `toAddress` (text, required) - Dash Address -- `amount` (number, required) - Amount (credits) -- `coreFeePerByte` (number, optional) - Core Fee Per Byte (optional) - -Example: -```javascript -const result = await sdk.identityCreditWithdrawal(identityHex, /* params */, privateKeyHex); -``` - -#### Data Contract Transitions - -**Data Contract Create** - `dataContractCreate` -*Create a new data contract* - -Parameters (in addition to identity/key): -- `canBeDeleted` (checkbox, optional) - Can Be Deleted -- `readonly` (checkbox, optional) - Read Only -- `keepsHistory` (checkbox, optional) - Keeps History -- `documentsKeepHistoryContractDefault` (checkbox, optional) - Documents Keep History (Default) -- `documentsMutableContractDefault` (checkbox, optional) - Documents Mutable (Default) -- `documentsCanBeDeletedContractDefault` (checkbox, optional) - Documents Can Be Deleted (Default) -- `requiresIdentityEncryptionBoundedKey` (text, optional) - Requires Identity Encryption Key (optional) -- `requiresIdentityDecryptionBoundedKey` (text, optional) - Requires Identity Decryption Key (optional) -- `documentSchemas` (json, required) - Document Schemas JSON - - Example: `{ - "note": { - "type": "object", - "properties": { - "message": { - "type": "string", - "maxLength": 100, - "position": 0 - } - }, - "required": ["message"], - "additionalProperties": false - } -}` -- `groups` (json, optional) - Groups (optional) - - Example: `{}` -- `tokens` (json, optional) - Tokens (optional) - - Example: `{}` -- `keywords` (text, optional) - Keywords (comma separated, optional) -- `description` (text, optional) - Description (optional) - -Example: -```javascript -const result = await sdk.dataContractCreate(identityHex, /* params */, privateKeyHex); -``` - -**Data Contract Update** - `dataContractUpdate` -*Add document types, groups, or tokens to an existing data contract* - -Parameters (in addition to identity/key): -- `dataContractId` (text, required) - Data Contract ID -- `newDocumentSchemas` (json, optional) - New Document Schemas to Add (optional) - - Example: `{ - "newType": { - "type": "object", - "properties": { - "field": { - "type": "string", - "maxLength": 100, - "position": 0 - } - }, - "required": ["field"], - "additionalProperties": false - } -}` -- `newGroups` (json, optional) - New Groups to Add (optional) - - Example: `{}` -- `newTokens` (json, optional) - New Tokens to Add (optional) - - Example: `{}` - -Example: -```javascript -const result = await sdk.dataContractUpdate(identityHex, /* params */, privateKeyHex); -``` - -#### Document Transitions - -**Document Create** - `documentCreate` -*Create a new document* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `documentType` (text, required) - Document Type -- `fetchSchema` (button, optional) - Fetch Schema -- `documentFields` (dynamic, optional) - Document Fields - -Example: -```javascript -const result = await sdk.document_create( - identityHex, - contractId, - "note", - JSON.stringify({ message: "Hello!" }), - privateKeyHex -); -``` - -**Document Replace** - `documentReplace` -*Replace an existing document* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `documentType` (text, required) - Document Type -- `documentId` (text, required) - Document ID -- `loadDocument` (button, optional) - Load Document -- `documentFields` (dynamic, optional) - Document Fields - -Example: -```javascript -const result = await sdk.documentReplace(identityHex, /* params */, privateKeyHex); -``` - -**Document Delete** - `documentDelete` -*Delete an existing document* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `documentType` (text, required) - Document Type -- `documentId` (text, required) - Document ID - -Example: -```javascript -const result = await sdk.documentDelete(identityHex, /* params */, privateKeyHex); -``` - -**Document Transfer** - `documentTransfer` -*Transfer document ownership* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `documentType` (text, required) - Document Type -- `documentId` (text, required) - Document ID -- `recipientId` (text, required) - Recipient Identity ID - -Example: -```javascript -const result = await sdk.documentTransfer(identityHex, /* params */, privateKeyHex); -``` - -**Document Purchase** - `documentPurchase` -*Purchase a document* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `documentType` (text, required) - Document Type -- `documentId` (text, required) - Document ID -- `price` (number, required) - Price (credits) - -Example: -```javascript -const result = await sdk.documentPurchase(identityHex, /* params */, privateKeyHex); -``` - -**Document Set Price** - `documentSetPrice` -*Set or update document price* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `documentType` (text, required) - Document Type -- `documentId` (text, required) - Document ID -- `price` (number, required) - Price (credits, 0 to remove) - -Example: -```javascript -const result = await sdk.documentSetPrice(identityHex, /* params */, privateKeyHex); -``` - -**DPNS Register Name** - `dpnsRegister` -*Register a new DPNS username* - -Parameters (in addition to identity/key): -- `label` (text, required) - Username - - Example: `Enter username (e.g., alice)` - -Example: -```javascript -const result = await sdk.dpnsRegister(identityHex, /* params */, privateKeyHex); -``` - -#### Token Transitions - -**Token Burn** - `tokenBurn` -*Burn tokens* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `amount` (text, required) - Amount to Burn -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenBurn(identityHex, /* params */, privateKeyHex); -``` - -**Token Mint** - `tokenMint` -*Mint new tokens* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `amount` (text, required) - Amount to Mint -- `issuedToIdentityId` (text, optional) - Issue To Identity ID -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenMint(identityHex, /* params */, privateKeyHex); -``` - -**Token Claim** - `tokenClaim` -*Claim tokens from a distribution* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `distributionType` (select, required) - Distribution Type -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenClaim(identityHex, /* params */, privateKeyHex); -``` - -**Token Set Price** - `tokenSetPriceForDirectPurchase` -*Set or update the price for direct token purchases* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `priceType` (select, required) - Price Type -- `priceData` (text, optional) - Price Data (single price or JSON map) - - Example: `Leave empty to remove pricing` -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenSetPriceForDirectPurchase(identityHex, /* params */, privateKeyHex); -``` - -**Token Direct Purchase** - `tokenDirectPurchase` -*Purchase tokens directly at the configured price* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `amount` (text, required) - Amount to Purchase -- `totalAgreedPrice` (text, optional) - Total Agreed Price (in credits) - Optional, fetches from pricing schedule if not provided - -Example: -```javascript -const result = await sdk.tokenDirectPurchase(identityHex, /* params */, privateKeyHex); -``` - -**Token Config Update** - `tokenConfigUpdate` -*Update token configuration settings* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `configItemType` (select, required) - Config Item Type -- `configValue` (text, required) - Config Value (JSON or specific value) -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenConfigUpdate(identityHex, /* params */, privateKeyHex); -``` - -**Token Transfer** - `tokenTransfer` -*Transfer tokens between identities* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `amount` (text, required) - Amount to Transfer -- `recipientId` (text, required) - Recipient Identity ID -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.token_transfer( - identityHex, - contractId, - tokenId, - 1000000, // amount - recipientId, - privateKeyHex -); -``` - -**Token Freeze** - `tokenFreeze` -*Freeze tokens for a specific identity* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `identityToFreeze` (text, required) - Identity ID to Freeze -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenFreeze(identityHex, /* params */, privateKeyHex); -``` - -**Token Unfreeze** - `tokenUnfreeze` -*Unfreeze tokens for a specific identity* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `identityToUnfreeze` (text, required) - Identity ID to Unfreeze -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenUnfreeze(identityHex, /* params */, privateKeyHex); -``` - -**Token Destroy Frozen** - `tokenDestroyFrozen` -*Destroy frozen tokens* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID -- `tokenPosition` (number, required) - Token Contract Position -- `frozenIdentityId` (text, required) - Identity ID whose frozen tokens to destroy -- `publicNote` (text, optional) - Public Note - -Example: -```javascript -const result = await sdk.tokenDestroyFrozen(identityHex, /* params */, privateKeyHex); -``` - -#### Voting Transitions - -**DPNS Username** - `dpnsUsername` -*Cast a vote for a contested DPNS username* - -Parameters (in addition to identity/key): -- `contestedUsername` (text, required) - Contested Username - - Example: `Enter the contested username (e.g., 'myusername')` -- `voteChoice` (select, required) - Vote Choice -- `targetIdentity` (text, optional) - Target Identity ID (if voting for identity) - - Example: `Identity ID to vote for` - -Example: -```javascript -const result = await sdk.dpnsUsername(identityHex, /* params */, privateKeyHex); -``` - -**Contested Resource** - `masternodeVote` -*Cast a vote for contested resources as a masternode* - -Parameters (in addition to identity/key): -- `contractId` (text, required) - Data Contract ID - - Example: `Contract ID containing the contested resource` -- `fetchContestedResources` (button, optional) - Get Contested Resources -- `contestedResourceDropdown` (dynamic, optional) - Contested Resources -- `voteChoice` (select, required) - Vote Choice -- `targetIdentity` (text, optional) - Target Identity ID (if voting for identity) - - Example: `Identity ID to vote for` - -Example: -```javascript -const result = await sdk.masternodeVote(identityHex, /* params */, privateKeyHex); -``` - -## Common Patterns - -### Error Handling -```javascript -try { - const result = await sdk.getIdentity(identityId); - console.log(result); -} catch (error) { - console.error("Query failed:", error); -} -``` - -### Working with Proofs -```javascript -// Enable proofs during SDK initialization -const sdk = await WasmSdk.new(transport, true); - -// Query with proof verification -const identityWithProof = await sdk.getIdentity(identityId); -``` - -### Document Queries with Where/OrderBy -```javascript -// Where clause format: [[field, operator, value], ...] -const whereClause = JSON.stringify([ - ["$ownerId", "==", identityId], - ["age", ">=", 18] -]); - -// OrderBy format: [[field, direction], ...] -const orderBy = JSON.stringify([ - ["$createdAt", "desc"] -]); - -const docs = await sdk.getDocuments( - contractId, - documentType, - whereClause, - orderBy, - limit -); -``` - -### Batch Operations -```javascript -// Get multiple identities -const identityIds = ["id1", "id2", "id3"]; -const balances = await sdk.getIdentitiesBalances(identityIds); -``` - -## Important Notes - -1. **Network Endpoints**: - - Testnet: `https://52.12.176.90:1443/` - - Mainnet: Update when available - -2. **Identity Format**: Identity IDs and keys should be hex-encoded strings - -3. **Credits**: All fees are paid in credits (1 credit = 1 satoshi equivalent) - -4. **Nonces**: The SDK automatically handles nonce management for state transitions - -5. **Proofs**: Enable proofs for production applications to ensure data integrity - -## Troubleshooting - -- **Connection errors**: Verify network endpoint and that SDK is initialized -- **Invalid parameters**: Check parameter types and required fields -- **Authentication failures**: Ensure correct identity/key format and key permissions -- **Query errors**: Validate contract IDs, document types, and field names exist diff --git a/packages/wasm-sdk/CACHING.md b/packages/wasm-sdk/CACHING.md deleted file mode 100644 index 21aec736267..00000000000 --- a/packages/wasm-sdk/CACHING.md +++ /dev/null @@ -1,146 +0,0 @@ -# WASM SDK Caching - -The WASM SDK implements multiple caching strategies to improve performance and reduce network requests. - -## How Cache Invalidation Works - -The cache system uses two mechanisms to ensure users get updates: - -### 1. ETag Support (with server.py) - -When using `python3 server.py`: -- Server generates **ETags** based on file content -- Browser validates cache using ETags -- Returns `304 Not Modified` if unchanged -- Automatic cache invalidation when files change - -### 2. Cache-First with Background Updates - -The service worker: -- **Serves from cache immediately** (fast!) -- **Updates cache in background** -- Next reload gets the updated version -- No manual cache busting needed - -### How It Works - -1. **First visit**: Downloads and caches WASM files -2. **Subsequent visits**: Loads from cache instantly -3. **Background**: Checks for updates -4. **File changes**: New ETag β†’ Cache updated -5. **Next visit**: New version served - -## Service Worker Caching - -The SDK uses a Service Worker to cache the WASM binary and JavaScript files. This provides: - -- **Offline capability**: Once cached, the SDK can work offline -- **Faster loading**: Subsequent visits load from cache instantly -- **Automatic updates**: The service worker checks for updates hourly - -### Files Cached - -- `/pkg/wasm_sdk.js` - JavaScript bindings -- `/pkg/wasm_sdk_bg.wasm` - WASM binary (several MB) -- `/pkg/wasm_sdk.d.ts` - TypeScript definitions -- `/index.html` - Main page - -## Using the Cache - -### Automatic Caching - -The service worker automatically caches files on first load. Subsequent loads will use the cached version. - -### Manual Cache Control - -The UI provides a "Clear Cache" button that: -1. Clears the service worker cache -2. Clears all browser caches -3. Reloads the page with fresh resources - -### Using the Python Server with Cache Headers - -For better cache control, use the provided Python server: - -```bash -# Instead of: -python3 -m http.server 8888 - -# Use: -python3 server.py -``` - -This server adds proper cache headers: -- WASM files: Cached for 1 week -- JS files in /pkg/: Cached for 1 week -- HTML files: Not cached -- Other files: Cached for 1 hour - -## Browser Developer Tips - -### Force Refresh -- **Chrome/Edge**: Ctrl+Shift+R (Cmd+Shift+R on Mac) -- **Firefox**: Ctrl+F5 (Cmd+Shift+R on Mac) -- **Safari**: Cmd+Option+R - -### Disable Cache in DevTools -1. Open Developer Tools (F12) -2. Go to Network tab -3. Check "Disable cache" - -### View Service Worker -1. Open Developer Tools -2. Go to Application tab -3. Click on "Service Workers" in sidebar -4. You can manually unregister the worker here - -## How It Works - -### Automatic Cache Updates - -1. **Build**: Just run `./build.sh` or `wasm-pack build` -2. **Deploy**: Upload the new files -3. **Automatic Detection**: Service worker detects the new WASM file hash -4. **User Notification**: Users see "New version detected!" -5. **Update**: Users refresh to get the new version - -### Example Flow - -```bash -# Make changes to Rust code -vim src/lib.rs - -# Build (cache updates automatically!) -./build.sh - -# That's it! No version increment needed -``` - -### Cache Names - -The cache is named based on content hash: -- `wasm-sdk-cache-a1b2c3d4` (old version) -- `wasm-sdk-cache-e5f6g7h8` (new version after rebuild) - -The service worker: -- Detects the hash changed -- Creates new cache with new name -- Deletes old cache -- Notifies users - -## Troubleshooting - -### Cache not working? -- Check if service worker registered (see console) -- Ensure HTTPS or localhost (service workers require secure context) -- Check browser support for service workers - -### Old version still loading? -1. Click "Clear Cache" button -2. Or manually unregister service worker in DevTools -3. Force refresh the page - -### Service worker errors? -- Check console for registration errors -- Ensure `/service-worker.js` is accessible -- Try incognito/private mode to bypass cache \ No newline at end of file diff --git a/packages/wasm-sdk/CLAUDE.md b/packages/wasm-sdk/CLAUDE.md deleted file mode 100644 index f2d9620bc8b..00000000000 --- a/packages/wasm-sdk/CLAUDE.md +++ /dev/null @@ -1,98 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Documentation - -**IMPORTANT**: For comprehensive API reference and usage examples, see: -- **[AI_REFERENCE.md](AI_REFERENCE.md)** - Complete API reference with all queries and state transitions -- **[docs.html](docs.html)** - User-friendly documentation -- **[index.html](index.html)** - Live interactive demo - -When implementing WASM SDK functionality, always refer to AI_REFERENCE.md first for accurate method signatures and examples. - -## Important Notes - -### Network Connectivity -**THERE ARE NO CORS OR SSL ISSUES WITH THE DASH PLATFORM ENDPOINTS IN WASM-SDK** -- The Dash Platform HTTPS endpoints (e.g., https://52.12.176.90:1443) work perfectly fine from browsers -- These endpoints have proper CORS headers configured -- SSL certificates are valid and accepted by browsers -- If you see connection errors, check: - - SDK initialization and configuration - - Parameter validation (identity IDs, contract IDs, etc.) - - Whether the SDK is in the correct network mode (testnet vs mainnet) - - The actual error message details (not just assuming it's CORS/SSL) - -## Architecture - -The WASM SDK is a WebAssembly build of the Dash SDK that runs in browsers. It provides: - -1. **Queries** - Read operations that fetch data from Dash Platform -2. **State Transitions** - Write operations that modify state on Dash Platform - -### Key Components - -- `src/sdk.rs` - Main SDK wrapper with WasmSdk and WasmSdkBuilder -- `src/queries/` - All query implementations (identity, documents, tokens, etc.) -- `src/state_transitions/` - State transition implementations -- `src/context_provider/` - Context providers for trusted/untrusted modes -- `index.html` - Example web interface for testing SDK functionality - -### Building - -Run `./build.sh` to build the WASM module. Output goes to `pkg/` directory. - -### Testing - -1. Start web server: `python3 -m http.server 8888` -2. Open http://localhost:8888 -3. Select network (testnet/mainnet) -4. Choose operation type (queries/state transitions) -5. Fill in parameters and execute - -## Documentation Maintenance - -When adding new queries or state transitions: -1. Update the definitions in `index.html` -2. Run `python3 generate_docs.py` to regenerate documentation -3. The CI will fail if documentation is out of sync - -## Common Issues - -1. **"time not implemented on this platform"** - Fixed by using `js_sys::Date::now()` in WASM builds -2. **Import errors** - Token functions are methods on WasmSdk, not standalone functions -3. **Network timeouts** - Usually means invalid parameters or identities, NOT network issues - -## Query Support - -The WASM SDK now fully supports where and orderBy clauses for document queries: - -### Where Clauses -- Format: JSON array of clause arrays `[[field, operator, value], ...]` -- Supported operators: - - `==` or `=` - Equal - - `>` - Greater than - - `>=` - Greater than or equals - - `<` - Less than - - `<=` - Less than or equals - - `in` or `In` - In array - - `startsWith` or `StartsWith` - String prefix match - - `Between`, `BetweenExcludeBounds`, `BetweenExcludeLeft`, `BetweenExcludeRight` - Range operators - -### Order By Clauses -- Format: JSON array of clause arrays `[[field, direction], ...]` -- Direction: `"asc"` or `"desc"` - -### Example -```javascript -const whereClause = JSON.stringify([ - ["$ownerId", ">", "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"], - ["age", ">=", 18] -]); - -const orderBy = JSON.stringify([ - ["$createdAt", "desc"], - ["name", "asc"] -]); -``` \ No newline at end of file diff --git a/packages/wasm-sdk/Cargo.lock b/packages/wasm-sdk/Cargo.lock deleted file mode 100644 index 43f721b89bd..00000000000 --- a/packages/wasm-sdk/Cargo.lock +++ /dev/null @@ -1,5156 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if 1.0.1", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" -dependencies = [ - "windows-sys 0.60.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.60.2", -] - -[[package]] -name = "anyhow" -version = "1.0.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "backon" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592277618714fbcecda9a02ba7a8781f319d26532a88553bbacc77ba5d2b3a8d" -dependencies = [ - "fastrand", - "tokio", -] - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if 1.0.1", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base58ck" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" -dependencies = [ - "bitcoin-internals 0.3.0", - "bitcoin_hashes 0.14.0", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64-compat" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" -dependencies = [ - "byteorder", -] - -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bincode" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" -dependencies = [ - "bincode_derive", - "serde", -] - -[[package]] -name = "bincode_derive" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" -dependencies = [ - "virtue 0.0.13", -] - -[[package]] -name = "bip37-bloom-filter" -version = "0.1.0" -source = "git+https://github.com/dashpay/rs-bip37-bloom-filter?branch=develop#352fa2312fd6e0f47bf4cba3a67bfd78c3ba763b" -dependencies = [ - "bitvec", - "murmur3", - "thiserror 1.0.69", -] - -[[package]] -name = "bip39" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d193de1f7487df1914d3a568b772458861d33f9c54249612cc2893d6915054" -dependencies = [ - "bitcoin_hashes 0.13.0", - "rand 0.8.5", - "rand_core 0.6.4", - "serde", - "unicode-normalization", - "zeroize", -] - -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - -[[package]] -name = "bitcoin-internals" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" - -[[package]] -name = "bitcoin-io" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" - -[[package]] -name = "bitcoin_hashes" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" -dependencies = [ - "bitcoin-internals 0.2.0", - "hex-conservative 0.1.2", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" -dependencies = [ - "bitcoin-io", - "hex-conservative 0.2.1", -] - -[[package]] -name = "bitflags" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake3" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if 1.0.1", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "blsful" -version = "3.0.0-pre8" -source = "git+https://github.com/dashpay/agora-blsful?rev=be108b2cf6ac64eedbe04f91c63731533c8956bc#be108b2cf6ac64eedbe04f91c63731533c8956bc" -dependencies = [ - "anyhow", - "blstrs_plus", - "hex", - "hkdf", - "merlin", - "pairing", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "serde_bare", - "sha2", - "sha3", - "subtle", - "thiserror 2.0.15", - "uint-zigzag", - "vsss-rs", - "zeroize", -] - -[[package]] -name = "blst" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", -] - -[[package]] -name = "blstrs_plus" -version = "0.8.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a16dd4b0d6b4538e1fa0388843acb186363082713a8fc8416d802a04d013818" -dependencies = [ - "arrayref", - "blst", - "elliptic-curve", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "serde", - "subtle", - "zeroize", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" -dependencies = [ - "serde", -] - -[[package]] -name = "cc" -version = "1.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "chrono-tz" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if 1.0.1", - "wasm-bindgen", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if 1.0.1", -] - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if 1.0.1", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "dapi-grpc" -version = "2.0.0" -dependencies = [ - "dapi-grpc-macros", - "futures-core", - "getrandom 0.2.16", - "platform-version", - "prost 0.14.1", - "serde", - "serde_bytes", - "serde_json", - "tenderdash-proto", - "tonic", - "tonic-prost", - "tonic-prost-build", -] - -[[package]] -name = "dapi-grpc-macros" -version = "2.0.0" -dependencies = [ - "heck", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.106", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "dash-context-provider" -version = "2.0.0" -dependencies = [ - "dpp", - "drive", - "hex", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "dash-network" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" -dependencies = [ - "bincode", - "bincode_derive", - "hex", - "serde", -] - -[[package]] -name = "dash-sdk" -version = "2.0.0" -dependencies = [ - "arc-swap", - "async-trait", - "backon", - "bip37-bloom-filter", - "chrono", - "ciborium", - "dapi-grpc", - "dapi-grpc-macros", - "dash-context-provider", - "derive_more 1.0.0", - "dotenvy", - "dpp", - "drive", - "drive-proof-verifier", - "envy", - "futures", - "hex", - "http", - "js-sys", - "lru", - "rs-dapi-client", - "rustls-pemfile", - "serde", - "serde_json", - "thiserror 2.0.15", - "tokio", - "tokio-util", - "tracing", - "zeroize", -] - -[[package]] -name = "dashcore" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" -dependencies = [ - "anyhow", - "base64-compat", - "bech32", - "bincode", - "bincode_derive", - "bitvec", - "blake3", - "blsful", - "dash-network", - "dashcore-private", - "dashcore_hashes", - "ed25519-dalek", - "hex", - "hex_lit", - "log", - "rustversion", - "secp256k1", - "serde", - "thiserror 2.0.15", -] - -[[package]] -name = "dashcore-private" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" - -[[package]] -name = "dashcore-rpc" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" -dependencies = [ - "dashcore-rpc-json", - "hex", - "jsonrpc", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "dashcore-rpc-json" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" -dependencies = [ - "bincode", - "dashcore", - "hex", - "key-wallet", - "serde", - "serde_json", - "serde_repr", - "serde_with", -] - -[[package]] -name = "dashcore_hashes" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" -dependencies = [ - "bincode", - "dashcore-private", - "secp256k1", - "serde", -] - -[[package]] -name = "dashpay-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "data-contracts" -version = "2.0.0" -dependencies = [ - "dashpay-contract", - "dpns-contract", - "feature-flags-contract", - "keyword-search-contract", - "masternode-reward-shares-contract", - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", - "token-history-contract", - "wallet-utils-contract", - "withdrawals-contract", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl 1.0.0", -] - -[[package]] -name = "derive_more" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" -dependencies = [ - "derive_more-impl 2.0.1", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "unicode-xid", -] - -[[package]] -name = "derive_more-impl" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dpns-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "dpp" -version = "2.0.0" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "bincode", - "bincode_derive", - "bs58", - "byteorder", - "chrono", - "chrono-tz", - "ciborium", - "dashcore", - "dashcore-rpc", - "data-contracts", - "derive_more 1.0.0", - "env_logger", - "getrandom 0.2.16", - "hex", - "indexmap 2.10.0", - "integer-encoding", - "itertools 0.13.0", - "key-wallet", - "lazy_static", - "nohash-hasher", - "num_enum 0.7.4", - "once_cell", - "platform-serialization", - "platform-serialization-derive", - "platform-value", - "platform-version", - "platform-versioning", - "rand 0.8.5", - "regex", - "serde", - "serde_json", - "serde_repr", - "sha2", - "strum", - "thiserror 2.0.15", - "tracing", -] - -[[package]] -name = "drive" -version = "2.0.0" -dependencies = [ - "bincode", - "byteorder", - "derive_more 1.0.0", - "dpp", - "grovedb", - "grovedb-costs", - "grovedb-epoch-based-storage-flags", - "grovedb-path", - "grovedb-version", - "hex", - "indexmap 2.10.0", - "integer-encoding", - "nohash-hasher", - "platform-version", - "serde", - "sqlparser", - "thiserror 2.0.15", - "tracing", -] - -[[package]] -name = "drive-proof-verifier" -version = "2.0.0" -dependencies = [ - "bincode", - "dapi-grpc", - "dash-context-provider", - "derive_more 1.0.0", - "dpp", - "drive", - "hex", - "indexmap 2.10.0", - "platform-serialization", - "platform-serialization-derive", - "serde", - "serde_json", - "tenderdash-abci", - "thiserror 2.0.15", - "tracing", -] - -[[package]] -name = "ed" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c8d6ea916fadcd87e3d1ff4802b696d717c83519b47e76f267ab77e536dd5a" -dependencies = [ - "ed-derive", - "thiserror 1.0.69", -] - -[[package]] -name = "ed-derive" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a91d774f4b861acaa791bc6165e66d72d3a5d1aa85fc8c0956f5580f863161" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.6.4", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array 0.14.7", - "group", - "hkdf", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "tap", - "zeroize", -] - -[[package]] -name = "elliptic-curve-tools" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48843edfbd0a370b3dd14cdbb4e446e9a8855311e6b2b57bf9a1fd1367bc317" -dependencies = [ - "elliptic-curve", - "heapless", - "hex", - "multiexp", - "serde", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if 1.0.1", -] - -[[package]] -name = "env_filter" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "envy" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" -dependencies = [ - "serde", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "feature-flags-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "bitvec", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flate2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" -dependencies = [ - "crc32fast", - "libz-rs-sys", - "miniz_oxide", -] - -[[package]] -name = "flex-error" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" -dependencies = [ - "paste", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703" -dependencies = [ - "serde", - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if 1.0.1", - "js-sys", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if 1.0.1", - "js-sys", - "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand 0.8.5", - "rand_core 0.6.4", - "rand_xorshift", - "subtle", -] - -[[package]] -name = "grovedb" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "bincode", - "bincode_derive", - "blake3", - "grovedb-costs", - "grovedb-merk", - "grovedb-path", - "grovedb-version", - "hex", - "hex-literal", - "indexmap 2.10.0", - "integer-encoding", - "reqwest", - "sha2", - "thiserror 2.0.15", -] - -[[package]] -name = "grovedb-costs" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "integer-encoding", - "intmap", - "thiserror 2.0.15", -] - -[[package]] -name = "grovedb-epoch-based-storage-flags" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "grovedb-costs", - "hex", - "integer-encoding", - "intmap", - "thiserror 2.0.15", -] - -[[package]] -name = "grovedb-merk" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "bincode", - "bincode_derive", - "blake3", - "byteorder", - "ed", - "grovedb-costs", - "grovedb-path", - "grovedb-version", - "grovedb-visualize", - "hex", - "indexmap 2.10.0", - "integer-encoding", - "thiserror 2.0.15", -] - -[[package]] -name = "grovedb-path" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "hex", -] - -[[package]] -name = "grovedb-version" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "thiserror 2.0.15", - "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "grovedb-visualize" -version = "3.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=1ecedf530fbc5b5e12edf1bc607bd288c187ddde#1ecedf530fbc5b5e12edf1bc607bd288c187ddde" -dependencies = [ - "hex", - "itertools 0.14.0", -] - -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap 2.10.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" -dependencies = [ - "cfg-if 1.0.1", - "crunchy", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-conservative" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" - -[[package]] -name = "hex-conservative" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hex_lit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "http-serde" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" -dependencies = [ - "http", - "serde", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" -dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2 0.6.0", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" - -[[package]] -name = "icu_properties" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "potential_utf", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" - -[[package]] -name = "icu_provider" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" -dependencies = [ - "displaydoc", - "icu_locale_core", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" -dependencies = [ - "equivalent", - "hashbrown 0.15.5", - "serde", -] - -[[package]] -name = "integer-encoding" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d762194228a2f1c11063e46e32e5acb96e66e906382b9eb5441f2e0504bbd5a" - -[[package]] -name = "intmap" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16dd999647b7a027fadf2b3041a4ea9c8ae21562823fe5cbdecd46537d535ae2" -dependencies = [ - "serde", -] - -[[package]] -name = "io-uring" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" -dependencies = [ - "bitflags", - "cfg-if 1.0.1", - "libc", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "jiff" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde", -] - -[[package]] -name = "jiff-static" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jsonrpc" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" -dependencies = [ - "base64 0.13.1", - "serde", - "serde_json", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "key-wallet" -version = "0.40.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=02d902c9845d5ed9e5cb88fd32a8c254742f20fd#02d902c9845d5ed9e5cb88fd32a8c254742f20fd" -dependencies = [ - "base58ck", - "bip39", - "bitflags", - "dash-network", - "dashcore", - "dashcore-private", - "dashcore_hashes", - "getrandom 0.2.16", - "hex", - "hkdf", - "rand 0.8.5", - "secp256k1", - "serde", - "serde_json", - "sha2", - "zeroize", -] - -[[package]] -name = "keyword-search-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lhash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744a4c881f502e98c2241d2e5f50040ac73b30194d64452bb6260393b53f0dc9" - -[[package]] -name = "libc" -version = "0.2.175" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" - -[[package]] -name = "libz-rs-sys" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "log" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "masternode-reward-shares-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "memchr" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", -] - -[[package]] -name = "multiexp" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a383da1ae933078ddb1e4141f1dd617b512b4183779d6977e6451b0e644806" -dependencies = [ - "ff", - "group", - "rustversion", - "std-shims", - "zeroize", -] - -[[package]] -name = "multimap" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" - -[[package]] -name = "murmur3" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252111cf132ba0929b6f8e030cac2a24b507f3a4d6db6fb2896f27b354c714b" - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework 2.11.1", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - -[[package]] -name = "num_enum" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" -dependencies = [ - "num_enum_derive 0.7.4", - "rustversion", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" -dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags", - "cfg-if 1.0.1", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset", - "indexmap 2.10.0", -] - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "platform-serialization" -version = "2.0.0" -dependencies = [ - "bincode", - "platform-version", -] - -[[package]] -name = "platform-serialization-derive" -version = "2.0.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "virtue 0.0.17", -] - -[[package]] -name = "platform-value" -version = "2.0.0" -dependencies = [ - "base64 0.22.1", - "bincode", - "bs58", - "ciborium", - "hex", - "indexmap 2.10.0", - "platform-serialization", - "platform-version", - "rand 0.8.5", - "serde", - "serde_json", - "thiserror 2.0.15", - "treediff", -] - -[[package]] -name = "platform-version" -version = "2.0.0" -dependencies = [ - "bincode", - "grovedb-version", - "once_cell", - "thiserror 2.0.15", - "versioned-feature-core 1.0.0 (git+https://github.com/dashpay/versioned-feature-core)", -] - -[[package]] -name = "platform-versioning" -version = "2.0.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.106", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" -dependencies = [ - "toml_edit 0.22.27", -] - -[[package]] -name = "proc-macro2" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive 0.13.5", -] - -[[package]] -name = "prost" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" -dependencies = [ - "bytes", - "prost-derive 0.14.1", -] - -[[package]] -name = "prost-build" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" -dependencies = [ - "heck", - "itertools 0.14.0", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.13.5", - "prost-types 0.13.5", - "regex", - "syn 2.0.106", - "tempfile", -] - -[[package]] -name = "prost-build" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" -dependencies = [ - "heck", - "itertools 0.14.0", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.14.1", - "prost-types 0.14.1", - "pulldown-cmark", - "pulldown-cmark-to-cmark", - "regex", - "syn 2.0.106", - "tempfile", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "prost-derive" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "prost-types" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" -dependencies = [ - "prost 0.13.5", -] - -[[package]] -name = "prost-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" -dependencies = [ - "prost 0.14.1", -] - -[[package]] -name = "pulldown-cmark" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" -dependencies = [ - "bitflags", - "memchr", - "unicase", -] - -[[package]] -name = "pulldown-cmark-to-cmark" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" -dependencies = [ - "pulldown-cmark", -] - -[[package]] -name = "quinn" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2 0.5.10", - "thiserror 2.0.15", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" -dependencies = [ - "bytes", - "getrandom 0.3.3", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.15", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.5.10", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "reqwest" -version = "0.12.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if 1.0.1", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rs-dapi-client" -version = "2.0.0" -dependencies = [ - "backon", - "chrono", - "dapi-grpc", - "futures", - "getrandom 0.2.16", - "gloo-timers", - "hex", - "http", - "http-body-util", - "http-serde", - "lru", - "rand 0.8.5", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.15", - "tokio", - "tonic-web-wasm-client", - "tower-service", - "tracing", - "wasm-bindgen-futures", -] - -[[package]] -name = "rs-sdk-trusted-context-provider" -version = "2.0.0" -dependencies = [ - "arc-swap", - "async-trait", - "dash-context-provider", - "dpp", - "futures", - "hex", - "lru", - "reqwest", - "serde", - "serde_json", - "thiserror 2.0.15", - "tracing", - "url", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.60.2", -] - -[[package]] -name = "rustls" -version = "0.23.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework 3.3.0", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" -dependencies = [ - "bitcoin_hashes 0.14.0", - "rand 0.8.5", - "secp256k1-sys", - "serde", -] - -[[package]] -name = "secp256k1-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" -dependencies = [ - "cc", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" -dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" - -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_bare" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c55386eed0f1ae957b091dc2ca8122f287b60c79c774cbe3d5f2b69fded660" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "serde_json" -version = "1.0.142" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" -dependencies = [ - "indexmap 2.10.0", - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" -dependencies = [ - "base64 0.13.1", - "chrono", - "hex", - "indexmap 1.9.3", - "serde", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if 1.0.1", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simple-signer" -version = "2.0.0" -dependencies = [ - "base64 0.22.1", - "bincode", - "dpp", - "hex", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlparser" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0272b7bb0a225320170c99901b4b5fb3a4384e255a7f2cc228f61e2ba3893e75" -dependencies = [ - "log", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "std-shims" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ade0decb9133b9d3cc0e7d99129c3bedabc92553736545cc4979800eaf8c21" -dependencies = [ - "hashbrown 0.14.5", - "spin", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.106", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "subtle-encoding" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" -dependencies = [ - "zeroize", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" -dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "tenderdash-abci" -version = "1.5.0-dev.1" -source = "git+https://github.com/dashpay/rs-tenderdash-abci?rev=9e3bcdc457ff5cbbd93be2fce510403d033c712b#9e3bcdc457ff5cbbd93be2fce510403d033c712b" -dependencies = [ - "bytes", - "hex", - "lhash", - "semver", - "tenderdash-proto", - "thiserror 2.0.15", - "tracing", - "url", -] - -[[package]] -name = "tenderdash-proto" -version = "1.5.0-dev.1" -source = "git+https://github.com/dashpay/rs-tenderdash-abci?rev=9e3bcdc457ff5cbbd93be2fce510403d033c712b#9e3bcdc457ff5cbbd93be2fce510403d033c712b" -dependencies = [ - "bytes", - "chrono", - "derive_more 2.0.1", - "flex-error", - "num-derive", - "num-traits", - "prost 0.13.5", - "serde", - "subtle-encoding", - "tenderdash-proto-compiler", - "time", -] - -[[package]] -name = "tenderdash-proto-compiler" -version = "1.5.0-dev.1" -source = "git+https://github.com/dashpay/rs-tenderdash-abci?rev=9e3bcdc457ff5cbbd93be2fce510403d033c712b#9e3bcdc457ff5cbbd93be2fce510403d033c712b" -dependencies = [ - "fs_extra", - "prost-build 0.13.5", - "regex", - "tempfile", - "ureq", - "walkdir", - "zip", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" -dependencies = [ - "thiserror-impl 2.0.15", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if 1.0.1", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "time" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "time-macros" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "token-history-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "tokio" -version = "1.47.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" -dependencies = [ - "backtrace", - "bytes", - "io-uring", - "libc", - "mio", - "pin-project-lite", - "slab", - "socket2 0.6.0", - "tokio-macros", - "windows-sys 0.59.0", -] - -[[package]] -name = "tokio-macros" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.10.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.10.0", - "toml_datetime", - "winnow 0.7.12", -] - -[[package]] -name = "tonic" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" -dependencies = [ - "async-trait", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "rustls-native-certs", - "socket2 0.6.0", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", - "webpki-roots", -] - -[[package]] -name = "tonic-build" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40aaccc9f9eccf2cd82ebc111adc13030d23e887244bc9cfa5d1d636049de3" -dependencies = [ - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "tonic-prost" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" -dependencies = [ - "bytes", - "prost 0.14.1", - "tonic", -] - -[[package]] -name = "tonic-prost-build" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a16cba4043dc3ff43fcb3f96b4c5c154c64cbd18ca8dce2ab2c6a451d058a2" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build 0.14.1", - "prost-types 0.14.1", - "quote", - "syn 2.0.106", - "tempfile", - "tonic-build", -] - -[[package]] -name = "tonic-web-wasm-client" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898cd44be5e23e59d2956056538f1d6b3c5336629d384ffd2d92e76f87fb98ff" -dependencies = [ - "base64 0.22.1", - "byteorder", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "httparse", - "js-sys", - "pin-project", - "thiserror 2.0.15", - "tonic", - "tower-service", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 2.10.0", - "pin-project-lite", - "slab", - "sync_wrapper", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "tracing-core" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "treediff" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ce481b2b7c2534fe7b5242cccebf37f9084392665c6a3783c414a1bada5432" - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "uint-zigzag" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abbf77aed65cb885a8ba07138c365879be3d9a93dce82bf6cc50feca9138ec15" -dependencies = [ - "core2", -] - -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00432f493971db5d8e47a65aeb3b02f8226b9b11f1450ff86bb772776ebadd70" -dependencies = [ - "base64 0.22.1", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "ureq-proto", - "utf-8", - "webpki-roots", -] - -[[package]] -name = "ureq-proto" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b6cabebbecc4c45189ab06b52f956206cea7d8c8a20851c35a85cb169224cc" -dependencies = [ - "base64 0.22.1", - "http", - "httparse", - "log", -] - -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "versioned-feature-core" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898c0ad500fdb1914df465a2c729fce33646ef65dfbbbd16a6d8050e0d2404df" - -[[package]] -name = "versioned-feature-core" -version = "1.0.0" -source = "git+https://github.com/dashpay/versioned-feature-core#560157096c8405a46ce0f21a2e7e1bd11d6625b4" - -[[package]] -name = "virtue" -version = "0.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" - -[[package]] -name = "virtue" -version = "0.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7302ac74a033bf17b6e609ceec0f891ca9200d502d31f02dc7908d3d98767c9d" - -[[package]] -name = "vsss-rs" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec4ebcc5594130c31b49594d55c0583fe80621f252f570b222ca4845cafd3cf" -dependencies = [ - "crypto-bigint", - "elliptic-curve", - "elliptic-curve-tools", - "generic-array 1.2.0", - "hex", - "num", - "rand_core 0.6.4", - "serde", - "sha3", - "subtle", - "zeroize", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wallet-utils-contract" -version = "2.0.0" -dependencies = [ - "platform-value", - "platform-version", - "serde_json", - "thiserror 2.0.15", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if 1.0.1", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if 1.0.1", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-sdk" -version = "0.0.0" -dependencies = [ - "base64 0.22.1", - "bip39", - "bs58", - "console_error_panic_hook", - "dapi-grpc", - "dash-sdk", - "drive", - "drive-proof-verifier", - "getrandom 0.2.16", - "hex", - "hmac", - "js-sys", - "once_cell", - "platform-value", - "rand 0.8.5", - "rs-dapi-client", - "rs-sdk-trusted-context-provider", - "serde", - "serde-wasm-bindgen", - "serde_json", - "sha2", - "simple-signer", - "thiserror 2.0.15", - "tracing", - "tracing-wasm", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "wee_alloc", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "wee_alloc" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "memory_units", - "winapi", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-registry" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.3", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] - -[[package]] -name = "withdrawals-contract" -version = "2.0.0" -dependencies = [ - "num_enum 0.5.11", - "platform-value", - "platform-version", - "serde", - "serde_json", - "serde_repr", - "thiserror 2.0.15", -] - -[[package]] -name = "writeable" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yoke" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "serde", - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "zip" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" -dependencies = [ - "arbitrary", - "crc32fast", - "flate2", - "indexmap 2.10.0", - "memchr", - "zopfli", -] - -[[package]] -name = "zlib-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" - -[[package]] -name = "zopfli" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] diff --git a/packages/wasm-sdk/Cargo.toml b/packages/wasm-sdk/Cargo.toml index b24678891f6..1e917ec222e 100644 --- a/packages/wasm-sdk/Cargo.toml +++ b/packages/wasm-sdk/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "wasm-sdk" +version = "2.0.0" edition = "2021" publish = false +rust-version.workspace = true + [lib] crate-type = ["cdylib"] @@ -42,6 +45,7 @@ wasm-bindgen = { version = "=0.2.100" } wasm-bindgen-futures = { version = "0.4.49" } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } # TODO: I think it's not needed (LKl) tracing = { version = "0.1.41" } +tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "registry"] } tracing-wasm = { version = "0.2.1" } wee_alloc = "0.4" platform-value = { path = "../rs-platform-value", features = ["json"] } @@ -71,3 +75,5 @@ lto = "fat" [package.metadata.wasm-pack] wasm-opt = false +[package.metadata.cargo-machete] +ignored = ["wasm-bindgen-futures"] diff --git a/packages/wasm-sdk/GROUP_QUERIES_DOCUMENTATION.md b/packages/wasm-sdk/GROUP_QUERIES_DOCUMENTATION.md deleted file mode 100644 index c33d7410c5b..00000000000 --- a/packages/wasm-sdk/GROUP_QUERIES_DOCUMENTATION.md +++ /dev/null @@ -1,168 +0,0 @@ -# Group Queries Implementation - -This document describes the implementation of group queries in the WASM SDK. - -## Overview - -The group queries allow you to interact with group functionality in Dash Platform data contracts. Groups are used to manage collective ownership and permissions. - -## Implemented Queries - -### 1. `get_group_info` - -Fetches information about a specific group. - -**Parameters:** -- `sdk`: The WASM SDK instance -- `data_contract_id`: The data contract ID (Base58 encoded string) -- `group_contract_position`: The position of the group in the contract (u32) - -**Returns:** -```javascript -{ - "members": { - "identityId1": 100, // member ID -> voting power - "identityId2": 50 - }, - "requiredPower": 100 // minimum power needed for decisions -} -``` -Returns `null` if the group doesn't exist. - -### 2. `get_group_members` - -Gets members of a specific group with optional filtering and pagination. - -**Parameters:** -- `sdk`: The WASM SDK instance -- `data_contract_id`: The data contract ID (Base58 encoded string) -- `group_contract_position`: The position of the group in the contract (u32) -- `member_ids`: Optional array of specific member IDs to fetch -- `start_at`: Optional member ID to start pagination from -- `limit`: Optional limit on number of results - -**Returns:** -```javascript -[ - { - "memberId": "identityId1", - "power": 100 - }, - { - "memberId": "identityId2", - "power": 50 - } -] -``` - -### 3. `get_identity_groups` - -Retrieves all groups associated with a specific identity. - -**Parameters:** -- `sdk`: The WASM SDK instance -- `identity_id`: The identity ID to search for (Base58 encoded string) -- `member_data_contracts`: Optional array of contract IDs to search for member roles -- `owner_data_contracts`: Optional array of contract IDs to search for owner roles (not yet implemented) -- `moderator_data_contracts`: Optional array of contract IDs to search for moderator roles (not yet implemented) - -**Returns:** -```javascript -[ - { - "dataContractId": "contractId1", - "groupContractPosition": 0, - "role": "member", - "power": 100 // only for member role - } -] -``` - -**Note:** Currently only member role queries are implemented. Owner and moderator roles require additional contract queries not yet available in the SDK. - -### 4. `get_groups_data_contracts` - -Fetches all groups for multiple data contracts. - -**Parameters:** -- `sdk`: The WASM SDK instance -- `data_contract_ids`: Array of data contract IDs to fetch groups from - -**Returns:** -```javascript -[ - { - "dataContractId": "contractId1", - "groups": [ - { - "position": 0, - "group": { - "members": { - "identityId1": 100 - }, - "requiredPower": 100 - } - } - ] - } -] -``` - -## Implementation Details - -The implementation uses: -- Dash SDK's `Fetch` and `FetchMany` traits for querying -- `GroupQuery` and `GroupInfosQuery` types from the SDK -- `serde_wasm_bindgen` with `json_compatible` serializer for proper JavaScript object conversion -- Base58 encoding for all identifiers passed to/from JavaScript - -## Error Handling - -All functions return proper error messages when: -- Invalid identifiers are provided -- Network errors occur -- Groups or contracts don't exist - -## Example Usage - -```javascript -import init, { - WasmSdkBuilder, - get_group_info, - get_group_members, - get_identity_groups, - get_groups_data_contracts -} from './pkg/wasm_sdk.js'; - -// Initialize SDK -await init(); -const builder = new WasmSdkBuilder(); -builder.with_core("127.0.0.1", 20002, "regtest", ""); -const sdk = await builder.build(); - -// Get group info -const groupInfo = await get_group_info( - sdk, - 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', - 0 -); - -// Get group members with pagination -const members = await get_group_members( - sdk, - 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', - 0, - null, // all members - null, // start from beginning - 10 // limit to 10 results -); - -// Get groups for an identity -const identityGroups = await get_identity_groups( - sdk, - '4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF', - ['GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'], // check member role in this contract - null, - null -); -``` \ No newline at end of file diff --git a/packages/wasm-sdk/README.md b/packages/wasm-sdk/README.md index 2af5d43444f..d326582e94b 100644 --- a/packages/wasm-sdk/README.md +++ b/packages/wasm-sdk/README.md @@ -1,145 +1,241 @@ -# Dash Platform WASM JS SDK +# WASM SDK -This package provides WebAssembly bindings for the Dash Platform SDK, allowing JavaScript and TypeScript applications in browsers to interact with Dash Platform. +Dash Platform WebAssembly SDK for JavaScript and TypeScript. The core is implemented in Rust and compiled to WebAssembly; a thin ESM wrapper exposes a convenient JS API that works in both Node.js and modern browsers. -## Overview +The SDK provides: +- Cryptographic utilities: mnemonic generation/validation, key derivation, key pair generation, address validation, message signing. +- Platform state transitions and queries: identities, documents, data contracts, tokens, groups, epochs/system, voting, proofs (when supported). +- A builder pattern (`WasmSdkBuilder`) to configure and construct a client for network-backed queries. -The WASM JS SDK provides: -- **Queries**: Read-only operations to fetch data from Dash Platform -- **State Transitions**: Write operations to modify state on Dash Platform +This package ships a single-file ESM build (`dist/sdk.js`) with the Wasm inlined and compiled off the main thread in browsers. Advanced users can also opt into separate raw artifacts under `dist/raw/*`. + +--- + +## Install + +From npm (consumer apps): + +```bash +npm install @dashevo/wasm-sdk +# or +yarn add @dashevo/wasm-sdk +``` + +From this monorepo (contributors): + +```bash +# fast for development +yarn build +# or optimized +yarn run build:release +``` + +The package is ESM-only ("type": "module"). In CommonJS, use dynamic import(). + +--- ## Usage -### Quick Start +Always call `await init()` once before using the API. It is idempotent and safe to call multiple times. -1. Build the WASM module: - ```bash - ./build.sh - ``` +### Node.js (ESM) -2. Serve the demo application: - ```bash - python3 -m http.server 8888 - ``` +```js +import init, * as sdk from '@dashevo/wasm-sdk'; -3. Open http://localhost:8888 in your browser +// Initialize Wasm (Node uses an inlined binary; no assets to load) +await init(); -### Integration +// Crypto helpers +const { address } = sdk.generate_key_pair('testnet'); +console.log('Address:', address); -```javascript -import init, { WasmSdk } from './pkg/wasm_sdk.js'; +// Optional: enable logs (silent by default) +await sdk.WasmSdk.setLogLevel('warn'); // or 'info' | 'debug' | full filter -// Initialize WASM module +// Platform queries via a client +let builder = sdk.WasmSdkBuilder.testnetTrusted(); +builder = builder.withSettings(5000, 10000, 3, true); +const client = await builder.withLogs('warn').build(); + +const DPNS = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; +const docs = await client.getDocuments(DPNS, 'domain', null, null, 5, null, null); +console.log('Docs:', docs.length); + +client.free(); +``` + +### Browser (bundler, ESM) + +```js +import init, * as sdk from '@dashevo/wasm-sdk'; + +// Initialize Wasm (browser compiles in a Web Worker; no separate .wasm files) await init(); -// Create SDK instance -const transport = { - url: "https://52.12.176.90:1443/", // testnet - network: "testnet" -}; -const sdk = await WasmSdk.new(transport, true); // true = enable proofs +const ok = sdk.validate_address('yXXX...', 'testnet'); +``` + +### Advanced: raw artifacts (separate .wasm) + +If you prefer to manage the `.wasm` file via your bundler, import the raw entry explicitly: + +```js +import init, * as sdk from '@dashevo/wasm-sdk/raw'; +await init(); // wasm-bindgen will resolve and fetch wasm_sdk_bg.wasm +``` -// Example query -const identity = await sdk.get_identity("GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"); +Configure your bundler accordingly. For webpack 5: + +```js +// webpack config +module.exports = { + experiments: { asyncWebAssembly: true }, + module: { rules: [{ test: /\.wasm$/, type: 'asset/resource' }] }, +}; ``` -## Documentation +--- + +## How bundling works + +The publishable build provides two ways to consume the SDK: + +1) Single file (default): `import '@dashevo/wasm-sdk'` +- `dist/sdk.js` inlines the Wasm (base64) to avoid asset pipelines and MIME issues. +- Browser: compiles the inlined bytes in a Web Worker to avoid main-thread stalls and 8MB sync limits; then instantiates on the main thread. +- Node: uses the inlined bytes with initSync internally; you still call `await init()` for a consistent API. +- The wrapper imports a sanitized variant of the wasm-bindgen glue so bundlers do not "see" any `new URL('…wasm')` and therefore will not emit a `.wasm` asset. + +2) Raw artifacts (opt-in): `import '@dashevo/wasm-sdk/raw'` +- `dist/raw/wasm_sdk.js` (unmodified wasm-bindgen output) plus `dist/raw/wasm_sdk_bg.wasm`. +- Your bundler must serve the `.wasm` with the correct content type and URL rewriting. + +Why this design? +- Eliminate flaky Wasm asset handling in test/dev and simplify consumer setup. +- Keep an escape hatch for asset-pipeline users. -- **[User Documentation](docs.html)**: Comprehensive guide for all queries and state transitions -- **[AI Reference](AI_REFERENCE.md)**: Quick reference optimized for AI assistants and developers -- **[Live Demo](index.html)**: Interactive interface to test all SDK functionality +--- -## Development +## Build (contributors) -### Building +Prerequisites: Rust toolchain, `wasm-pack`, and (optionally) Binaryen for optimized builds. -The SDK requires Rust and wasm-pack: +Commands (run at repo root or inside `packages/wasm-sdk`): ```bash -# Install wasm-pack if not already installed -curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh +# Development build (fast) +yarn workspace @dashevo/wasm-sdk build -# Build the WASM module -./build.sh +# Release build (optimized) +yarn workspace @dashevo/wasm-sdk build:release ``` +--- -### Documentation +## Test (contributors) -**IMPORTANT**: Documentation must be kept in sync with `index.html`. When adding or modifying queries/state transitions in `index.html`, you MUST update the documentation: +Unit tests (Node + browser/Karma): ```bash -# Regenerate documentation after changes to index.html -python3 generate_docs.py +yarn workspace @dashevo/wasm-sdk test:unit +``` + +Functional tests (networked; some cases may be skipped if offline): -# Check if documentation is up to date -python3 check_documentation.py +```bash +yarn workspace @dashevo/wasm-sdk test:functional ``` -The CI will fail if documentation is out of sync with the code. +--- -### Adding New Features +## API highlights -1. Add the query/transition definition to `index.html` -2. Implement the corresponding method in the Rust code -3. Regenerate documentation: `python3 generate_docs.py` -4. Test your changes using the web interface +Examples (after `await init()`): -### CI/CD +```js +// Addresses & keys +const { address, private_key_wif } = sdk.generate_key_pair('mainnet'); +sdk.validate_address(address, 'mainnet'); +const addr = sdk.pubkey_to_address('02...pubkeyHex', 'testnet'); -This package has automated checks for: -- Documentation completeness (all queries/transitions must be documented) -- Documentation freshness (docs must be regenerated when index.html changes) +// Mnemonics & derivation +const m = sdk.generate_mnemonic(12); +const r = sdk.derive_key_from_seed_with_path(m, undefined, "m/44'/5'/0'/0/0", 'mainnet'); -The checks run on: -- Pull requests that modify relevant files -- Pushes to master and release branches +// Network client +let b = sdk.WasmSdkBuilder.testnetTrusted(); +const client = await b.withSettings(5000, 10000, 3, true).withLogs('info').build(); +const status = await client.getStatus(); +client.free(); +``` + +The full surface includes identity, document, contract, token, group, epoch/system, and proof helpers. -## Architecture +### Logging -### File Structure +By default, the SDK is silent. You can enable tracing logs globally or via the builder: -- `src/`: Rust source code - - `queries/`: Query implementations - - `state_transitions/`: State transition implementations - - `sdk.rs`: Main SDK interface -- `index.html`: Interactive demo and test interface -- `docs.html`: User-friendly documentation -- `AI_REFERENCE.md`: Developer/AI reference documentation -- `generate_docs.py`: Documentation generator -- `check_documentation.py`: Documentation validation +```js +// Globally +await sdk.WasmSdk.setLogLevel('info'); -### Key Concepts +// Or on the builder (applies immediately) +await sdk.WasmSdkBuilder.new_testnet().withLogs('wasm_sdk=debug,rs_dapi_client=warn').build(); +``` -1. **Queries**: Read operations that don't modify state - - Identity queries (balance, keys, nonce) - - Document queries (with where/orderBy support) - - Data contract queries - - Token queries - - System queries +Accepted values are simple levels ('off'|'error'|'warn'|'info'|'debug'|'trace') or a full EnvFilter string. -2. **State Transitions**: Operations that modify platform state - - Identity operations (create, update, transfer) - - Document operations (create, update, delete) - - Token operations (mint, burn, transfer) - - Voting operations +--- -3. **Proofs**: Cryptographic proofs can be requested for most queries to verify data authenticity +## Environment & compatibility -## Testing +- ESM-only package ("type": "module"). Use dynamic import in CJS. +- Node.js: 16+ recommended (18+ preferred). +- Browsers: modern engines with WebAssembly + Web Workers. -The web interface (`index.html`) provides comprehensive testing capabilities: -- Network selection (mainnet/testnet) -- Query execution with parameter validation -- State transition testing with authentication -- Proof verification toggle +--- -## Contributing +## Troubleshooting -1. Make your changes -2. Update documentation if needed: `python3 generate_docs.py` -3. Run tests -4. Submit a pull request +- "expected magic word 00 61 73 6d …" in browsers: + - Ensure you import the default entry (`@dashevo/wasm-sdk`), not the raw one, unless you have configured Wasm assets. + - Always `await init()` before calling functions. -## License +- Karma/webpack serving errors: + - Not applicable with the default single-file build. If you opt into `@dashevo/wasm-sdk/raw`, configure a `.wasm` asset rule and correct MIME type. -See the main platform repository for license information. \ No newline at end of file +--- + +## Contributing & License + +This package is part of the Dash Platform monorepo. Follow the repository’s contribution guidelines and do not commit secrets. See the root repository for license details. + +--- + +## State transitions + +In addition to read-only queries, the SDK exposes helpers to construct and submit state transitions (requires a networked client and valid inputs): + +```js +import init, * as sdk from '@dashevo/wasm-sdk'; +await init(); + +const builder = sdk.WasmSdkBuilder.testnetTrusted(); +const client = await builder.build(); + +// Identity create (example β€” requires a valid proof and keys) +const proofJson = JSON.stringify({ /* platform-provided proof object */ }); +const assetLockWif = 'Kx...'; +const pubKeysJson = JSON.stringify([ + { keyType: 'ECDSA_SECP256K1', purpose: 'AUTHENTICATION', securityLevel: 'MASTER', privateKeyHex: '...' }, +]); +await sdk.identityCreate(proofJson, assetLockWif, pubKeysJson) + .then(() => {/* submitted */}) + .catch((e) => {/* handle error */}); + +// Token transfer (negative example if parameters are invalid) +await sdk.tokenTransfer('contractId', 0, '1000', 'senderIdentityId', 'recipientIdentityId', assetLockWif, null) + .catch(() => {}); + +client.free(); +``` diff --git a/packages/wasm-sdk/api-definitions.json b/packages/wasm-sdk/api-definitions.json deleted file mode 100644 index 4164a7182eb..00000000000 --- a/packages/wasm-sdk/api-definitions.json +++ /dev/null @@ -1,2196 +0,0 @@ -{ - "version": "1.0.3", - "generated_at": "2025-08-13T17:10:00.000000", - "source": "index.html", - "queries": { - "identity": { - "label": "Identity Queries", - "queries": { - "getIdentity": { - "label": "Get Identity", - "description": "Fetch an identity by its identifier", - "inputs": [ - { - "name": "id", - "type": "text", - "label": "Identity ID", - "required": true - } - ] - }, - "getIdentityKeys": { - "label": "Get Identity Keys", - "description": "Retrieve keys associated with an identity", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - }, - { - "name": "keyRequestType", - "type": "select", - "label": "Key Request Type", - "required": false, - "options": [ - { - "value": "all", - "label": "All Keys (AllKeys {}) - Get all keys for the identity" - }, - { - "value": "specific", - "label": "Specific Keys (SpecificKeys with key_ids) - Get specific keys by ID [🚧 Work in Progress]" - }, - { - "value": "search", - "label": "Search Keys (SearchKey with purpose_map) - Search by purpose and security level [🚧 Work in Progress]" - } - ] - }, - { - "name": "specificKeyIds", - "type": "array", - "label": "Specific Key IDs (required for 'specific' type)", - "required": false, - "placeholder": "0,1,2" - }, - { - "name": "searchPurposeMap", - "type": "text", - "label": "Search Purpose Map JSON (required for 'search' type)", - "required": false, - "placeholder": "{\"0\": {\"0\": \"current\"}, \"1\": {\"0\": \"all\"}}" - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "offset", - "type": "number", - "label": "Offset", - "required": false - } - ] - }, - "getIdentitiesContractKeys": { - "label": "Get Identities Contract Keys", - "description": "Get keys for multiple identities related to a specific contract", - "inputs": [ - { - "name": "identitiesIds", - "type": "array", - "label": "Identity IDs", - "required": true - }, - { - "name": "contractId", - "type": "text", - "label": "Contract ID", - "required": true - }, - { - "name": "purposes", - "type": "multiselect", - "label": "Key Purposes", - "required": false, - "options": [ - { - "value": "0", - "label": "Authentication" - }, - { - "value": "1", - "label": "Encryption" - }, - { - "value": "2", - "label": "Decryption" - }, - { - "value": "3", - "label": "Transfer" - }, - { - "value": "5", - "label": "Voting" - } - ] - } - ] - }, - "getIdentityNonce": { - "label": "Get Identity Nonce", - "description": "Get the current nonce for an identity", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - } - ] - }, - "getIdentityContractNonce": { - "label": "Get Identity Contract Nonce", - "description": "Get the nonce for an identity in relation to a specific contract", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - }, - { - "name": "contractId", - "type": "text", - "label": "Contract ID", - "required": true - } - ] - }, - "getIdentityBalance": { - "label": "Get Identity Balance", - "description": "Get the credit balance of an identity", - "inputs": [ - { - "name": "id", - "type": "text", - "label": "Identity ID", - "required": true - } - ] - }, - "getIdentitiesBalances": { - "label": "Get Identities Balances", - "description": "Get balances for multiple identities", - "inputs": [ - { - "name": "ids", - "type": "array", - "label": "Identity IDs", - "required": true - } - ] - }, - "getIdentityBalanceAndRevision": { - "label": "Get Identity Balance and Revision", - "description": "Get both balance and revision number for an identity", - "inputs": [ - { - "name": "id", - "type": "text", - "label": "Identity ID", - "required": true - } - ] - }, - "getIdentityByPublicKeyHash": { - "label": "Get Identity by Unique Public Key Hash", - "description": "Find an identity by its unique public key hash", - "inputs": [ - { - "name": "publicKeyHash", - "type": "text", - "label": "Public Key Hash", - "required": true, - "placeholder": "b7e904ce25ed97594e72f7af0e66f298031c1754" - } - ] - }, - "getIdentityByNonUniquePublicKeyHash": { - "label": "Get Identity by Non-Unique Public Key Hash", - "description": "Find identities by non-unique public key hash", - "inputs": [ - { - "name": "publicKeyHash", - "type": "text", - "label": "Public Key Hash", - "required": true, - "placeholder": "518038dc858461bcee90478fd994bba8057b7531" - }, - { - "name": "startAfter", - "type": "text", - "label": "Start After", - "required": false - } - ] - }, - "getIdentityTokenBalances": { - "label": "Get Identity Token Balances", - "description": "Get token balances for an identity", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - }, - { - "name": "tokenIds", - "type": "array", - "label": "Token IDs", - "required": true - } - ] - }, - "getIdentitiesTokenBalances": { - "label": "Get Identities Token Balances", - "description": "Get token balance for multiple identities", - "inputs": [ - { - "name": "identityIds", - "type": "array", - "label": "Identity IDs", - "required": true - }, - { - "name": "tokenId", - "type": "text", - "label": "Token ID", - "required": true, - "placeholder": "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv" - } - ] - }, - "getIdentityTokenInfos": { - "label": "Get Identity Token Info", - "description": "Get token information for an identity's tokens", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - }, - { - "name": "tokenIds", - "type": "array", - "label": "Token IDs (optional)", - "required": false, - "placeholder": "[\"Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv\"]" - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "offset", - "type": "number", - "label": "Offset", - "required": false - } - ] - }, - "getIdentitiesTokenInfos": { - "label": "Get Identities Token Info", - "description": "Get token information for multiple identities with a specific token", - "inputs": [ - { - "name": "identityIds", - "type": "array", - "label": "Identity IDs", - "required": true - }, - { - "name": "tokenId", - "type": "text", - "label": "Token ID", - "required": true, - "placeholder": "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv" - } - ] - } - } - }, - "dataContract": { - "label": "Data Contract Queries", - "queries": { - "getDataContract": { - "label": "Get Data Contract", - "description": "Fetch a data contract by its identifier", - "inputs": [ - { - "name": "id", - "type": "text", - "label": "Data Contract ID", - "required": true, - "placeholder": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" - } - ] - }, - "getDataContractHistory": { - "label": "Get Data Contract History", - "description": "Get the version history of a data contract", - "inputs": [ - { - "name": "id", - "type": "text", - "label": "Data Contract ID", - "required": true, - "placeholder": "HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM" - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "offset", - "type": "number", - "label": "Offset", - "required": false - }, - { - "name": "startAtMs", - "type": "number", - "label": "Start At Timestamp (ms)", - "required": false - } - ] - }, - "getDataContracts": { - "label": "Get Data Contracts", - "description": "Fetch multiple data contracts by their identifiers", - "inputs": [ - { - "name": "ids", - "type": "array", - "label": "Data Contract IDs", - "required": true, - "placeholder": "[\"GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec\", \"ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A\"]" - } - ] - } - } - }, - "document": { - "label": "Document Queries", - "queries": { - "getDocuments": { - "label": "Get Documents", - "description": "Query documents from a data contract", - "inputs": [ - { - "name": "dataContractId", - "type": "text", - "label": "Data Contract ID", - "required": true, - "placeholder": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true, - "placeholder": "domain" - }, - { - "name": "where", - "type": "json", - "label": "Where Clause (JSON)", - "required": false, - "placeholder": "[[\"normalizedParentDomainName\", \"==\", \"dash\"], [\"normalizedLabel\", \"==\", \"therea1s11mshaddy5\"]]" - }, - { - "name": "orderBy", - "type": "json", - "label": "Order By (JSON)", - "required": false, - "placeholder": "[[\"$createdAt\", \"desc\"]]" - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - } - ] - }, - "getDocument": { - "label": "Get Document", - "description": "Fetch a specific document by ID", - "inputs": [ - { - "name": "dataContractId", - "type": "text", - "label": "Data Contract ID", - "required": true, - "placeholder": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true, - "placeholder": "domain" - }, - { - "name": "documentId", - "type": "text", - "label": "Document ID", - "required": true, - "placeholder": "7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r" - } - ] - } - } - }, - "dpns": { - "label": "DPNS Queries", - "queries": { - "getDpnsUsername": { - "label": "Get DPNS Usernames", - "description": "Get DPNS usernames for an identity", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - } - ] - }, - "dpnsCheckAvailability": { - "label": "DPNS Check Availability", - "description": "Check if a DPNS username is available", - "inputs": [ - { - "name": "label", - "type": "text", - "label": "Label (Username)", - "required": true - } - ] - }, - "dpnsResolve": { - "label": "DPNS Resolve Name", - "description": "Resolve a DPNS name to an identity ID", - "inputs": [ - { - "name": "name", - "type": "text", - "label": "Name", - "required": true - } - ] - }, - "dpnsSearch": { - "label": "DPNS Search Name", - "description": "Search for DPNS names that start with a given prefix", - "inputs": [ - { - "name": "prefix", - "type": "text", - "label": "Name Prefix", - "required": true, - "placeholder": "Enter prefix (e.g., ali)" - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false, - "placeholder": "Default: 10" - } - ] - } - } - }, - "voting": { - "label": "Voting & Contested Resources", - "queries": { - "getContestedResources": { - "label": "Get Contested Resources", - "description": "Get list of contested resources", - "inputs": [ - { - "name": "documentTypeName", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "dataContractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "indexName", - "type": "text", - "label": "Index Name", - "required": true - }, - { - "name": "startAtValue", - "type": "text", - "label": "Start At Value", - "required": false - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "offset", - "type": "number", - "label": "Offset", - "required": false - }, - { - "name": "orderAscending", - "type": "checkbox", - "label": "Order Ascending", - "required": false - } - ] - }, - "getContestedResourceVoteState": { - "label": "Get Contested Resource Vote State", - "description": "Get the current vote state for a contested resource", - "inputs": [ - { - "name": "dataContractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentTypeName", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "indexName", - "type": "text", - "label": "Index Name", - "required": true - }, - { - "name": "indexValues", - "type": "array", - "label": "Index Values", - "required": true, - "placeholder": "[\"dash\", \"alice\"]" - }, - { - "name": "resultType", - "type": "text", - "label": "Result Type", - "required": true - }, - { - "name": "allowIncludeLockedAndAbstainingVoteTally", - "type": "checkbox", - "label": "Allow Include Locked and Abstaining Vote Tally", - "required": false - }, - { - "name": "startAtIdentifierInfo", - "type": "text", - "label": "Start At Identifier Info", - "required": false - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": false - }, - { - "name": "orderAscending", - "type": "checkbox", - "label": "Order Ascending", - "required": false - } - ] - }, - "getContestedResourceVotersForIdentity": { - "label": "Get Contested Resource Voters for Identity", - "description": "Get voters who voted for a specific identity in a contested resource", - "inputs": [ - { - "name": "dataContractId", - "type": "text", - "label": "Contract ID", - "required": true - }, - { - "name": "documentTypeName", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "indexName", - "type": "text", - "label": "Index Name", - "required": true - }, - { - "name": "indexValues", - "type": "array", - "label": "Index Values", - "required": true, - "placeholder": "[\"dash\", \"alice\"]" - }, - { - "name": "contestantId", - "type": "text", - "label": "Contestant Identity ID", - "required": true - }, - { - "name": "startAtIdentifierInfo", - "type": "text", - "label": "Start At Identifier Info", - "required": false - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": false, - "placeholder": "Default: 100" - }, - { - "name": "orderAscending", - "type": "checkbox", - "label": "Order Ascending", - "required": false - } - ] - }, - "getContestedResourceIdentityVotes": { - "label": "Get Contested Resource Identity Votes", - "description": "Get all votes cast by a specific identity", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "offset", - "type": "number", - "label": "Offset", - "required": false - }, - { - "name": "orderAscending", - "type": "checkbox", - "label": "Order Ascending", - "required": false - } - ] - }, - "getVotePollsByEndDate": { - "label": "Get Vote Polls by End Date", - "description": "Get vote polls within a time range", - "inputs": [ - { - "name": "startTimeMs", - "type": "number", - "label": "Start Time (ms)", - "required": false, - "placeholder": "Timestamp in milliseconds as string (e.g., 1650000000000)" - }, - { - "name": "endTimeMs", - "type": "number", - "label": "End Time (ms)", - "required": false, - "placeholder": "Timestamp in milliseconds as string (e.g., 1650086400000)" - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "offset", - "type": "number", - "label": "Offset", - "required": false - }, - { - "name": "orderAscending", - "type": "checkbox", - "label": "Order Ascending", - "required": false - } - ] - } - } - }, - "protocol": { - "label": "Protocol & Version", - "queries": { - "getProtocolVersionUpgradeState": { - "label": "Get Protocol Version Upgrade State", - "description": "Get the current state of protocol version upgrades", - "inputs": [] - }, - "getProtocolVersionUpgradeVoteStatus": { - "label": "Get Protocol Version Upgrade Vote Status", - "description": "Get voting status for protocol version upgrades", - "inputs": [ - { - "name": "startProTxHash", - "type": "text", - "label": "Start ProTx Hash", - "required": true, - "placeholder": "143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113" - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": true - } - ] - } - } - }, - "epoch": { - "label": "Epoch & Block", - "queries": { - "getEpochsInfo": { - "label": "Get Epochs Info", - "description": "Get information about epochs", - "inputs": [ - { - "name": "startEpoch", - "type": "number", - "label": "Start Epoch", - "required": true - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": true - }, - { - "name": "ascending", - "type": "checkbox", - "label": "Ascending Order", - "required": false - } - ] - }, - "getCurrentEpoch": { - "label": "Get Current Epoch", - "description": "Get information about the current epoch", - "inputs": [] - }, - "getFinalizedEpochInfos": { - "label": "Get Finalized Epoch Info", - "description": "Get information about finalized epochs", - "inputs": [ - { - "name": "startEpoch", - "type": "number", - "label": "Start Epoch", - "required": true - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": true - }, - { - "name": "ascending", - "type": "checkbox", - "label": "Ascending Order", - "required": false - } - ] - }, - "getEvonodesProposedEpochBlocksByIds": { - "label": "Get Evonodes Proposed Epoch Blocks by IDs", - "description": "Get proposed blocks by evonode IDs", - "supportsProof": false, - "inputs": [ - { - "name": "epoch", - "type": "number", - "label": "Epoch", - "required": true - }, - { - "name": "ids", - "type": "array", - "label": "ProTx Hashes", - "required": true, - "placeholder": "[\"143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113\"]" - } - ] - }, - "getEvonodesProposedEpochBlocksByRange": { - "label": "Get Evonodes Proposed Epoch Blocks by Range", - "description": "Get proposed blocks by range", - "supportsProof": false, - "inputs": [ - { - "name": "epoch", - "type": "number", - "label": "Epoch", - "required": true - }, - { - "name": "limit", - "type": "number", - "label": "Limit", - "required": false - }, - { - "name": "startAfter", - "type": "text", - "label": "Start After (Evonode ID)", - "required": false, - "placeholder": "143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113" - }, - { - "name": "orderAscending", - "type": "checkbox", - "label": "Order Ascending", - "required": false - } - ] - } - } - }, - "token": { - "label": "Token Queries", - "queries": { - "getTokenStatuses": { - "label": "Get Token Statuses", - "description": "Get token statuses", - "inputs": [ - { - "name": "tokenIds", - "type": "array", - "label": "Token IDs", - "required": true - } - ] - }, - "getTokenDirectPurchasePrices": { - "label": "Get Token Direct Purchase Prices", - "description": "Get direct purchase prices for tokens", - "inputs": [ - { - "name": "tokenIds", - "type": "array", - "label": "Token IDs", - "required": true - } - ] - }, - "getTokenContractInfo": { - "label": "Get Token Contract Info", - "description": "Get information about a token contract", - "inputs": [ - { - "name": "dataContractId", - "type": "text", - "label": "Data Contract ID", - "required": true, - "placeholder": "EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta" - } - ] - }, - "getTokenPerpetualDistributionLastClaim": { - "label": "Get Token Perpetual Distribution Last Claim", - "description": "Get last claim information for perpetual distribution", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true - }, - { - "name": "tokenId", - "type": "text", - "label": "Token ID", - "required": true - } - ] - }, - "getTokenTotalSupply": { - "label": "Get Token Total Supply", - "description": "Get total supply of a token", - "inputs": [ - { - "name": "tokenId", - "type": "text", - "label": "Token ID", - "required": true, - "placeholder": "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv" - } - ] - } - } - }, - "group": { - "label": "Group Queries", - "queries": { - "getGroupInfo": { - "label": "Get Group Info", - "description": "Get information about a group", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Contract ID", - "required": true - }, - { - "name": "groupContractPosition", - "type": "number", - "label": "Group Contract Position", - "required": true - } - ] - }, - "getGroupInfos": { - "label": "Get Group Infos", - "description": "Get information about multiple groups", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Contract ID", - "required": true - }, - { - "name": "startAtGroupContractPosition", - "type": "number", - "label": "Start at Position", - "required": false - }, - { - "name": "startGroupContractPositionIncluded", - "type": "checkbox", - "label": "Include Start Position", - "required": false - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": false - } - ] - }, - "getGroupActions": { - "label": "Get Group Actions", - "description": "Get actions for a group", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Contract ID", - "required": true - }, - { - "name": "groupContractPosition", - "type": "number", - "label": "Group Contract Position", - "required": true - }, - { - "name": "status", - "type": "select", - "label": "Status", - "required": true, - "options": [ - { - "value": "ACTIVE", - "label": "Active" - }, - { - "value": "CLOSED", - "label": "Closed" - } - ] - }, - { - "name": "startActionId", - "type": "text", - "label": "Start Action ID", - "required": false - }, - { - "name": "startActionIdIncluded", - "type": "checkbox", - "label": "Include Start Action", - "required": false - }, - { - "name": "count", - "type": "number", - "label": "Count", - "required": false - } - ] - }, - "getGroupActionSigners": { - "label": "Get Group Action Signers", - "description": "Get signers for a group action", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Contract ID", - "required": true - }, - { - "name": "groupContractPosition", - "type": "number", - "label": "Group Contract Position", - "required": true - }, - { - "name": "status", - "type": "select", - "label": "Status", - "required": true, - "options": [ - { - "value": "ACTIVE", - "label": "Active" - }, - { - "value": "CLOSED", - "label": "Closed" - } - ] - }, - { - "name": "actionId", - "type": "text", - "label": "Action ID", - "required": true - } - ] - } - } - }, - "system": { - "label": "System & Utility", - "queries": { - "getStatus": { - "label": "Get Status", - "description": "Get system status", - "supportsProof": false, - "inputs": [] - }, - "getCurrentQuorumsInfo": { - "label": "Get Current Quorums Info", - "description": "Get information about current quorums", - "supportsProof": false, - "inputs": [] - }, - "getPrefundedSpecializedBalance": { - "label": "Get Prefunded Specialized Balance", - "description": "Get prefunded specialized balance", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Specialized Balance ID", - "required": true, - "placeholder": "AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT" - } - ] - }, - "getTotalCreditsInPlatform": { - "label": "Get Total Credits in Platform", - "description": "Get total credits in the platform", - "inputs": [] - }, - "getPathElements": { - "label": "Get Path Elements", - "description": "Access any data in the Dash Platform state tree. This low-level query allows direct access to GroveDB storage by specifying a path through the tree structure and keys to retrieve at that path. Common paths include: Identities (32), Tokens (96), DataContractDocuments (64), Balances (16), Votes (80), and more.", - "inputs": [ - { - "name": "path", - "type": "array", - "label": "Path", - "required": true - }, - { - "name": "keys", - "type": "array", - "label": "Keys", - "required": true - } - ] - }, - "waitForStateTransitionResult": { - "label": "Wait for State Transition Result", - "description": "Internal query to wait for and retrieve the result of a previously submitted state transition", - "supportsProof": false, - "inputs": [ - { - "name": "stateTransitionHash", - "type": "text", - "label": "State Transition Hash", - "required": true - } - ] - } - } - } - }, - "transitions": { - "identity": { - "label": "Identity Transitions", - "transitions": { - "identityCreate": { - "label": "Identity Create", - "description": "Create a new identity with initial credits", - "inputs": [ - { - "name": "seedPhrase", - "type": "textarea", - "label": "Seed Phrase", - "required": true, - "placeholder": "Enter seed phrase (12-24 words) or click Generate", - "help": "The wallet seed phrase that will be used to derive identity keys" - }, - { - "name": "generateSeedButton", - "type": "button", - "label": "Generate New Seed", - "action": "generateTestSeed" - }, - { - "name": "identityIndex", - "type": "number", - "label": "Identity Index", - "required": true, - "value": 0, - "min": 0, - "max": 999, - "help": "The identity index is an internal reference within the wallet. Leave as 0 for first identity." - }, - { - "name": "keySelectionMode", - "type": "select", - "label": "Key Selection Mode", - "required": true, - "value": "default", - "options": [ - { - "value": "default", - "label": "Default (Recommended)" - }, - { - "value": "advanced", - "label": "Advanced" - } - ], - "help": "Default adds standard keys for most Platform operations. Advanced allows custom key configuration." - }, - { - "name": "keyType", - "type": "select", - "label": "Key Type", - "required": true, - "value": "ECDSA_HASH160", - "options": [ - { - "value": "ECDSA_HASH160", - "label": "ECDSA_HASH160 (Dash Evo Tool default)" - }, - { - "value": "ECDSA_SECP256K1", - "label": "ECDSA_SECP256K1 (Dash mobile wallet default)" - } - ], - "help": "Choose key type" - }, - { - "name": "keyPreview", - "type": "keyPreview", - "label": "Keys to be added", - "help": "These keys will be added to your new identity" - } - ], - "sdk_params": [ - { - "name": "assetLockProof", - "type": "string", - "label": "Asset Lock Proof", - "required": true, - "description": "Hex-encoded JSON asset lock proof" - }, - { - "name": "assetLockProofPrivateKey", - "type": "string", - "label": "Asset Lock Proof Private Key", - "required": true, - "description": "WIF format private key" - }, - { - "name": "publicKeys", - "type": "string", - "label": "Public Keys", - "required": true, - "description": "JSON array of public keys. Key requirements: ECDSA_SECP256K1 requires privateKeyHex or privateKeyWif for signing, BLS12_381 requires privateKeyHex for signing, ECDSA_HASH160 requires either the data field (base64-encoded 20-byte public key hash) or privateKeyHex (produces empty signatures)." - } - ], - "sdk_example": "// Asset lock proof is a hex-encoded JSON object\nconst assetLockProof = \"a9147d3b... (hex-encoded)\";\nconst assetLockProofPrivateKey = \"XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1\"; // WIF format\n\n// Public keys array with proper key types and private keys for signing/hashing\nconst publicKeys = JSON.stringify([\n {\n id: 0,\n keyType: \"ECDSA_SECP256K1\",\n purpose: \"AUTHENTICATION\",\n securityLevel: \"MASTER\",\n data: \"A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ\", // Base64-encoded public key\n readOnly: false,\n privateKeyWif: \"XBrZJKcW4ajWVNAU6yP87WQog6CjFnpbqyAKgNTZRqmhYvPgMNV2\"\n },\n {\n id: 1,\n keyType: \"ECDSA_HASH160\",\n purpose: \"AUTHENTICATION\",\n securityLevel: \"HIGH\",\n data: \"ripemd160hash20bytes1234\", // Base64-encoded 20-byte RIPEMD160 hash\n readOnly: false,\n // ECDSA_HASH160 keys produce empty signatures (not required/used for signing)\n }\n]);\n\nconst result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);" - }, - "identityTopUp": { - "label": "Identity Top Up", - "description": "Add credits to an existing identity", - "inputs": [ - { - "name": "identityId", - "type": "text", - "label": "Identity ID", - "required": true, - "placeholder": "Enter the identity ID to top up (base58)", - "help": "The identity ID that will receive the credits from the asset lock proof" - } - ], - "sdk_params": [ - { - "name": "identityId", - "type": "string", - "label": "Identity ID", - "required": true, - "description": "Base58 format identity ID" - }, - { - "name": "assetLockProof", - "type": "string", - "label": "Asset Lock Proof", - "required": true, - "description": "Hex-encoded JSON asset lock proof" - }, - { - "name": "assetLockProofPrivateKey", - "type": "string", - "label": "Asset Lock Proof Private Key", - "required": true, - "description": "WIF format private key" - } - ], - "sdk_example": "const identityId = \"5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk\"; // base58\nconst assetLockProof = \"a9147d3b... (hex-encoded)\";\nconst assetLockProofPrivateKey = \"XFfpaSbZq52HPy3WWve1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1\"; // WIF format\n\nconst result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey);" - }, - "identityUpdate": { - "label": "Identity Update", - "description": "Update identity keys (add or disable)", - "inputs": [ - { - "name": "addPublicKeys", - "type": "textarea", - "label": "Keys to Add (JSON array)", - "required": false, - "placeholder": "[{\"keyType\":\"ECDSA_HASH160\",\"purpose\":\"AUTHENTICATION\",\"data\":\"base64_key_data\"}]" - }, - { - "name": "disablePublicKeys", - "type": "text", - "label": "Key IDs to Disable (comma-separated)", - "required": false, - "placeholder": "2,3,5" - } - ] - }, - "identityCreditTransfer": { - "label": "Identity Credit Transfer", - "description": "Transfer credits between identities", - "inputs": [ - { - "name": "recipientId", - "type": "text", - "label": "Recipient Identity ID", - "required": true - }, - { - "name": "amount", - "type": "number", - "label": "Amount (credits)", - "required": true - } - ] - }, - "identityCreditWithdrawal": { - "label": "Identity Credit Withdrawal", - "description": "Withdraw credits from identity to Dash address", - "inputs": [ - { - "name": "toAddress", - "type": "text", - "label": "Dash Address", - "required": true - }, - { - "name": "amount", - "type": "number", - "label": "Amount (credits)", - "required": true - }, - { - "name": "coreFeePerByte", - "type": "number", - "label": "Core Fee Per Byte (optional)", - "required": false - } - ] - } - } - }, - "dataContract": { - "label": "Data Contract Transitions", - "transitions": { - "dataContractCreate": { - "label": "Data Contract Create", - "description": "Create a new data contract", - "inputs": [ - { - "name": "canBeDeleted", - "type": "checkbox", - "label": "Can Be Deleted", - "required": false - }, - { - "name": "readonly", - "type": "checkbox", - "label": "Read Only", - "required": false - }, - { - "name": "keepsHistory", - "type": "checkbox", - "label": "Keeps History", - "required": false - }, - { - "name": "documentsKeepHistoryContractDefault", - "type": "checkbox", - "label": "Documents Keep History (Default)", - "required": false - }, - { - "name": "documentsMutableContractDefault", - "type": "checkbox", - "label": "Documents Mutable (Default)", - "required": false, - "defaultValue": true - }, - { - "name": "documentsCanBeDeletedContractDefault", - "type": "checkbox", - "label": "Documents Can Be Deleted (Default)", - "required": false, - "defaultValue": true - }, - { - "name": "requiresIdentityEncryptionBoundedKey", - "type": "text", - "label": "Requires Identity Encryption Key (optional)", - "required": false - }, - { - "name": "requiresIdentityDecryptionBoundedKey", - "type": "text", - "label": "Requires Identity Decryption Key (optional)", - "required": false - }, - { - "name": "documentSchemas", - "type": "json", - "label": "Document Schemas JSON", - "required": true, - "placeholder": "{\n \"note\": {\n \"type\": \"object\",\n \"properties\": {\n \"message\": {\n \"type\": \"string\",\n \"maxLength\": 100,\n \"position\": 0\n }\n },\n \"required\": [\"message\"],\n \"additionalProperties\": false\n }\n}" - }, - { - "name": "groups", - "type": "json", - "label": "Groups (optional)", - "required": false, - "placeholder": "{}" - }, - { - "name": "tokens", - "type": "json", - "label": "Tokens (optional)", - "required": false, - "placeholder": "{}" - }, - { - "name": "keywords", - "type": "text", - "label": "Keywords (comma separated, optional)", - "required": false - }, - { - "name": "description", - "type": "text", - "label": "Description (optional)", - "required": false - } - ] - }, - "dataContractUpdate": { - "label": "Data Contract Update", - "description": "Add document types, groups, or tokens to an existing data contract", - "inputs": [ - { - "name": "dataContractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "newDocumentSchemas", - "type": "json", - "label": "New Document Schemas to Add (optional)", - "required": false, - "placeholder": "{\n \"newType\": {\n \"type\": \"object\",\n \"properties\": {\n \"field\": {\n \"type\": \"string\",\n \"maxLength\": 100,\n \"position\": 0\n }\n },\n \"required\": [\"field\"],\n \"additionalProperties\": false\n }\n}" - }, - { - "name": "newGroups", - "type": "json", - "label": "New Groups to Add (optional)", - "required": false, - "placeholder": "{}" - }, - { - "name": "newTokens", - "type": "json", - "label": "New Tokens to Add (optional)", - "required": false, - "placeholder": "{}" - } - ] - } - } - }, - "document": { - "label": "Document Transitions", - "transitions": { - "documentCreate": { - "label": "Document Create", - "description": "Create a new document", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "fetchSchema", - "type": "button", - "label": "Fetch Schema", - "action": "fetchDocumentSchema" - }, - { - "name": "documentFields", - "type": "dynamic", - "label": "Document Fields" - } - ] - }, - "documentReplace": { - "label": "Document Replace", - "description": "Replace an existing document", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "documentId", - "type": "text", - "label": "Document ID", - "required": true - }, - { - "name": "loadDocument", - "type": "button", - "label": "Load Document", - "action": "loadExistingDocument" - }, - { - "name": "documentFields", - "type": "dynamic", - "label": "Document Fields" - } - ] - }, - "documentDelete": { - "label": "Document Delete", - "description": "Delete an existing document", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "documentId", - "type": "text", - "label": "Document ID", - "required": true - } - ] - }, - "documentTransfer": { - "label": "Document Transfer", - "description": "Transfer document ownership", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "documentId", - "type": "text", - "label": "Document ID", - "required": true - }, - { - "name": "recipientId", - "type": "text", - "label": "Recipient Identity ID", - "required": true - } - ] - }, - "documentPurchase": { - "label": "Document Purchase", - "description": "Purchase a document", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "documentId", - "type": "text", - "label": "Document ID", - "required": true - }, - { - "name": "price", - "type": "number", - "label": "Price (credits)", - "required": true - } - ] - }, - "documentSetPrice": { - "label": "Document Set Price", - "description": "Set or update document price", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "documentType", - "type": "text", - "label": "Document Type", - "required": true - }, - { - "name": "documentId", - "type": "text", - "label": "Document ID", - "required": true - }, - { - "name": "price", - "type": "number", - "label": "Price (credits, 0 to remove)", - "required": true - } - ] - }, - "dpnsRegister": { - "label": "DPNS Register Name", - "description": "Register a new DPNS username", - "inputs": [ - { - "name": "label", - "type": "text", - "label": "Username", - "required": true, - "placeholder": "Enter username (e.g., alice)", - "validateOnType": true - } - ] - } - } - }, - "token": { - "label": "Token Transitions", - "transitions": { - "tokenBurn": { - "label": "Token Burn", - "description": "Burn tokens", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "amount", - "type": "text", - "label": "Amount to Burn", - "required": true - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenMint": { - "label": "Token Mint", - "description": "Mint new tokens", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "amount", - "type": "text", - "label": "Amount to Mint", - "required": true - }, - { - "name": "issuedToIdentityId", - "type": "text", - "label": "Issue To Identity ID", - "required": false - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenClaim": { - "label": "Token Claim", - "description": "Claim tokens from a distribution", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "distributionType", - "type": "select", - "label": "Distribution Type", - "required": true, - "options": [ - { - "value": "perpetual", - "label": "Perpetual" - }, - { - "value": "preprogrammed", - "label": "Pre-programmed" - } - ] - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenSetPriceForDirectPurchase": { - "label": "Token Set Price", - "description": "Set or update the price for direct token purchases", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "priceType", - "type": "select", - "label": "Price Type", - "required": true, - "options": [ - { - "value": "single", - "label": "Single Price" - }, - { - "value": "tiered", - "label": "Tiered Pricing" - } - ] - }, - { - "name": "priceData", - "type": "text", - "label": "Price Data (single price or JSON map)", - "required": false, - "placeholder": "Leave empty to remove pricing" - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenDirectPurchase": { - "label": "Token Direct Purchase", - "description": "Purchase tokens directly at the configured price", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "amount", - "type": "text", - "label": "Amount to Purchase", - "required": true - }, - { - "name": "totalAgreedPrice", - "type": "text", - "label": "Total Agreed Price (in credits) - Optional, fetches from pricing schedule if not provided", - "required": false - } - ] - }, - "tokenConfigUpdate": { - "label": "Token Config Update", - "description": "Update token configuration settings", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "configItemType", - "type": "select", - "label": "Config Item Type", - "required": true, - "options": [ - { - "value": "conventions", - "label": "Conventions" - }, - { - "value": "max_supply", - "label": "Max Supply" - }, - { - "value": "perpetual_distribution", - "label": "Perpetual Distribution" - }, - { - "value": "new_tokens_destination_identity", - "label": "New Tokens Destination Identity" - }, - { - "value": "minting_allow_choosing_destination", - "label": "Minting Allow Choosing Destination" - }, - { - "value": "manual_minting", - "label": "Manual Minting" - }, - { - "value": "manual_burning", - "label": "Manual Burning" - }, - { - "value": "conventions_control_group", - "label": "Conventions Control Group" - }, - { - "value": "conventions_admin_group", - "label": "Conventions Admin Group" - }, - { - "value": "max_supply_control_group", - "label": "Max Supply Control Group" - }, - { - "value": "max_supply_admin_group", - "label": "Max Supply Admin Group" - } - ] - }, - { - "name": "configValue", - "type": "text", - "label": "Config Value (JSON or specific value)", - "required": true - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenTransfer": { - "label": "Token Transfer", - "description": "Transfer tokens between identities", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "amount", - "type": "text", - "label": "Amount to Transfer", - "required": true - }, - { - "name": "recipientId", - "type": "text", - "label": "Recipient Identity ID", - "required": true - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenFreeze": { - "label": "Token Freeze", - "description": "Freeze tokens for a specific identity", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "identityToFreeze", - "type": "text", - "label": "Identity ID to Freeze", - "required": true - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenUnfreeze": { - "label": "Token Unfreeze", - "description": "Unfreeze tokens for a specific identity", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "identityToUnfreeze", - "type": "text", - "label": "Identity ID to Unfreeze", - "required": true - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - }, - "tokenDestroyFrozen": { - "label": "Token Destroy Frozen", - "description": "Destroy frozen tokens", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true - }, - { - "name": "tokenPosition", - "type": "number", - "label": "Token Contract Position", - "required": true - }, - { - "name": "frozenIdentityId", - "type": "text", - "label": "Identity ID whose frozen tokens to destroy", - "required": true - }, - { - "name": "publicNote", - "type": "text", - "label": "Public Note", - "required": false - } - ] - } - } - }, - "voting": { - "label": "Voting Transitions", - "transitions": { - "dpnsUsername": { - "label": "DPNS Username", - "description": "Cast a vote for a contested DPNS username", - "inputs": [ - { - "name": "contestedUsername", - "type": "text", - "label": "Contested Username", - "required": true, - "placeholder": "Enter the contested username (e.g., 'myusername')" - }, - { - "name": "voteChoice", - "type": "select", - "label": "Vote Choice", - "required": true, - "options": [ - { - "value": "abstain", - "label": "Abstain" - }, - { - "value": "lock", - "label": "Lock (Give to no one)" - }, - { - "value": "towardsIdentity", - "label": "Vote for Identity" - } - ] - }, - { - "name": "targetIdentity", - "type": "text", - "label": "Target Identity ID (if voting for identity)", - "required": false, - "placeholder": "Identity ID to vote for", - "dependsOn": { - "field": "voteChoice", - "value": "towardsIdentity" - } - } - ] - }, - "masternodeVote": { - "label": "Contested Resource", - "description": "Cast a vote for contested resources as a masternode", - "inputs": [ - { - "name": "contractId", - "type": "text", - "label": "Data Contract ID", - "required": true, - "placeholder": "Contract ID containing the contested resource" - }, - { - "name": "fetchContestedResources", - "type": "button", - "label": "Get Contested Resources", - "action": "fetchContestedResources" - }, - { - "name": "contestedResourceDropdown", - "type": "dynamic", - "label": "Contested Resources" - }, - { - "name": "voteChoice", - "type": "select", - "label": "Vote Choice", - "required": true, - "options": [ - { - "value": "abstain", - "label": "Abstain" - }, - { - "value": "lock", - "label": "Lock (Give to no one)" - }, - { - "value": "towardsIdentity", - "label": "Vote for Identity" - } - ] - }, - { - "name": "targetIdentity", - "type": "text", - "label": "Target Identity ID (if voting for identity)", - "required": false, - "placeholder": "Identity ID to vote for", - "dependsOn": { - "field": "voteChoice", - "value": "towardsIdentity" - } - } - ] - } - } - } - } -} \ No newline at end of file diff --git a/packages/wasm-sdk/check_documentation.py b/packages/wasm-sdk/check_documentation.py deleted file mode 100755 index 1c766b3229c..00000000000 --- a/packages/wasm-sdk/check_documentation.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -""" -Check that all queries and state transitions in api-definitions.json are documented -""" - -import os -import sys -import json -from pathlib import Path -from datetime import datetime, timezone - -def check_documentation_completeness(): - """Check if documentation is up to date with api-definitions.json""" - - script_dir = Path(__file__).parent - - # Required files - api_definitions_file = script_dir / 'api-definitions.json' - manifest_file = script_dir / 'docs_manifest.json' - docs_file = script_dir / 'docs.html' - ai_ref_file = script_dir / 'AI_REFERENCE.md' - - errors = [] - warnings = [] - - # Check if all required files exist - if not api_definitions_file.exists(): - errors.append(f"ERROR: api-definitions.json not found at {api_definitions_file}") - return errors, warnings - - if not manifest_file.exists(): - errors.append(f"ERROR: Documentation manifest not found at {manifest_file}. Run generate_docs.py first.") - return errors, warnings - - if not docs_file.exists(): - errors.append(f"ERROR: User documentation not found at {docs_file}. Run generate_docs.py first.") - - if not ai_ref_file.exists(): - errors.append(f"ERROR: AI reference not found at {ai_ref_file}. Run generate_docs.py first.") - - # Load current definitions from api-definitions.json - print("Loading definitions from api-definitions.json...") - try: - with open(api_definitions_file, 'r') as f: - api_data = json.load(f) - current_defs = { - 'queries': api_data.get('queries', {}), - 'transitions': api_data.get('transitions', {}) - } - except (FileNotFoundError, json.JSONDecodeError) as e: - errors.append(f"ERROR: Failed to load api-definitions.json: {e}") - return errors, warnings - - # Load documentation manifest - with open(manifest_file, 'r') as f: - manifest = json.load(f) - - # Check if manifest is stale (older than 24 hours) - if 'generated_at' in manifest: - generated_time = datetime.fromisoformat(manifest['generated_at']) - # Normalize to UTC timezone - if generated_time.tzinfo is None: - generated_time = generated_time.replace(tzinfo=timezone.utc) - else: - generated_time = generated_time.astimezone(timezone.utc) - age_hours = (datetime.now(timezone.utc) - generated_time).total_seconds() / 3600 - if age_hours > 24: - warnings.append(f"WARNING: Documentation was generated {age_hours:.1f} hours ago. Consider regenerating.") - - # Extract all current queries and transitions - current_queries = set() - current_transitions = set() - - for cat_key, category in current_defs.get('queries', {}).items(): - for query_key in category.get('queries', {}).keys(): - current_queries.add(query_key) - - for cat_key, category in current_defs.get('transitions', {}).items(): - for trans_key in category.get('transitions', {}).keys(): - current_transitions.add(trans_key) - - documented_queries = set(manifest.get('queries', {}).keys()) - documented_transitions = set(manifest.get('transitions', {}).keys()) - - # Find undocumented items - undocumented_queries = current_queries - documented_queries - undocumented_transitions = current_transitions - documented_transitions - - # Find removed items (documented but no longer in code) - removed_queries = documented_queries - current_queries - removed_transitions = documented_transitions - current_transitions - - # Report findings - if undocumented_queries: - errors.append(f"ERROR: {len(undocumented_queries)} queries are not documented:") - for q in sorted(undocumented_queries): - errors.append(f" - {q}") - - if undocumented_transitions: - errors.append(f"ERROR: {len(undocumented_transitions)} state transitions are not documented:") - for t in sorted(undocumented_transitions): - errors.append(f" - {t}") - - if removed_queries: - warnings.append(f"WARNING: {len(removed_queries)} queries are documented but no longer exist:") - for q in sorted(removed_queries): - warnings.append(f" - {q}") - - if removed_transitions: - warnings.append(f"WARNING: {len(removed_transitions)} transitions are documented but no longer exist:") - for t in sorted(removed_transitions): - warnings.append(f" - {t}") - - # Check file timestamps - api_definitions_mtime = os.path.getmtime(api_definitions_file) - - if docs_file.exists(): - docs_mtime = os.path.getmtime(docs_file) - if api_definitions_mtime > docs_mtime: - warnings.append("WARNING: api-definitions.json has been modified after docs.html was generated") - - if ai_ref_file.exists(): - ai_mtime = os.path.getmtime(ai_ref_file) - if api_definitions_mtime > ai_mtime: - warnings.append("WARNING: api-definitions.json has been modified after AI_REFERENCE.md was generated") - - return errors, warnings - -def main(): - """Main function""" - - errors, warnings = check_documentation_completeness() - - # Write report - report_lines = [] - report_lines.append("=" * 80) - report_lines.append("Documentation Completeness Check") - report_lines.append("=" * 80) - report_lines.append(f"Timestamp: {datetime.now().isoformat()}\n") - - if not errors and not warnings: - report_lines.append("βœ… All documentation is up to date!") - else: - if warnings: - report_lines.append(f"⚠️ {len(warnings)} warnings found:\n") - for warning in warnings: - report_lines.append(warning) - report_lines.append("") - - if errors: - report_lines.append(f"❌ {len(errors)} errors found:\n") - for error in errors: - report_lines.append(error) - report_lines.append("") - - report_lines.append("=" * 80) - - if errors: - report_lines.append("\nTo fix these errors, run: python3 generate_docs.py") - - # Print report - report = '\n'.join(report_lines) - print(report) - - # Save report - with open('documentation-check-report.txt', 'w') as f: - f.write(report) - - # Exit with error if there are any errors - return 1 if errors else 0 - -if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file diff --git a/packages/wasm-sdk/docs.css b/packages/wasm-sdk/docs.css deleted file mode 100644 index 4af6d243095..00000000000 --- a/packages/wasm-sdk/docs.css +++ /dev/null @@ -1,476 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - line-height: 1.6; - color: #333; - margin: 0; - padding: 0; - background-color: #f5f5f5; - display: flex; -} - -/* Sidebar styles */ -.sidebar { - width: 280px; - background-color: white; - box-shadow: 2px 0 4px rgba(0,0,0,0.1); - position: fixed; - height: 100vh; - overflow-y: auto; - padding: 20px; -} - -.sidebar h2 { - font-size: 1.2em; - margin-bottom: 10px; - color: #2c3e50; -} - -.sidebar ul { - list-style: none; - padding: 0; - margin: 0 0 20px 0; -} - -.sidebar li { - margin-bottom: 5px; -} - -.sidebar a { - color: #34495e; - text-decoration: none; - font-size: 0.9em; - display: block; - padding: 5px 10px; - border-radius: 3px; - transition: background-color 0.2s; -} - -.sidebar a:hover { - background-color: #ecf0f1; - color: #2c3e50; -} - -.sidebar .section-header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 12px 20px; - margin: 20px -20px 15px -20px; - font-weight: 600; - font-size: 0.9em; - text-transform: uppercase; - letter-spacing: 0.5px; - position: relative; - overflow: hidden; -} - -.sidebar .section-header:before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(255, 255, 255, 0.1); - transform: translateX(-100%); - transition: transform 0.6s ease; -} - -.sidebar .section-header:hover:before { - transform: translateX(0); -} - -.sidebar .section-header.state-transitions { - background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); -} - -.sidebar .category { - font-weight: 600; - color: #34495e; - margin-top: 15px; - margin-bottom: 8px; - font-size: 0.85em; - padding-left: 10px; - border-left: 3px solid #3498db; -} - -/* Search box styles */ -.search-container { - padding: 0 20px 20px 20px; - border-bottom: 1px solid #ecf0f1; -} - -.search-input { - width: 100%; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-size: 0.9em; - outline: none; - transition: border-color 0.2s; -} - -.search-input:focus { - border-color: #3498db; -} - -.search-input::placeholder { - color: #95a5a6; -} - -.sidebar li.hidden { - display: none; -} - -.sidebar .no-results { - text-align: center; - color: #95a5a6; - padding: 20px; - font-size: 0.9em; - display: none; -} - -/* Main content styles */ -.main-content { - margin-left: 320px; - padding: 20px 40px; - max-width: 900px; -} - -h1, h2, h3, h4 { - color: #2c3e50; -} - -h1 { - border-bottom: 3px solid #3498db; - padding-bottom: 10px; -} - -h2 { - border-bottom: 2px solid #ecf0f1; - padding-bottom: 8px; - margin-top: 30px; -} - -h3 { - color: #34495e; - margin-top: 25px; -} - -.nav { - background-color: white; - padding: 15px; - border-radius: 8px; - margin-bottom: 30px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} - -.nav ul { - list-style: none; - padding: 0; - margin: 0; -} - -.nav li { - display: inline-block; - margin-right: 20px; -} - -.nav a { - color: #3498db; - text-decoration: none; - font-weight: 500; -} - -.nav a:hover { - text-decoration: underline; -} - -.category { - background-color: white; - padding: 20px; - border-radius: 8px; - margin-bottom: 20px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} - -.operation { - border-left: 4px solid #3498db; - padding-left: 20px; - margin-bottom: 30px; -} - -.description { - color: #7f8c8d; - font-style: italic; - margin-bottom: 15px; -} - -.parameters { - background-color: #ecf0f1; - padding: 15px; - border-radius: 5px; - margin-top: 10px; -} - -.parameter { - margin-bottom: 10px; - padding: 5px 0; - border-bottom: 1px solid #bdc3c7; -} - -.parameter:last-child { - border-bottom: none; -} - -.param-name { - font-weight: bold; - color: #2c3e50; -} - -.param-type { - color: #e74c3c; - font-family: monospace; - font-size: 0.9em; -} - -.param-required { - color: #e74c3c; - font-weight: bold; -} - -.param-optional { - color: #95a5a6; -} - -.code-example { - background-color: #2c3e50; - color: #ecf0f1; - padding: 15px; - border-radius: 5px; - overflow-x: auto; - font-family: monospace; - margin-top: 10px; -} - -/* Interactive example styles */ -.example-container { - background-color: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 5px; - padding: 15px; - margin-top: 15px; -} - -.example-code { - background-color: #2c3e50; - color: #ecf0f1; - padding: 10px; - border-radius: 3px; - font-family: monospace; - font-size: 0.9em; - margin-bottom: 10px; - position: relative; - white-space: pre-wrap; - overflow-x: auto; -} - -.run-button { - background-color: #3498db; - color: white; - border: none; - padding: 8px 16px; - border-radius: 3px; - cursor: pointer; - font-weight: 500; - transition: background-color 0.2s; -} - -.run-button:hover { - background-color: #2980b9; -} - -.run-button:disabled { - background-color: #95a5a6; - cursor: not-allowed; -} - -.example-result { - margin-top: 10px; - padding: 10px; - border-radius: 3px; - font-family: monospace; - font-size: 0.85em; - display: none; -} - -.example-result.success { - background-color: #d4edda; - border: 1px solid #c3e6cb; - color: #155724; -} - -.example-result.error { - background-color: #f8d7da; - border: 1px solid #f5c6cb; - color: #721c24; -} - -.loading { - display: inline-block; - width: 20px; - height: 20px; - border: 3px solid rgba(255,255,255,.3); - border-radius: 50%; - border-top-color: #fff; - animation: spin 1s ease-in-out infinite; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -.back-to-top { - position: fixed; - bottom: 20px; - right: 20px; - background-color: #3498db; - color: white; - padding: 10px 15px; - border-radius: 5px; - text-decoration: none; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); -} - -.back-to-top:hover { - background-color: #2980b9; -} - -.info-note { - background-color: #e3f2fd; - color: #1565c0; - padding: 12px 16px; - border-radius: 4px; - font-size: 0.9em; - margin: 10px 0; - border-left: 4px solid #1976d2; -} - -.path-info { - background-color: #f5f7fa; - border: 1px solid #e1e5eb; - border-radius: 4px; - padding: 15px; - margin-top: 15px; -} - -.path-info h6 { - margin-top: 15px; - margin-bottom: 10px; - color: #2c3e50; - font-size: 0.95em; -} - -.path-info h6:first-child { - margin-top: 0; -} - -.path-table { - width: 100%; - border-collapse: collapse; - margin-bottom: 15px; -} - -.path-table th { - background-color: #e9ecef; - padding: 8px 12px; - text-align: left; - font-weight: 600; - border: 1px solid #dee2e6; -} - -.path-table td { - padding: 8px 12px; - border: 1px solid #dee2e6; -} - -.path-table code { - background-color: #fff; - padding: 2px 6px; - border-radius: 3px; - font-family: monospace; -} - -.path-info ul { - margin: 0; - padding-left: 25px; -} - -.path-info li { - margin-bottom: 5px; - line-height: 1.6; -} - -.path-info li code { - background-color: #fff; - padding: 2px 6px; - border-radius: 3px; - font-family: monospace; -} - -/* Preloader styles */ -#preloader { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.8); - z-index: 9999; -} - -.preloader--visible { - display: flex; - justify-content: center; - align-items: center; -} - -.preloader-content { - text-align: center; - background: white; - padding: 30px 50px; - border-radius: 10px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); -} - -.preloader-text { - font-size: 16px; - margin-bottom: 15px; - color: #333; -} - -.preloader-progress { - margin-top: 20px; -} - -.progress-bar { - width: 300px; - height: 20px; - background-color: #f0f0f0; - border-radius: 10px; - overflow: hidden; - margin-bottom: 10px; -} - -.progress-fill { - height: 100%; - background: linear-gradient(90deg, #4CAF50, #45a049); - width: 0%; - transition: width 0.3s ease; -} - -.progress-percent { - font-size: 14px; - font-weight: bold; - color: #333; -} \ No newline at end of file diff --git a/packages/wasm-sdk/docs.html b/packages/wasm-sdk/docs.html deleted file mode 100644 index e28aef577cb..00000000000 --- a/packages/wasm-sdk/docs.html +++ /dev/null @@ -1,3111 +0,0 @@ - - - - - - Dash Platform WASM JS SDK Documentation - - - - - - - -
-
-
Loading WASM module...
-
-
-
-
-
0%
-
-
-
- - - - - -
- - -

Dash Platform WASM JS SDK Documentation

- -
-

Overview

-

The Dash Platform WASM JS SDK provides a WebAssembly-based interface for interacting with the Dash Platform from JavaScript and TypeScript applications. - This documentation covers all available queries and state transitions.

- -

Key Concepts

-
    -
  • Queries: Read-only operations that fetch data from the platform
  • -
  • State Transitions: Write operations that modify state on the platform
  • -
  • Proofs: Cryptographic proofs can be requested for most queries to verify data authenticity
  • -
  • Credits: The platform's unit of account for paying transaction fees
  • -
  • Default Limits: All queries with optional limit parameters default to a maximum of 100 items if not specified
  • -
- -

Note: All examples below can be run directly in your browser using the testnet. Click the "Run" button to execute any example.

- -
- Test Identity: All examples use the testnet identity 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk -
This is a known identity with activity on testnet that you can use for testing. -
-
- -

Queries

- -
-

Identity Queries

-
-

Get Identity

-

Fetch an identity by its identifier

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.identity_fetch(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk');
- -
-
-
-

Get Identity Keys

-

Retrieve keys associated with an identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- Key Request Type - select - (optional) -
Options: All Keys (AllKeys {}) - Get all keys for the identity, Specific Keys (SpecificKeys with key_ids) - Get specific keys by ID [🚧 Work in Progress], Search Keys (SearchKey with purpose_map) - Search by purpose and security level [🚧 Work in Progress] -
-
- Specific Key IDs (required for 'specific' type) - array - (optional) -
Example: 0,1,2 -
-
- Search Purpose Map JSON (required for 'search' type) - text - (optional) -
Example: {"0": {"0": "current"}, "1": {"0": "all"}} -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Offset - number - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_keys(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', 'all', 10, 0);
- -
-
- -
-
Example 2 - Get Specific Keys
-
return await window.wasmFunctions.get_identity_keys(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', 'specific', [0, 1, 2]);
- -
-
- -
-
Example 3 - Search Keys by Purpose
-
return await window.wasmFunctions.get_identity_keys(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', 'search', undefined, '{"0": {"0": "current"}, "1": {"0": "current"}}');
- -
-
-
-

Get Identities Contract Keys

-

Get keys for multiple identities related to a specific contract

- -
-
Parameters:
-
- Identity IDs - array - (required) -
-
- Contract ID - text - (required) -
-
- Key Purposes - multiselect - (optional) -
Options: Authentication, Encryption, Decryption, Transfer, Voting -
-
- -
-
Example
-
return await window.wasmFunctions.get_identities_contract_keys(sdk, ['5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'], 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec');
- -
-
-
-

Get Identity Nonce

-

Get the current nonce for an identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_nonce(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk');
- -
-
-
-

Get Identity Contract Nonce

-

Get the nonce for an identity in relation to a specific contract

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- Contract ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_contract_nonce(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec');
- -
-
-
-

Get Identity Balance

-

Get the credit balance of an identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_balance(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk');
- -
-
-
-

Get Identities Balances

-

Get balances for multiple identities

- -
-
Parameters:
-
- Identity IDs - array - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identities_balances(sdk, ['5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk']);
- -
-
-
-

Get Identity Balance and Revision

-

Get both balance and revision number for an identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_balance_and_revision(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk');
- -
-
-
-

Get Identity by Unique Public Key Hash

-

Find an identity by its unique public key hash

- -
-
Parameters:
-
- Public Key Hash - text - (required) -
Example: b7e904ce25ed97594e72f7af0e66f298031c1754 -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_by_public_key_hash(sdk, 'b7e904ce25ed97594e72f7af0e66f298031c1754');
- -
-
-
-

Get Identity by Non-Unique Public Key Hash

-

Find identities by non-unique public key hash

- -
-
Parameters:
-
- Public Key Hash - text - (required) -
Example: 518038dc858461bcee90478fd994bba8057b7531 -
-
- Start After - text - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_by_non_unique_public_key_hash(sdk, '518038dc858461bcee90478fd994bba8057b7531', null);
- -
-
-
-

Get Identity Token Balances

-

Get token balances for an identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- Token IDs - array - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_token_balances(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', ['Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv']);
- -
-
-
-

Get Identities Token Balances

-

Get token balance for multiple identities

- -
-
Parameters:
-
- Identity IDs - array - (required) -
-
- Token ID - text - (required) -
Example: Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv -
-
- -
-
Example
-
return await window.wasmFunctions.get_identities_token_balances(sdk, ['5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'], 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv');
- -
-
-
-

Get Identity Token Info

-

Get token information for an identity's tokens

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- Token IDs (optional) - array - (optional) -
Example: ["Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv"] -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Offset - number - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_identity_token_infos(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', ['Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv'], 10, 0);
- -
-
-
-

Get Identities Token Info

-

Get token information for multiple identities with a specific token

- -
-
Parameters:
-
- Identity IDs - array - (required) -
-
- Token ID - text - (required) -
Example: Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv -
-
- -
-
Example
-
return await window.wasmFunctions.get_identities_token_infos(sdk, ['5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'], 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv');
- -
-
-
-
-

Data Contract Queries

-
-

Get Data Contract

-

Fetch a data contract by its identifier

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
Example: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec -
-
- -
-
Example
-
return await window.wasmFunctions.data_contract_fetch(sdk, 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec');
- -
-
-
-

Get Data Contract History

-

Get the version history of a data contract

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
Example: HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Offset - number - (optional) -
-
- Start At Timestamp (ms) - number - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_data_contract_history(sdk, 'HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM', 10, 0, '0');
- -
-
-
-

Get Data Contracts

-

Fetch multiple data contracts by their identifiers

- -
-
Parameters:
-
- Data Contract IDs - array - (required) -
Example: ["GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", "ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A"] -
-
- -
-
Example
-
return await window.wasmFunctions.get_data_contracts(sdk, ['GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A']);
- -
-
-
-
-

Document Queries

-
-

Get Documents

-

Query documents from a data contract

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
Example: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec -
-
- Document Type - text - (required) -
Example: domain -
-
- Where Clause (JSON) - json - (optional) -
Example: [["normalizedParentDomainName", "==", "dash"], ["normalizedLabel", "==", "therea1s11mshaddy5"]] -
-
- Order By (JSON) - json - (optional) -
Example: [["$createdAt", "desc"]] -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- -
-
Example
-
return await window.wasmFunctions.get_documents(sdk, 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'domain', JSON.stringify([["normalizedParentDomainName", "==", "dash"]]), JSON.stringify([["normalizedLabel", "asc"]]), 10);
- -
-
-
-

Get Document

-

Fetch a specific document by ID

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
Example: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec -
-
- Document Type - text - (required) -
Example: domain -
-
- Document ID - text - (required) -
Example: 7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r -
-
- -
-
Example
-
return await window.wasmFunctions.get_document(sdk, 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'domain', '7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r');
- -
-
-
-
-

DPNS Queries

-
-

Get DPNS Usernames

-

Get DPNS usernames for an identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_dpns_usernames(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk');
- -
-
-
-

DPNS Check Availability

-

Check if a DPNS username is available

- -
-
Parameters:
-
- Label (Username) - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.dpns_is_name_available(sdk, 'alice');
- -
-
-
-

DPNS Resolve Name

-

Resolve a DPNS name to an identity ID

- -
-
Parameters:
-
- Name - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.dpns_resolve_name(sdk, 'alice');
- -
-
-
-

DPNS Search Name

-

Search for DPNS names that start with a given prefix

- -
-
Parameters:
-
- Name Prefix - text - (required) -
Example: Enter prefix (e.g., ali) -
-
- Limit - number - (optional) -
Example: Default: 10 -
-
- -
-
Example
-
return await window.wasmFunctions.get_documents(sdk, 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'domain', JSON.stringify([["normalizedLabel", "startsWith", "ali"], ["normalizedParentDomainName", "==", "dash"]]), JSON.stringify([["normalizedLabel", "asc"]]), 10);
- -
-
-
-
-

Voting & Contested Resources

-
-

Get Contested Resources

-

Get list of contested resources

- -
-
Parameters:
-
- Document Type - text - (required) -
-
- Data Contract ID - text - (required) -
-
- Index Name - text - (required) -
-
- Start At Value - text - (optional) -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Offset - number - (optional) -
-
- Order Ascending - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_contested_resources(sdk, 'domain', 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'parentNameAndLabel', null, 10, 0, true);
- -
-
-
-

Get Contested Resource Vote State

-

Get the current vote state for a contested resource

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Index Name - text - (required) -
-
- Index Values - array - (required) -
Example: ["dash", "alice"] -
-
- Result Type - text - (required) -
-
- Allow Include Locked and Abstaining Vote Tally - checkbox - (optional) -
-
- Start At Identifier Info - text - (optional) -
-
- Count - number - (optional) -
-
- Order Ascending - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_contested_resource_vote_state(sdk, 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'domain', 'parentNameAndLabel', ['dash', 'alice'], 'documents', null, null, 100, true);
- -
-
-
-

Get Contested Resource Voters for Identity

-

Get voters who voted for a specific identity in a contested resource

- -
-
Parameters:
-
- Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Index Name - text - (required) -
-
- Index Values - array - (required) -
Example: ["dash", "alice"] -
-
- Contestant Identity ID - text - (required) -
-
- Start At Identifier Info - text - (optional) -
-
- Count - number - (optional) -
Example: Default: 100 -
-
- Order Ascending - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_contested_resource_voters_for_identity(sdk, 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', 'domain', 'parentNameAndLabel', ['dash', 'alice'], '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', null, 100, true);
- 🚧 Work in Progress -
-
-
-

Get Contested Resource Identity Votes

-

Get all votes cast by a specific identity

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Offset - number - (optional) -
-
- Order Ascending - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_contested_resource_identity_votes(sdk, '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', 10, 0, true);
- -
-
-
-

Get Vote Polls by End Date

-

Get vote polls within a time range

- -
-
Parameters:
-
- Start Time (ms) - number - (optional) -
Example: Timestamp in milliseconds as string (e.g., 1650000000000) -
-
- End Time (ms) - number - (optional) -
Example: Timestamp in milliseconds as string (e.g., 1650086400000) -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Offset - number - (optional) -
-
- Order Ascending - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_vote_polls_by_end_date(sdk, Date.now() - 86400000, Date.now(), 10, 0, true);
- -
-
-
-
-

Protocol & Version

-
-

Get Protocol Version Upgrade State

-

Get the current state of protocol version upgrades

- -
-
Parameters:
-

No parameters required

- -
-
Example
-
return await window.wasmFunctions.get_protocol_version_upgrade_state(sdk);
- -
-
-
-

Get Protocol Version Upgrade Vote Status

-

Get voting status for protocol version upgrades

- -
-
Parameters:
-
- Start ProTx Hash - text - (required) -
Example: 143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113 -
-
- Count - number - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_protocol_version_upgrade_vote_status(sdk, '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113', 100);
- -
-
-
-
-

Epoch & Block

-
-

Get Epochs Info

-

Get information about epochs

- -
-
Parameters:
-
- Start Epoch - number - (required) -
-
- Count - number - (required) -
-
- Ascending Order - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_epochs_info(sdk, 8635, 100, true);
- -
-
-
-

Get Current Epoch

-

Get information about the current epoch

- -
-
Parameters:
-

No parameters required

- -
-
Example
-
return await window.wasmFunctions.get_current_epoch(sdk);
- -
-
-
-

Get Finalized Epoch Info

-

Get information about finalized epochs

- -
-
Parameters:
-
- Start Epoch - number - (required) -
-
- Count - number - (required) -
-
- Ascending Order - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_finalized_epoch_infos(sdk, 8635, 100, true);
- -
-
-
-

Get Evonodes Proposed Epoch Blocks by IDs

-

Get proposed blocks by evonode IDs

- -
-
Parameters:
-
- Epoch - number - (required) -
-
- ProTx Hashes - array - (required) -
Example: ["143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113"] -
-
- -
-
Example
-
return await window.wasmFunctions.get_evonodes_proposed_epoch_blocks_by_ids(sdk, 8635, ['143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113']);
- -
-
-
-

Get Evonodes Proposed Epoch Blocks by Range

-

Get proposed blocks by range

- -
-
Parameters:
-
- Epoch - number - (required) -
-
- Limit - number - (optional) -
Default: 100 (maximum items returned if not specified) -
-
- Start After (Evonode ID) - text - (optional) -
Example: 143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113 -
-
- Order Ascending - checkbox - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_evonodes_proposed_epoch_blocks_by_range(sdk, 8635, 10, null, true);
- -
-
-
-
-

Token Queries

-
-

Get Token Statuses

-

Get token statuses

- -
-
Parameters:
-
- Token IDs - array - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_token_statuses(sdk, ['Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv', 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy']);
- -
-
-
-

Get Token Direct Purchase Prices

-

Get direct purchase prices for tokens

- -
-
Parameters:
-
- Token IDs - array - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_token_direct_purchase_prices(sdk, ['H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy']);
- -
-
-
-

Get Token Contract Info

-

Get information about a token contract

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
Example: EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta -
-
- -
-
Example
-
return await window.wasmFunctions.get_token_contract_info(sdk, 'EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta');
- -
-
-
-

Get Token Perpetual Distribution Last Claim

-

Get last claim information for perpetual distribution

- -
-
Parameters:
-
- Identity ID - text - (required) -
-
- Token ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_token_perpetual_distribution_last_claim(sdk, '5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX', 'HEv1AYWQfwCffXQgmuzmzyzUo9untRTmVr67n4e4PSWa');
- -
-
-
-

Get Token Total Supply

-

Get total supply of a token

- -
-
Parameters:
-
- Token ID - text - (required) -
Example: Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv -
-
- -
-
Example
-
return await window.wasmFunctions.get_token_total_supply(sdk, 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv');
- -
-
-
-
-

Group Queries

-
-

Get Group Info

-

Get information about a group

- -
-
Parameters:
-
- Contract ID - text - (required) -
-
- Group Contract Position - number - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_group_info(sdk, '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N', 0);
- -
-
-
-

Get Group Infos

-

Get information about multiple groups

- -
-
Parameters:
-
- Contract ID - text - (required) -
-
- Start at Position - number - (optional) -
-
- Include Start Position - checkbox - (optional) -
-
- Count - number - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_group_infos(sdk, '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N', null, 100);
- -
-
-
-

Get Group Actions

-

Get actions for a group

- -
-
Parameters:
-
- Contract ID - text - (required) -
-
- Group Contract Position - number - (required) -
-
- Status - select - (required) -
Options: Active, Closed -
-
- Start Action ID - text - (optional) -
-
- Include Start Action - checkbox - (optional) -
-
- Count - number - (optional) -
-
- -
-
Example
-
return await window.wasmFunctions.get_group_actions(sdk, '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N', 0, 'ACTIVE', null, 100);
- -
-
-
-

Get Group Action Signers

-

Get signers for a group action

- -
-
Parameters:
-
- Contract ID - text - (required) -
-
- Group Contract Position - number - (required) -
-
- Status - select - (required) -
Options: Active, Closed -
-
- Action ID - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_group_action_signers(sdk, '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N', 0, 'ACTIVE', '6XJzL6Qb8Zhwxt4HFwh8NAn7q1u4dwdoUf8EmgzDudFZ');
- -
-
-
-
-

System & Utility

-
-

Get Status

-

Get system status

- -
-
Parameters:
-

No parameters required

- -
-
Example
-
return await window.wasmFunctions.get_status(sdk);
- -
-
-
-

Get Current Quorums Info

-

Get information about current quorums

- -
-
Parameters:
-

No parameters required

- -
-
Example
-
return await window.wasmFunctions.get_current_quorums_info(sdk);
- -
-
-
-

Get Prefunded Specialized Balance

-

Get prefunded specialized balance

- -
-
Parameters:
-
- Specialized Balance ID - text - (required) -
Example: AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT -
-
- -
-
Example
-
return await window.wasmFunctions.get_prefunded_specialized_balance(sdk, 'AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT');
- -
-
-
-

Get Total Credits in Platform

-

Get total credits in the platform

- -
-
Parameters:
-

No parameters required

- -
-
Example
-
return await window.wasmFunctions.get_total_credits_in_platform(sdk);
- -
-
-
-

Get Path Elements

-

Access any data in the Dash Platform state tree. This low-level query allows direct access to GroveDB storage by specifying a path through the tree structure and keys to retrieve at that path. Common paths include: Identities (32), Tokens (96), DataContractDocuments (64), Balances (16), Votes (80), and more.

- -
-
Parameters:
-
- Path - array - (required) -
-
- Keys - array - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.get_path_elements(sdk, ['96'], ['5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk']);
- 🚧 Work in Progress -
-
Common Path Values:
- - - - - - - - - - - - - - - - -
Path ValueDescription
64Data Contract Documents
32Identities
24Unique Public Key Hashes to Identities
8Non-Unique Public Key Hashes to Identities
16Tokens
48Pools
40Prefunded Specialized Balances
72Spent Asset Lock Transactions
80Withdrawal Transactions
88Group Actions
96Balances
104Misc
112Votes
120Versions
-
Example Paths:
-
    -
  • [32, identity_id] - Access identity data
  • -
  • [96, identity_id] - Access identity balance
  • -
  • [16, contract_id, token_id] - Access token info
  • -
  • [64, contract_id, 1, document_type] - Access documents by type
  • -
  • [88, contract_id, group_position] - Access group actions
  • -
  • [112] - Access votes tree
  • -
-
-
-
-
-

Wait for State Transition Result

-

Internal query to wait for and retrieve the result of a previously submitted state transition

- -
-
Parameters:
-
- State Transition Hash - text - (required) -
-
- -
-
Example
-
return await window.wasmFunctions.wait_for_state_transition_result(sdk, '0000000000000000000000000000000000000000000000000000000000000000');
-

This is an internal query used to wait for and retrieve the result of a previously submitted state transition. It requires a valid state transition hash from a prior operation.

-
-
-

State Transitions

-
-

Identity Transitions

-
-

Identity Create

-

Create a new identity with initial credits

- -
-
Parameters:
-
- Asset Lock Proof - string - (required) -
Hex-encoded JSON asset lock proof -
-
- Asset Lock Proof Private Key - string - (required) -
WIF format private key -
-
- Public Keys - string - (required) -
JSON array of public keys. Key requirements: ECDSA_SECP256K1 requires privateKeyHex or privateKeyWif for signing, BLS12_381 requires privateKeyHex for signing, ECDSA_HASH160 requires either the data field (base64-encoded 20-byte public key hash) or privateKeyHex (produces empty signatures). -
-
- -
-
Example
-
// Asset lock proof is a hex-encoded JSON object -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -// Public keys array with proper key types and private keys for signing/hashing -const publicKeys = JSON.stringify([ - { - id: 0, - keyType: "ECDSA_SECP256K1", - purpose: "AUTHENTICATION", - securityLevel: "MASTER", - data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key - readOnly: false, - privateKeyWif: "XBrZJKcW4ajWVNAU6yP87WQog6CjFnpbqyAKgNTZRqmhYvPgMNV2" - }, - { - id: 1, - keyType: "ECDSA_HASH160", - purpose: "AUTHENTICATION", - securityLevel: "HIGH", - data: "ripemd160hash20bytes1234", // Base64-encoded 20-byte RIPEMD160 hash - readOnly: false, - // ECDSA_HASH160 keys produce empty signatures (not required/used for signing) - } -]); - -const result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);
-
-
-

Identity Top Up

-

Add credits to an existing identity

- -
-
Parameters:
-
- Identity ID - string - (required) -
Base58 format identity ID -
-
- Asset Lock Proof - string - (required) -
Hex-encoded JSON asset lock proof -
-
- Asset Lock Proof Private Key - string - (required) -
WIF format private key -
-
- -
-
Example
-
const identityId = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; // base58 -const assetLockProof = "a9147d3b... (hex-encoded)"; -const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWve1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format - -const result = await sdk.identityTopUp(identityId, assetLockProof, assetLockProofPrivateKey);
-
-
-

Identity Update

-

Update identity keys (add or disable)

- -
-
Parameters:
-
- Keys to Add (JSON array) - textarea - (optional) -
Example: [{"keyType":"ECDSA_HASH160","purpose":"AUTHENTICATION","data":"base64_key_data"}] -
-
- Key IDs to Disable (comma-separated) - text - (optional) -
Example: 2,3,5 -
-
- -
-
Example
-
const result = await sdk.identityUpdate(identityHex, /* params */, privateKeyHex);
-
-
-

Identity Credit Transfer

-

Transfer credits between identities

- -
-
Parameters:
-
- Recipient Identity ID - text - (required) -
-
- Amount (credits) - number - (required) -
-
- -
-
Example
-
const result = await sdk.identityCreditTransfer(identityHex, /* params */, privateKeyHex);
-
-
-

Identity Credit Withdrawal

-

Withdraw credits from identity to Dash address

- -
-
Parameters:
-
- Dash Address - text - (required) -
-
- Amount (credits) - number - (required) -
-
- Core Fee Per Byte (optional) - number - (optional) -
-
- -
-
Example
-
const result = await sdk.identityCreditWithdrawal(identityHex, /* params */, privateKeyHex);
-
-
-
-

Data Contract Transitions

-
-

Data Contract Create

-

Create a new data contract

- -
-
Parameters:
-
- Can Be Deleted - checkbox - (optional) -
-
- Read Only - checkbox - (optional) -
-
- Keeps History - checkbox - (optional) -
-
- Documents Keep History (Default) - checkbox - (optional) -
-
- Documents Mutable (Default) - checkbox - (optional) -
-
- Documents Can Be Deleted (Default) - checkbox - (optional) -
-
- Requires Identity Encryption Key (optional) - text - (optional) -
-
- Requires Identity Decryption Key (optional) - text - (optional) -
-
- Document Schemas JSON - json - (required) -
Example: { - "note": { - "type": "object", - "properties": { - "message": { - "type": "string", - "maxLength": 100, - "position": 0 - } - }, - "required": ["message"], - "additionalProperties": false - } -} -
-
- Groups (optional) - json - (optional) -
Example: {} -
-
- Tokens (optional) - json - (optional) -
Example: {} -
-
- Keywords (comma separated, optional) - text - (optional) -
-
- Description (optional) - text - (optional) -
-
- -
-
Example
-
const result = await sdk.dataContractCreate(identityHex, /* params */, privateKeyHex);
-
-
-

Data Contract Update

-

Add document types, groups, or tokens to an existing data contract

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- New Document Schemas to Add (optional) - json - (optional) -
Example: { - "newType": { - "type": "object", - "properties": { - "field": { - "type": "string", - "maxLength": 100, - "position": 0 - } - }, - "required": ["field"], - "additionalProperties": false - } -} -
-
- New Groups to Add (optional) - json - (optional) -
Example: {} -
-
- New Tokens to Add (optional) - json - (optional) -
Example: {} -
-
- -
-
Example
-
const result = await sdk.dataContractUpdate(identityHex, /* params */, privateKeyHex);
-
-
-
-

Document Transitions

-
-

Document Create

-

Create a new document

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Fetch Schema - button - (optional) -
-
- Document Fields - dynamic - (optional) -
-
- -
-
Example
-
const result = await sdk.document_create( - identityHex, - contractId, - "note", - JSON.stringify({ message: "Hello!" }), - privateKeyHex -);
-
-
-

Document Replace

-

Replace an existing document

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Document ID - text - (required) -
-
- Load Document - button - (optional) -
-
- Document Fields - dynamic - (optional) -
-
- -
-
Example
-
const result = await sdk.documentReplace(identityHex, /* params */, privateKeyHex);
-
-
-

Document Delete

-

Delete an existing document

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Document ID - text - (required) -
-
- -
-
Example
-
const result = await sdk.documentDelete(identityHex, /* params */, privateKeyHex);
-
-
-

Document Transfer

-

Transfer document ownership

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Document ID - text - (required) -
-
- Recipient Identity ID - text - (required) -
-
- -
-
Example
-
const result = await sdk.documentTransfer(identityHex, /* params */, privateKeyHex);
-
-
-

Document Purchase

-

Purchase a document

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Document ID - text - (required) -
-
- Price (credits) - number - (required) -
-
- -
-
Example
-
const result = await sdk.documentPurchase(identityHex, /* params */, privateKeyHex);
-
-
-

Document Set Price

-

Set or update document price

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Document Type - text - (required) -
-
- Document ID - text - (required) -
-
- Price (credits, 0 to remove) - number - (required) -
-
- -
-
Example
-
const result = await sdk.documentSetPrice(identityHex, /* params */, privateKeyHex);
-
-
-

DPNS Register Name

-

Register a new DPNS username

- -
-
Parameters:
-
- Username - text - (required) -
Example: Enter username (e.g., alice) -
-
- -
-
Example
-
const result = await sdk.dpnsRegister(identityHex, /* params */, privateKeyHex);
-
-
-
-

Token Transitions

-
-

Token Burn

-

Burn tokens

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Amount to Burn - text - (required) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenBurn(identityHex, /* params */, privateKeyHex);
-
-
-

Token Mint

-

Mint new tokens

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Amount to Mint - text - (required) -
-
- Issue To Identity ID - text - (optional) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenMint(identityHex, /* params */, privateKeyHex);
-
-
-

Token Claim

-

Claim tokens from a distribution

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Distribution Type - select - (required) -
Options: Perpetual, Pre-programmed -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenClaim(identityHex, /* params */, privateKeyHex);
-
-
-

Token Set Price

-

Set or update the price for direct token purchases

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Price Type - select - (required) -
Options: Single Price, Tiered Pricing -
-
- Price Data (single price or JSON map) - text - (optional) -
Example: Leave empty to remove pricing -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenSetPriceForDirectPurchase(identityHex, /* params */, privateKeyHex);
-
-
-

Token Direct Purchase

-

Purchase tokens directly at the configured price

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Amount to Purchase - text - (required) -
-
- Total Agreed Price (in credits) - Optional, fetches from pricing schedule if not provided - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenDirectPurchase(identityHex, /* params */, privateKeyHex);
-
-
-

Token Config Update

-

Update token configuration settings

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Config Item Type - select - (required) -
Options: Conventions, Max Supply, Perpetual Distribution, New Tokens Destination Identity, Minting Allow Choosing Destination, Manual Minting, Manual Burning, Conventions Control Group, Conventions Admin Group, Max Supply Control Group, Max Supply Admin Group -
-
- Config Value (JSON or specific value) - text - (required) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenConfigUpdate(identityHex, /* params */, privateKeyHex);
-
-
-

Token Transfer

-

Transfer tokens between identities

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Amount to Transfer - text - (required) -
-
- Recipient Identity ID - text - (required) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.token_transfer( - identityHex, - contractId, - tokenId, - 1000000, // amount - recipientId, - privateKeyHex -);
-
-
-

Token Freeze

-

Freeze tokens for a specific identity

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Identity ID to Freeze - text - (required) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenFreeze(identityHex, /* params */, privateKeyHex);
-
-
-

Token Unfreeze

-

Unfreeze tokens for a specific identity

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Identity ID to Unfreeze - text - (required) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenUnfreeze(identityHex, /* params */, privateKeyHex);
-
-
-

Token Destroy Frozen

-

Destroy frozen tokens

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
-
- Token Contract Position - number - (required) -
-
- Identity ID whose frozen tokens to destroy - text - (required) -
-
- Public Note - text - (optional) -
-
- -
-
Example
-
const result = await sdk.tokenDestroyFrozen(identityHex, /* params */, privateKeyHex);
-
-
-
-

Voting Transitions

-
-

DPNS Username

-

Cast a vote for a contested DPNS username

- -
-
Parameters:
-
- Contested Username - text - (required) -
Example: Enter the contested username (e.g., 'myusername') -
-
- Vote Choice - select - (required) -
Options: Abstain, Lock (Give to no one), Vote for Identity -
-
- Target Identity ID (if voting for identity) - text - (optional) -
Example: Identity ID to vote for -
-
- -
-
Example
-
const result = await sdk.dpnsUsername(identityHex, /* params */, privateKeyHex);
-
-
-

Contested Resource

-

Cast a vote for contested resources as a masternode

- -
-
Parameters:
-
- Data Contract ID - text - (required) -
Example: Contract ID containing the contested resource -
-
- Get Contested Resources - button - (optional) -
-
- Contested Resources - dynamic - (optional) -
-
- Vote Choice - select - (required) -
Options: Abstain, Lock (Give to no one), Vote for Identity -
-
- Target Identity ID (if voting for identity) - text - (optional) -
Example: Identity ID to vote for -
-
- -
-
Example
-
const result = await sdk.masternodeVote(identityHex, /* params */, privateKeyHex);
-
-
- ↑ Top -
- - - - - - diff --git a/packages/wasm-sdk/docs_manifest.json b/packages/wasm-sdk/docs_manifest.json deleted file mode 100644 index 2d8c69f70ff..00000000000 --- a/packages/wasm-sdk/docs_manifest.json +++ /dev/null @@ -1,311 +0,0 @@ -{ - "generated_at": "2025-09-04T13:38:48.983658+00:00", - "queries": { - "getIdentity": { - "category": "identity", - "documented": true - }, - "getIdentityKeys": { - "category": "identity", - "documented": true - }, - "getIdentitiesContractKeys": { - "category": "identity", - "documented": true - }, - "getIdentityNonce": { - "category": "identity", - "documented": true - }, - "getIdentityContractNonce": { - "category": "identity", - "documented": true - }, - "getIdentityBalance": { - "category": "identity", - "documented": true - }, - "getIdentitiesBalances": { - "category": "identity", - "documented": true - }, - "getIdentityBalanceAndRevision": { - "category": "identity", - "documented": true - }, - "getIdentityByPublicKeyHash": { - "category": "identity", - "documented": true - }, - "getIdentityByNonUniquePublicKeyHash": { - "category": "identity", - "documented": true - }, - "getIdentityTokenBalances": { - "category": "identity", - "documented": true - }, - "getIdentitiesTokenBalances": { - "category": "identity", - "documented": true - }, - "getIdentityTokenInfos": { - "category": "identity", - "documented": true - }, - "getIdentitiesTokenInfos": { - "category": "identity", - "documented": true - }, - "getDataContract": { - "category": "dataContract", - "documented": true - }, - "getDataContractHistory": { - "category": "dataContract", - "documented": true - }, - "getDataContracts": { - "category": "dataContract", - "documented": true - }, - "getDocuments": { - "category": "document", - "documented": true - }, - "getDocument": { - "category": "document", - "documented": true - }, - "getDpnsUsername": { - "category": "dpns", - "documented": true - }, - "dpnsCheckAvailability": { - "category": "dpns", - "documented": true - }, - "dpnsResolve": { - "category": "dpns", - "documented": true - }, - "dpnsSearch": { - "category": "dpns", - "documented": true - }, - "getContestedResources": { - "category": "voting", - "documented": true - }, - "getContestedResourceVoteState": { - "category": "voting", - "documented": true - }, - "getContestedResourceVotersForIdentity": { - "category": "voting", - "documented": true - }, - "getContestedResourceIdentityVotes": { - "category": "voting", - "documented": true - }, - "getVotePollsByEndDate": { - "category": "voting", - "documented": true - }, - "getProtocolVersionUpgradeState": { - "category": "protocol", - "documented": true - }, - "getProtocolVersionUpgradeVoteStatus": { - "category": "protocol", - "documented": true - }, - "getEpochsInfo": { - "category": "epoch", - "documented": true - }, - "getCurrentEpoch": { - "category": "epoch", - "documented": true - }, - "getFinalizedEpochInfos": { - "category": "epoch", - "documented": true - }, - "getEvonodesProposedEpochBlocksByIds": { - "category": "epoch", - "documented": true - }, - "getEvonodesProposedEpochBlocksByRange": { - "category": "epoch", - "documented": true - }, - "getTokenStatuses": { - "category": "token", - "documented": true - }, - "getTokenDirectPurchasePrices": { - "category": "token", - "documented": true - }, - "getTokenContractInfo": { - "category": "token", - "documented": true - }, - "getTokenPerpetualDistributionLastClaim": { - "category": "token", - "documented": true - }, - "getTokenTotalSupply": { - "category": "token", - "documented": true - }, - "getGroupInfo": { - "category": "group", - "documented": true - }, - "getGroupInfos": { - "category": "group", - "documented": true - }, - "getGroupActions": { - "category": "group", - "documented": true - }, - "getGroupActionSigners": { - "category": "group", - "documented": true - }, - "getStatus": { - "category": "system", - "documented": true - }, - "getCurrentQuorumsInfo": { - "category": "system", - "documented": true - }, - "getPrefundedSpecializedBalance": { - "category": "system", - "documented": true - }, - "getTotalCreditsInPlatform": { - "category": "system", - "documented": true - }, - "getPathElements": { - "category": "system", - "documented": true - }, - "waitForStateTransitionResult": { - "category": "system", - "documented": true - } - }, - "transitions": { - "identityCreate": { - "category": "identity", - "documented": true - }, - "identityTopUp": { - "category": "identity", - "documented": true - }, - "identityUpdate": { - "category": "identity", - "documented": true - }, - "identityCreditTransfer": { - "category": "identity", - "documented": true - }, - "identityCreditWithdrawal": { - "category": "identity", - "documented": true - }, - "dataContractCreate": { - "category": "dataContract", - "documented": true - }, - "dataContractUpdate": { - "category": "dataContract", - "documented": true - }, - "documentCreate": { - "category": "document", - "documented": true - }, - "documentReplace": { - "category": "document", - "documented": true - }, - "documentDelete": { - "category": "document", - "documented": true - }, - "documentTransfer": { - "category": "document", - "documented": true - }, - "documentPurchase": { - "category": "document", - "documented": true - }, - "documentSetPrice": { - "category": "document", - "documented": true - }, - "dpnsRegister": { - "category": "document", - "documented": true - }, - "tokenBurn": { - "category": "token", - "documented": true - }, - "tokenMint": { - "category": "token", - "documented": true - }, - "tokenClaim": { - "category": "token", - "documented": true - }, - "tokenSetPriceForDirectPurchase": { - "category": "token", - "documented": true - }, - "tokenDirectPurchase": { - "category": "token", - "documented": true - }, - "tokenConfigUpdate": { - "category": "token", - "documented": true - }, - "tokenTransfer": { - "category": "token", - "documented": true - }, - "tokenFreeze": { - "category": "token", - "documented": true - }, - "tokenUnfreeze": { - "category": "token", - "documented": true - }, - "tokenDestroyFrozen": { - "category": "token", - "documented": true - }, - "dpnsUsername": { - "category": "voting", - "documented": true - }, - "masternodeVote": { - "category": "voting", - "documented": true - } - } -} \ No newline at end of file diff --git a/packages/wasm-sdk/generate_docs.py b/packages/wasm-sdk/generate_docs.py deleted file mode 100755 index db5dba31243..00000000000 --- a/packages/wasm-sdk/generate_docs.py +++ /dev/null @@ -1,1374 +0,0 @@ -#!/usr/bin/env python3 -""" -Documentation generator for WASM JS SDK -Reads query and state transition definitions from api-definitions.json -and generates both user documentation (HTML) and AI reference (Markdown) -""" - -import json -import html as html_lib -from pathlib import Path -from datetime import datetime, timezone - -# Module-level constants extracted for maintainability - -# Test data for various query types - using a known testnet values -TESTNET_TEST_DATA = { - 'identity_id': '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', - 'specialized_balance_id': 'AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT', - 'contract_id': 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', - 'data_contract_id': 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', - 'data_contract_history_id': 'HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM', - 'token_contract_id': 'ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A', - 'group_contract_id': '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N', - 'public_key_hash_unique': 'b7e904ce25ed97594e72f7af0e66f298031c1754', - 'public_key_hash_non_unique': '518038dc858461bcee90478fd994bba8057b7531', - 'pro_tx_hash': '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113', - 'token_id': 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv', - 'document_type': 'domain', - 'document_id': '7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r', - 'username': 'alice', - 'epoch': 8635 -} - -# Function name mappings from API names to SDK function names -FUNCTION_NAME_MAP = { - 'getIdentity': 'identity_fetch', - 'getIdentityKeys': 'get_identity_keys', - 'getIdentityNonce': 'get_identity_nonce', - 'getIdentityContractNonce': 'get_identity_contract_nonce', - 'getIdentityBalance': 'get_identity_balance', - 'getIdentitiesBalances': 'get_identities_balances', - 'getIdentityBalanceAndRevision': 'get_identity_balance_and_revision', - 'getIdentityByPublicKeyHash': 'get_identity_by_public_key_hash', - 'getIdentityByNonUniquePublicKeyHash': 'get_identity_by_non_unique_public_key_hash', - 'getIdentitiesContractKeys': 'get_identities_contract_keys', - 'getIdentityTokenBalances': 'get_identity_token_balances', - 'getIdentitiesTokenBalances': 'get_identities_token_balances', - 'getIdentityTokenInfos': 'get_identity_token_infos', - 'getIdentitiesTokenInfos': 'get_identities_token_infos', - 'getDataContract': 'data_contract_fetch', - 'getDataContractHistory': 'get_data_contract_history', - 'getDataContracts': 'get_data_contracts', - 'getDocuments': 'get_documents', - 'getDocument': 'get_document', - 'getDpnsUsername': 'get_dpns_usernames', - 'dpnsCheckAvailability': 'dpns_is_name_available', - 'dpnsResolve': 'dpns_resolve_name', - 'dpnsSearch': 'get_documents', - 'getContestedResources': 'get_contested_resources', - 'getContestedResourceVoteState': 'get_contested_resource_vote_state', - 'getContestedResourceVotersForIdentity': 'get_contested_resource_voters_for_identity', - 'getContestedResourceIdentityVotes': 'get_contested_resource_identity_votes', - 'getVotePollsByEndDate': 'get_vote_polls_by_end_date', - 'getProtocolVersionUpgradeState': 'get_protocol_version_upgrade_state', - 'getProtocolVersionUpgradeVoteStatus': 'get_protocol_version_upgrade_vote_status', - 'getEpochsInfo': 'get_epochs_info', - 'getCurrentEpoch': 'get_current_epoch', - 'getFinalizedEpochInfos': 'get_finalized_epoch_infos', - 'getEvonodesProposedEpochBlocksByIds': 'get_evonodes_proposed_epoch_blocks_by_ids', - 'getEvonodesProposedEpochBlocksByRange': 'get_evonodes_proposed_epoch_blocks_by_range', - 'getTokenStatuses': 'get_token_statuses', - 'getTokenDirectPurchasePrices': 'get_token_direct_purchase_prices', - 'getTokenContractInfo': 'get_token_contract_info', - 'getTokenPerpetualDistributionLastClaim': 'get_token_perpetual_distribution_last_claim', - 'getTokenTotalSupply': 'get_token_total_supply', - 'getGroupInfo': 'get_group_info', - 'getGroupInfos': 'get_group_infos', - 'getGroupActions': 'get_group_actions', - 'getGroupActionSigners': 'get_group_action_signers', - 'getStatus': 'get_status', - 'getCurrentQuorumsInfo': 'get_current_quorums_info', - 'getPrefundedSpecializedBalance': 'get_prefunded_specialized_balance', - 'getTotalCreditsInPlatform': 'get_total_credits_in_platform', - 'getPathElements': 'get_path_elements', - 'waitForStateTransitionResult': 'wait_for_state_transition_result' -} - -def load_api_definitions(api_definitions_file): - """Load query and state transition definitions from api-definitions.json""" - try: - with open(api_definitions_file, 'r', encoding='utf-8') as f: - api_data = json.load(f) - - query_definitions = api_data.get('queries', {}) - transition_definitions = api_data.get('transitions', {}) - - return query_definitions, transition_definitions - - except (FileNotFoundError, json.JSONDecodeError) as e: - print(f"Error loading API definitions: {e}") - return {}, {} - -def generate_example_code(query_key, inputs): - """Generate example code for a query""" - - # Map input names to test values - param_mapping = { - 'id': f"'{TESTNET_TEST_DATA['data_contract_history_id']}'" if 'getDataContractHistory' in query_key else f"'{TESTNET_TEST_DATA['data_contract_id']}'" if 'getDataContract' in query_key else f"'{TESTNET_TEST_DATA['identity_id']}'", - 'identityId': f"'{TESTNET_TEST_DATA['specialized_balance_id']}'" if 'getPrefundedSpecializedBalance' in query_key else "'5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX'" if 'getTokenPerpetualDistributionLastClaim' in query_key else f"'{TESTNET_TEST_DATA['identity_id']}'", - 'ids': f"['{TESTNET_TEST_DATA['data_contract_id']}', '{TESTNET_TEST_DATA['token_contract_id']}']" if 'getDataContracts' in query_key else f"['{TESTNET_TEST_DATA['pro_tx_hash']}']" if 'Evonodes' in query_key else f"['{TESTNET_TEST_DATA['identity_id']}']", - 'identitiesIds': f"['{TESTNET_TEST_DATA['identity_id']}']", - 'identityIds': f"['{TESTNET_TEST_DATA['identity_id']}']", - 'contractId': f"'{TESTNET_TEST_DATA['group_contract_id']}'" if ('group' in query_key.lower() or 'Group' in query_key) else f"'{TESTNET_TEST_DATA['token_contract_id']}'" if ('token' in query_key.lower() or 'Token' in query_key) and 'TokenBalance' not in query_key and 'TokenInfo' not in query_key else f"'{TESTNET_TEST_DATA['contract_id']}'", - 'dataContractId': "'EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta'" if 'getTokenContractInfo' in query_key else f"'{TESTNET_TEST_DATA['data_contract_id']}'", - 'publicKeyHash': f"'{TESTNET_TEST_DATA['public_key_hash_unique']}'" if 'ByPublicKeyHash' in query_key and 'NonUnique' not in query_key else f"'{TESTNET_TEST_DATA['public_key_hash_non_unique']}'", - 'startProTxHash': f"'{TESTNET_TEST_DATA['pro_tx_hash']}'", - 'tokenId': "'HEv1AYWQfwCffXQgmuzmzyzUo9untRTmVr67n4e4PSWa'" if 'getTokenPerpetualDistributionLastClaim' in query_key else f"'{TESTNET_TEST_DATA['token_id']}'", - 'tokenIds': f"['{TESTNET_TEST_DATA['token_id']}', 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy']" if 'getTokenStatuses' in query_key else "['H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy']" if 'getTokenDirectPurchasePrices' in query_key else f"['{TESTNET_TEST_DATA['token_id']}']", - 'documentType': f"'{TESTNET_TEST_DATA['document_type']}'", - 'documentId': f"'{TESTNET_TEST_DATA['document_id']}'", - 'label': f"'{TESTNET_TEST_DATA['username']}'", - 'name': f"'{TESTNET_TEST_DATA['username']}'", - 'prefix': "'ali'", - 'epoch': '1000' if 'getEpochsInfo' in query_key else TESTNET_TEST_DATA['epoch'], - 'keyRequestType': "'all'", - 'limit': '10', - 'count': '100', - 'offset': '0', - 'startEpoch': '8635', - 'startTimeMs': 'Date.now() - 86400000', # 24 hours ago - 'endTimeMs': 'Date.now()', - 'ascending': 'true', - 'orderAscending': 'true', - 'resultType': "'documents'", - 'documentTypeName': "'domain'", - 'indexName': "'parentNameAndLabel'", - 'contestantId': f"'{TESTNET_TEST_DATA['identity_id']}'", - 'amount': '1000000', - 'recipientId': f"'{TESTNET_TEST_DATA['identity_id']}'", - 'toAddress': "'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf'", - 'where': 'JSON.stringify([["normalizedParentDomainName", "==", "dash"]])', - 'orderBy': 'JSON.stringify([["normalizedLabel", "asc"]])', - 'groupContractPosition': '0', - 'groupContractPositions': '[0]', - 'startAtGroupContractPosition': '0', - 'startGroupContractPositionIncluded': 'false', - 'status': "'ACTIVE'", - 'startActionId': "'0'", - 'startActionIdIncluded': 'false', - 'actionId': "'6XJzL6Qb8Zhwxt4HFwh8NAn7q1u4dwdoUf8EmgzDudFZ'", - 'path': "['96']", - 'keys': f"['{TESTNET_TEST_DATA['identity_id']}']", - 'stateTransitionHash': "'0000000000000000000000000000000000000000000000000000000000000000'", - 'allowIncludeLockedAndAbstainingVoteTally': 'null', - 'startAtValue': 'null', - 'startAtIdentifierInfo': 'null', - 'indexValues': "['dash', 'alice']", - 'startAtVoterInfo': 'null', - 'startAtVotePollIdInfo': 'null', - 'startTimeInfo': '(Date.now() - 86400000).toString()', - 'endTimeInfo': 'Date.now().toString()', - 'startAfter': 'null' - } - - # Handle special cases for functions with structured parameters - if query_key == 'getGroupInfos': - # getGroupInfos expects: sdk, contractId, startAtInfo (object or null), count - params = [f"'{TESTNET_TEST_DATA['group_contract_id']}'", "null", "100"] - elif query_key == 'getGroupActions': - # getGroupActions expects: sdk, contractId, groupContractPosition, status, startAtInfo (object or null), count - params = [f"'{TESTNET_TEST_DATA['group_contract_id']}'", "0", "'ACTIVE'", "null", "100"] - elif query_key == 'getDataContractHistory': - # getDataContractHistory expects: sdk, id, limit, offset, startAtMs - # Use the specific contract ID for getDataContractHistory examples - params = ["'HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM'", "10", "0", "'0'"] - elif query_key == 'dpnsSearch': - # dpnsSearch is implemented as get_documents with DPNS-specific parameters - # get_documents expects: sdk, contractId, documentType, whereClause, orderBy, limit - dpns_contract_id = "'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'" - document_type = "'domain'" - where_clause = 'JSON.stringify([["normalizedLabel", "startsWith", "ali"], ["normalizedParentDomainName", "==", "dash"]])' - order_by = 'JSON.stringify([["normalizedLabel", "asc"]])' - limit = "10" - params = [dpns_contract_id, document_type, where_clause, order_by, limit] - else: - # Generate parameters normally - params = [] - for input_def in inputs: - name = input_def.get('name', '') - if name in param_mapping: - params.append(str(param_mapping[name])) - elif input_def.get('required', False): - # Generate a default value based on type - if input_def.get('type') == 'array': - params.append('[]') - elif input_def.get('type') == 'number': - params.append('100') - elif input_def.get('type') == 'checkbox': - params.append('false') - else: - params.append('""') - - # Use module-level function name mapping - func_name = FUNCTION_NAME_MAP.get(query_key, query_key) - - # Add sdk as first parameter for all functions - all_params = ['sdk'] + params - - # Generate the function call (functions are imported directly, not methods on sdk) - return f'return await window.wasmFunctions.{func_name}({", ".join(all_params)});' - - -def generate_sidebar_entries(definitions, type_prefix, section_class=""): - """Generate sidebar entries for queries or transitions""" - html_content = "" - for cat_key, category in definitions.items(): - html_content += f'
  • {category.get("label", cat_key)}
  • \n' - items = category.get('queries' if type_prefix == 'query' else 'transitions', {}) - for item_key in items: - item = items[item_key] - html_content += f'
  • {item.get("label", item_key)}
  • \n' - return html_content - -def generate_operation_docs(definitions, type_name, type_prefix): - """Generate documentation for operations (queries or transitions)""" - html_content = "" - for cat_key, category in definitions.items(): - html_content += f'''\n
    -

    {category.get('label', cat_key)}

    -''' - - items_key = 'queries' if type_prefix == 'query' else 'transitions' - items = category.get(items_key, {}) - for item_key, item in items.items(): - html_content += generate_operation_entry(item_key, item, type_prefix) - - html_content += '
    ' - return html_content - -def generate_operation_entry(operation_key, operation, type_prefix): - """Generate documentation for a single operation""" - html_content = f'''
    -

    {operation.get('label', operation_key)}

    -

    {operation.get('description', 'No description available')}

    - -
    -
    Parameters:
    -''' - - # Use sdk_params if available (for state transitions), otherwise use inputs - sdk_params = operation.get('sdk_params', []) - inputs = operation.get('inputs', []) - params_to_use = sdk_params if sdk_params else inputs - - if not params_to_use: - html_content += '

    No parameters required

    ' - else: - for param in params_to_use: - html_content += generate_parameter_entry(param) - - html_content += '''
    - -
    -
    Example
    -''' - - if type_prefix == 'query': - example_code = generate_example_code(operation_key, inputs) - html_content += f'
    {example_code}
    \n' - - # Special handling for certain operations - if operation_key == 'waitForStateTransitionResult': - html_content += '

    This is an internal query used to wait for and retrieve the result of a previously submitted state transition. It requires a valid state transition hash from a prior operation.

    ' - else: - html_content += f' ' - if operation_key in ['getPathElements', 'getContestedResourceVotersForIdentity']: - html_content += ' 🚧 Work in Progress' - - # Add special examples and info - if operation_key == 'getIdentityKeys': - html_content += f''' -
    -
    - -
    -
    Example 2 - Get Specific Keys
    -
    return await window.wasmFunctions.get_identity_keys(sdk, \'5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk\', \'specific\', [0, 1, 2]);
    - -
    -
    - -
    -
    Example 3 - Search Keys by Purpose
    -
    return await window.wasmFunctions.get_identity_keys(sdk, \'5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk\', \'search\', undefined, \'{{"0": {{"0": "current"}}, "1": {{"0": "current"}}}}\');
    - -
    -
    -
    ''' - return html_content - elif operation_key == 'getPathElements': - html_content += generate_path_elements_info() - - html_content += f'\n
    ' - else: - # State transitions don't have run buttons - html_content += f'
    {generate_transition_example(operation_key, operation)}
    ' - - html_content += ''' - -''' - return html_content - -def generate_parameter_entry(param): - """Generate documentation for a single parameter""" - required_text = '(required)' if param.get('required', False) else '(optional)' - html_content = f'''
    - {param.get('label', param.get('name', 'Unknown'))} - {param.get('type', 'text')} - {required_text} -''' - if param.get('description'): - html_content += f'
    {html_lib.escape(param.get("description"))}\n' - elif param.get('placeholder'): - html_content += f'
    Example: {html_lib.escape(param.get("placeholder"))}\n' - elif param.get('name') == 'limit' and not param.get('required', False): - html_content += '
    Default: 100 (maximum items returned if not specified)\n' - if param.get('options'): - html_content += '
    Options: ' - opts = [f'{opt.get("label", opt.get("value"))}' for opt in param.get('options', [])] - html_content += ', '.join(opts) - html_content += '\n' - html_content += '
    \n' - return html_content - -def generate_transition_example(trans_key, transition=None): - """Generate example code for state transitions""" - # Check if there's a custom sdk_example - if transition and transition.get('sdk_example'): - return transition.get('sdk_example') - - if trans_key == 'documentCreate': - return '''const result = await sdk.document_create( - identityHex, - contractId, - "note", - JSON.stringify({ message: "Hello!" }), - privateKeyHex -);''' - elif trans_key == 'tokenTransfer': - return '''const result = await sdk.token_transfer( - identityHex, - contractId, - tokenId, - 1000000, // amount - recipientId, - privateKeyHex -);''' - else: - return f'const result = await sdk.{trans_key}(identityHex, /* params */, privateKeyHex);' - -def generate_path_elements_info(): - """Generate path elements documentation""" - return '''\n
    -
    Common Path Values:
    - - - - - - - - - - - - - - - - -
    Path ValueDescription
    64Data Contract Documents
    32Identities
    24Unique Public Key Hashes to Identities
    8Non-Unique Public Key Hashes to Identities
    16Tokens
    48Pools
    40Prefunded Specialized Balances
    72Spent Asset Lock Transactions
    80Withdrawal Transactions
    88Group Actions
    96Balances
    104Misc
    112Votes
    120Versions
    -
    Example Paths:
    -
      -
    • [32, identity_id] - Access identity data
    • -
    • [96, identity_id] - Access identity balance
    • -
    • [16, contract_id, token_id] - Access token info
    • -
    • [64, contract_id, 1, document_type] - Access documents by type
    • -
    • [88, contract_id, group_position] - Access group actions
    • -
    • [112] - Access votes tree
    • -
    -
    ''' - - -def generate_docs_javascript(): - """Generate the main JavaScript module for docs.html""" - - # Generate function lists dynamically from FUNCTION_NAME_MAP - wasm_functions = list(dict.fromkeys(FUNCTION_NAME_MAP.values())) - - # Additional functions not in FUNCTION_NAME_MAP - additional_functions = ['prefetch_trusted_quorums_testnet'] - - # For imports: init is default export, others are named exports - named_imports = ['WasmSdkBuilder', *wasm_functions, *additional_functions] - named_imports_str = ',\n '.join(named_imports) - - # For window.wasmFunctions: only include the actual functions (not init/WasmSdkBuilder) - window_functions = wasm_functions # Note: excluding prefetch_trusted_quorums_testnet from window object - window_assignments = ',\n '.join(window_functions) - - return ''' import init, { - ''' + named_imports_str + ''' - } from './pkg/wasm_sdk.js'; - - let sdk = null; - let isInitialized = false; - - // Make functions available globally for the examples - window.wasmFunctions = { - ''' + window_assignments + ''' - }; - - // Progress update function - function updateProgress(percent, text) { - const progressFill = document.getElementById('progressFill'); - const progressPercent = document.getElementById('progressPercent'); - const preloaderText = document.querySelector('.preloader-text'); - - if (progressFill) progressFill.style.width = percent + '%'; - if (progressPercent) progressPercent.textContent = percent + '%'; - if (preloaderText && text) preloaderText.textContent = text; - } - - async function checkWasmCached() { - if ('caches' in window) { - try { - const cache = await caches.open('wasm-sdk-cache-v7'); - const wasmResponse = await cache.match('/pkg/wasm_sdk_bg.wasm'); - const jsResponse = await cache.match('/pkg/wasm_sdk.js'); - return wasmResponse && jsResponse; - } catch (e) { - return false; - } - } - return false; - } - - async function initializeSdk() { - if (isInitialized) return; - - const preloader = document.getElementById('preloader'); - const isCached = await checkWasmCached(); - - // Only show preloader if not cached - if (!isCached || window.location.search.includes('force-reload')) { - preloader.classList.add('preloader--visible'); - } - - try { - const startTime = performance.now(); - - if (!isCached) { - updateProgress(10, 'Loading WASM module...'); - } else { - updateProgress(50, 'Loading from cache...'); - } - - await init(); - - if (!isCached) { - updateProgress(50, 'Initializing SDK...'); - } else { - updateProgress(80, 'Initializing SDK...'); - } - - updateProgress(80, 'Prefetching quorum information...'); - await prefetch_trusted_quorums_testnet(); - - updateProgress(85, 'Building SDK...'); - sdk = await WasmSdkBuilder.new_testnet_trusted().build(); - updateProgress(90, 'Finalizing...'); - - isInitialized = true; - const endTime = performance.now(); - const loadTime = endTime - startTime; - console.log(`SDK initialized in ${loadTime.toFixed(2)}ms (${isCached ? 'from cache' : 'fresh load'})`); - - updateProgress(100, 'Ready!'); - - // Hide preloader faster if cached - setTimeout(() => { - preloader.classList.remove('preloader--visible'); - }, isCached ? 200 : 500); - } catch (error) { - console.error('Failed to initialize SDK:', error); - sdk = null; - isInitialized = false; - preloader.classList.remove('preloader--visible'); - throw error; - } - } - - window.runExample = async function(exampleId) { - const button = document.getElementById(`run-${exampleId}`); - const result = document.getElementById(`result-${exampleId}`); - - button.disabled = true; - button.innerHTML = ' Running...'; - result.style.display = 'none'; - - if (!isInitialized) { - await initializeSdk(); - } - - if (!sdk) { - result.className = 'example-result error'; - result.textContent = 'Error: SDK failed to initialize. Check console for details.'; - result.style.display = 'block'; - button.disabled = false; - button.innerHTML = 'Run'; - return; - } - - try { - const code = document.getElementById(`code-${exampleId}`).textContent; - const func = new Function('sdk', 'return (async () => { ' + code + ' })()'); - let output = await func(sdk); - - result.className = 'example-result success'; - result.textContent = JSON.stringify(output, null, 2); - result.style.display = 'block'; - } catch (error) { - // Check if it's a quorum cache error and retry once - if (error.message && error.message.includes('Quorum not found in cache')) { - console.log('Quorum cache miss, retrying with fresh quorum prefetch...'); - try { - // Reinitialize SDK to refresh quorum cache - sdk = null; - isInitialized = false; - - // Prefetch quorums again to ensure we have the latest - await prefetch_trusted_quorums_testnet(); - await initializeSdk(); - - if (sdk) { - const code = document.getElementById(`code-${exampleId}`).textContent; - const func = new Function('sdk', 'return (async () => { ' + code + ' })()'); - const output = await func(sdk); - - result.className = 'example-result success'; - result.textContent = JSON.stringify(output, null, 2); - result.style.display = 'block'; - } else { - throw new Error('Failed to reinitialize SDK'); - } - } catch (retryError) { - result.className = 'example-result error'; - result.textContent = 'Error after retry: ' + retryError.message; - result.style.display = 'block'; - } - } else { - result.className = 'example-result error'; - result.textContent = 'Error: ' + error.message; - result.style.display = 'block'; - } - } finally { - button.disabled = false; - button.innerHTML = 'Run'; - } - }; - - // Register service worker for caching - if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/service-worker-simple.js') - .then(registration => { - console.log('ServiceWorker registered:', registration.scope); - }) - .catch(error => { - console.error('ServiceWorker registration failed:', error); - }); - } - - // Initialize SDK when page loads - window.addEventListener('DOMContentLoaded', async () => { - console.log('[Performance] Starting WASM module load...'); - try { - await initializeSdk(); - } catch (error) { - console.error('Failed to initialize SDK on page load:', error); - } - }); - - // Search functionality - window.addEventListener('DOMContentLoaded', function() { - const searchInput = document.getElementById('sidebar-search'); - const sidebarItems = document.querySelectorAll('.sidebar li'); - const noResults = document.getElementById('no-results'); - const categories = document.querySelectorAll('.sidebar .category'); - const sectionHeaders = document.querySelectorAll('.sidebar .section-header'); - - searchInput.addEventListener('input', function(e) { - const searchTerm = e.target.value.toLowerCase(); - let hasResults = false; - - // Hide all categories and section headers initially - categories.forEach(cat => { - cat.style.display = searchTerm ? 'none' : 'block'; - }); - sectionHeaders.forEach(header => { - header.style.display = searchTerm ? 'none' : 'block'; - }); - - sidebarItems.forEach(item => { - const link = item.querySelector('a'); - if (link) { - const text = link.textContent.toLowerCase(); - const matches = text.includes(searchTerm); - - if (searchTerm === '') { - item.classList.remove('hidden'); - hasResults = true; - } else if (matches) { - item.classList.remove('hidden'); - hasResults = true; - - // Show the category for this item - let prevSibling = item.previousElementSibling; - while (prevSibling) { - if (prevSibling.classList.contains('category')) { - prevSibling.style.display = 'block'; - break; - } - if (prevSibling.querySelector('strong')) { - // This is a category item - prevSibling.style.display = 'block'; - break; - } - prevSibling = prevSibling.previousElementSibling; - } - } else { - item.classList.add('hidden'); - } - } - }); - - // Show/hide no results message - noResults.style.display = hasResults ? 'none' : 'block'; - }); - });''' - - -def generate_test_runner_javascript(): - """Generate the test runner JavaScript for the hidden testing feature""" - return ''' // Hidden test runner feature - - // Create the test runner UI - const testRunnerHTML = ` - - `; - - // Add test runner to body - document.body.insertAdjacentHTML('beforeend', testRunnerHTML); - - // Get references to elements - const queriesHeader = document.querySelector('.section-header'); - const testRunner = document.getElementById('test-runner'); - const closeButton = document.getElementById('close-test-runner'); - const runAllButton = document.getElementById('run-all-tests'); - const testProgress = document.getElementById('test-progress'); - const testSummary = document.getElementById('test-summary'); - const testResults = document.getElementById('test-results'); - - // Show test runner - function showTestRunner() { - testRunner.style.display = 'block'; - document.body.style.overflow = 'hidden'; // Prevent scrolling - } - - // Hide test runner - function hideTestRunner() { - testRunner.style.display = 'none'; - document.body.style.overflow = 'auto'; - } - - // Run a single test - async function runSingleTest(buttonId) { - try { - const functionId = buttonId.replace('run-', ''); - - // Call the existing runExample function - await window.runExample(functionId); - - // Wait a bit for the result to be displayed - await new Promise(resolve => setTimeout(resolve, 100)); - - // Check the result element to see if it succeeded - const resultDiv = document.getElementById(`result-${functionId}`); - if (resultDiv && resultDiv.style.display !== 'none') { - const hasError = resultDiv.className && resultDiv.className.includes('error'); - if (hasError) { - const errorText = resultDiv.textContent || 'Unknown error'; - return { id: buttonId, success: false, error: errorText }; - } - return { id: buttonId, success: true }; - } - - // If no result div or not displayed, assume it worked - return { id: buttonId, success: true }; - } catch (error) { - return { id: buttonId, success: false, error: error.message || error.toString() }; - } - } - - // Run all tests - async function runAllTests() { - testProgress.textContent = 'Starting tests...'; - testResults.innerHTML = ''; - testSummary.textContent = ''; - - // Find all run buttons - const runButtons = document.querySelectorAll('.run-button'); - const totalTests = runButtons.length; - let passed = 0; - let failed = 0; - let currentTest = 0; - - const results = []; - - for (const button of runButtons) { - currentTest++; - const testName = button.id.replace('run-', ''); - testProgress.textContent = `Running test ${currentTest} of ${totalTests}: ${testName}...`; - - const result = await runSingleTest(button.id); - - if (result.success) { - passed++; - results.push(`
    βœ… ${testName}: PASSED
    `); - } else { - failed++; - results.push(`
    ❌ ${testName}: FAILED - ${result.error}
    `); - } - - // Update results in real-time - testResults.innerHTML = results.join(''); - } - - testProgress.textContent = 'All tests completed!'; - testSummary.innerHTML = ` -
    Total: ${totalTests}
    -
    Passed: ${passed}
    -
    Failed: ${failed}
    -
    Success Rate: ${((passed/totalTests) * 100).toFixed(1)}%
    - `; - } - - // Set up triple-click detection on Queries header - if (queriesHeader) { - let clickCount = 0; - let clickTimer = null; - - queriesHeader.addEventListener('click', () => { - clickCount++; - - // Reset click count after 500ms - if (clickTimer) { - clearTimeout(clickTimer); - } - - clickTimer = setTimeout(() => { - clickCount = 0; - }, 500); - - // Show test runner on triple click - if (clickCount === 3) { - showTestRunner(); - clickCount = 0; - if (clickTimer) { - clearTimeout(clickTimer); - clickTimer = null; - } - } - }); - } - - // Close button handler - closeButton.addEventListener('click', hideTestRunner); - - // Run all tests button handler - runAllButton.addEventListener('click', runAllTests); - - // Close on escape key - document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && testRunner.style.display !== 'none') { - hideTestRunner(); - } - }); - - // Make runExample function available globally if it doesn't exist - if (!window.runExample) { - window.runExample = async function(exampleId) { - const resultDiv = document.getElementById('result-' + exampleId); - const codeElement = document.getElementById('code-' + exampleId); - const button = document.getElementById('run-' + exampleId); - - if (!resultDiv || !codeElement || !button) return; - - // Disable button - button.disabled = true; - button.textContent = 'Running...'; - - try { - // Clear previous results - resultDiv.innerHTML = '
    Executing...
    '; - - // Execute the example code - const code = codeElement.textContent; - const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; - const testFunc = new AsyncFunction('window', 'sdk', code); - - if (!window.sdk) { - throw new Error('SDK not initialized'); - } - - const result = await testFunc(window, window.sdk); - - // Display result - resultDiv.className = 'example-result success'; - resultDiv.innerHTML = '
    ' + JSON.stringify(result, null, 2) + '
    '; - resultDiv.style.display = 'block'; - } catch (error) { - resultDiv.className = 'example-result error'; - resultDiv.textContent = 'Error: ' + (error.message || error); - resultDiv.style.display = 'block'; - } finally { - // Re-enable button - button.disabled = false; - button.textContent = 'Run'; - } - }; - }''' - -def generate_html_head(): - """Generate HTML head section with meta tags and scripts""" - return ''' - - Dash Platform WASM JS SDK Documentation - - - - ''' - - -def generate_preloader_html(): - """Generate preloader HTML section""" - return ''' -
    -
    -
    Loading WASM module...
    -
    -
    -
    -
    -
    0%
    -
    -
    -
    ''' - - -def generate_sidebar_html(query_defs, transition_defs): - """Generate sidebar HTML section with table of contents""" - sidebar_html = ''' - ''' - - return sidebar_html - - -def generate_main_content_html(query_defs, transition_defs): - """Generate main content HTML section with documentation""" - main_content_html = ''' -
    - - -

    Dash Platform WASM JS SDK Documentation

    - -
    -

    Overview

    -

    The Dash Platform WASM JS SDK provides a WebAssembly-based interface for interacting with the Dash Platform from JavaScript and TypeScript applications. - This documentation covers all available queries and state transitions.

    - -

    Key Concepts

    -
      -
    • Queries: Read-only operations that fetch data from the platform
    • -
    • State Transitions: Write operations that modify state on the platform
    • -
    • Proofs: Cryptographic proofs can be requested for most queries to verify data authenticity
    • -
    • Credits: The platform's unit of account for paying transaction fees
    • -
    • Default Limits: All queries with optional limit parameters default to a maximum of 100 items if not specified
    • -
    - -

    Note: All examples below can be run directly in your browser using the testnet. Click the "Run" button to execute any example.

    - -
    - Test Identity: All examples use the testnet identity 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk -
    This is a known identity with activity on testnet that you can use for testing. -
    -
    - -

    Queries

    -''' - - # Add query documentation - main_content_html += generate_operation_docs(query_defs, 'query', 'query') - - # Add state transition documentation - main_content_html += '

    State Transitions

    ' - main_content_html += generate_operation_docs(transition_defs, 'transition', 'transition') - - # Close main content div - main_content_html += ''' - ↑ Top -
    ''' - - return main_content_html - -def generate_user_docs_html(query_defs, transition_defs): - """Generate user-friendly HTML documentation""" - - html_content = ''' - - -''' + generate_html_head() + ''' - - -''' + generate_preloader_html() + ''' - -''' + generate_sidebar_html(query_defs, transition_defs) + ''' - -''' + generate_main_content_html(query_defs, transition_defs) + ''' - - - - - - -''' - - return html_content - -def generate_ai_reference_md(query_defs, transition_defs): - """Generate AI-friendly markdown reference""" - - md_content = '''# Dash Platform WASM JS SDK - AI Reference - -## Overview -The Dash Platform WASM JS SDK provides WebAssembly bindings for interacting with Dash Platform from JavaScript/TypeScript. This reference is optimized for AI understanding and quick implementation. - -## Quick Setup -```javascript -// Import and initialize -import init, { WasmSdk } from './pkg/wasm_sdk.js'; - -await init(); -const transport = { - url: "https://52.12.176.90:1443/", // testnet - network: "testnet" -}; -const proofs = true; // Enable proof verification -const sdk = await WasmSdk.new(transport, proofs); -``` - -## Authentication -Most state transitions require authentication: -```javascript -const identityHex = "hex_encoded_identity"; -const privateKeyHex = "hex_encoded_private_key"; -``` - -## Query Operations - -### Pattern -All queries follow this pattern: -```javascript -const result = await sdk.{query_name}(param1, param2, ...); -``` - -### Available Queries -''' - - # Document all queries - for cat_key, category in query_defs.items(): - md_content += f"\n#### {category.get('label', cat_key)}\n" - - queries = category.get('queries', {}) - for query_key, query in queries.items(): - md_content += f"\n**{query.get('label', query_key)}** - `{query_key}`\n" - md_content += f"*{query.get('description', 'No description')}*\n\n" - - # Parameters - inputs = query.get('inputs', []) - if inputs: - md_content += "Parameters:\n" - for param in inputs: - req = "required" if param.get('required', False) else "optional" - md_content += f"- `{param.get('name', 'unknown')}` ({param.get('type', 'text')}, {req})" - - if param.get('label') and param.get('label') != param.get('name'): - md_content += f" - {param.get('label')}" - - if param.get('placeholder'): - md_content += f"\n - Example: `{param.get('placeholder')}`" - - if param.get('options'): - md_content += "\n - Options: " - opts = [f"`{opt.get('value')}` ({opt.get('label')})" for opt in param.get('options', [])] - md_content += ', '.join(opts) - - md_content += "\n" - else: - md_content += "No parameters required.\n" - - # Example - md_content += f"\nExample:\n```javascript\n" - - # Generate example based on query type - if query_key == 'getIdentity': - md_content += 'const identity = await sdk.getIdentity("GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec");' - elif query_key == 'getDocuments': - md_content += '''const docs = await sdk.getDocuments( - contractId, - "note", - JSON.stringify([["$ownerId", "==", identityId]]), - JSON.stringify([["$createdAt", "desc"]]), - 10 -);''' - elif query_key == 'getIdentityBalance': - md_content += 'const balance = await sdk.getIdentityBalance(identityId);' - elif query_key == 'getPathElements': - md_content += '''// Access any data in the Dash Platform state tree -// Common root paths: -// - DataContractDocuments: 64 -// - Identities: 32 -// - UniquePublicKeyHashesToIdentities: 24 -// - NonUniquePublicKeyKeyHashesToIdentities: 8 -// - Tokens: 16 -// - Pools: 48 -// - PreFundedSpecializedBalances: 40 -// - SpentAssetLockTransactions: 72 -// - WithdrawalTransactions: 80 -// - GroupActions: 88 -// - Balances: 96 -// - Misc: 104 -// - Votes: 112 -// - Versions: 120 - -// Example: Get identity balance -const result = await sdk.getPathElements(['96'], ['identityId']); - -// Example: Get identity info -const identityKeys = await sdk.getPathElements(['32'], ['identityId']); - -// Example: Get contract documents -const documents = await sdk.getPathElements(['64'], ['contractId', '1', 'documentType']);''' - else: - # Generic example - params = [] - for inp in inputs: - if inp.get('required', False): - if inp.get('type') == 'array': - params.append('[]') - elif inp.get('type') == 'number': - params.append('100') - else: - params.append(f'"{inp.get("name", "value")}"') - md_content += f'const result = await sdk.{query_key}({", ".join(params)});' - - md_content += "\n```\n" - - # Document state transitions - md_content += "\n## State Transition Operations\n\n### Pattern\n" - md_content += "All state transitions require authentication and follow this pattern:\n" - md_content += "```javascript\nconst result = await sdk.{transition_name}(identityHex, ...params, privateKeyHex);\n```\n" - - md_content += "\n### Available State Transitions\n" - - for cat_key, category in transition_defs.items(): - md_content += f"\n#### {category.get('label', cat_key)}\n" - - transitions = category.get('transitions', {}) - for trans_key, transition in transitions.items(): - md_content += f"\n**{transition.get('label', trans_key)}** - `{trans_key}`\n" - md_content += f"*{transition.get('description', 'No description')}*\n\n" - - # Parameters - use sdk_params if available, otherwise fall back to inputs - sdk_params = transition.get('sdk_params', []) - inputs = transition.get('inputs', []) - params_to_use = sdk_params if sdk_params else inputs - - # Adjust parameter section header based on whether we're using SDK params - if sdk_params: - md_content += "Parameters:\n" - elif inputs: - md_content += "Parameters (in addition to identity/key):\n" - - if params_to_use: - for param in params_to_use: - req = "required" if param.get('required', False) else "optional" - md_content += f"- `{param.get('name', 'unknown')}` ({param.get('type', 'text')}, {req})" - - if param.get('label') and param.get('label') != param.get('name'): - md_content += f" - {param.get('label')}" - - if param.get('description'): - md_content += f"\n - {param.get('description')}" - elif param.get('placeholder'): - md_content += f"\n - Example: `{param.get('placeholder')}`" - - md_content += "\n" - - # Example - md_content += f"\nExample:\n```javascript\n" - - # Check if there's a custom sdk_example - sdk_example = transition.get('sdk_example') - if sdk_example: - md_content += sdk_example - elif trans_key == 'documentCreate': - md_content += '''const result = await sdk.document_create( - identityHex, - contractId, - "note", - JSON.stringify({ message: "Hello!" }), - privateKeyHex -);''' - elif trans_key == 'tokenTransfer': - md_content += '''const result = await sdk.token_transfer( - identityHex, - contractId, - tokenId, - 1000000, // amount - recipientId, - privateKeyHex -);''' - else: - md_content += f'const result = await sdk.{trans_key}(identityHex, /* params */, privateKeyHex);' - - md_content += "\n```\n" - - # Add common patterns section - md_content += ''' -## Common Patterns - -### Error Handling -```javascript -try { - const result = await sdk.getIdentity(identityId); - console.log(result); -} catch (error) { - console.error("Query failed:", error); -} -``` - -### Working with Proofs -```javascript -// Enable proofs during SDK initialization -const sdk = await WasmSdk.new(transport, true); - -// Query with proof verification -const identityWithProof = await sdk.getIdentity(identityId); -``` - -### Document Queries with Where/OrderBy -```javascript -// Where clause format: [[field, operator, value], ...] -const whereClause = JSON.stringify([ - ["$ownerId", "==", identityId], - ["age", ">=", 18] -]); - -// OrderBy format: [[field, direction], ...] -const orderBy = JSON.stringify([ - ["$createdAt", "desc"] -]); - -const docs = await sdk.getDocuments( - contractId, - documentType, - whereClause, - orderBy, - limit -); -``` - -### Batch Operations -```javascript -// Get multiple identities -const identityIds = ["id1", "id2", "id3"]; -const balances = await sdk.getIdentitiesBalances(identityIds); -``` - -## Important Notes - -1. **Network Endpoints**: - - Testnet: `https://52.12.176.90:1443/` - - Mainnet: Update when available - -2. **Identity Format**: Identity IDs and keys should be hex-encoded strings - -3. **Credits**: All fees are paid in credits (1 credit = 1 satoshi equivalent) - -4. **Nonces**: The SDK automatically handles nonce management for state transitions - -5. **Proofs**: Enable proofs for production applications to ensure data integrity - -## Troubleshooting - -- **Connection errors**: Verify network endpoint and that SDK is initialized -- **Invalid parameters**: Check parameter types and required fields -- **Authentication failures**: Ensure correct identity/key format and key permissions -- **Query errors**: Validate contract IDs, document types, and field names exist -''' - - return md_content - -def main(): - """Main function to generate documentation""" - - # Get paths - script_dir = Path(__file__).parent - - # Load API definitions from api-definitions.json - api_definitions_file = script_dir / 'api-definitions.json' - - if not api_definitions_file.exists(): - print(f"Error: api-definitions.json not found at {api_definitions_file}") - return 1 - - print("Loading API definitions from api-definitions.json...") - try: - query_defs, transition_defs = load_api_definitions(api_definitions_file) - - # Check if loading failed (returns empty dictionaries on error) - if not query_defs or not transition_defs: - print("Error: Failed to load API definitions or definitions are empty") - return 1 - - # Validate that we have actual data - query_count = sum(len(cat.get('queries', {})) for cat in query_defs.values()) - transition_count = sum(len(cat.get('transitions', {})) for cat in transition_defs.values()) - - if query_count == 0 and transition_count == 0: - print("Error: No queries or state transitions found in API definitions") - return 1 - - print(f"Found {query_count} queries") - print(f"Found {transition_count} state transitions") - - except Exception as e: - print(f"Error: Failed to load API definitions: {e}") - return 1 - - # API definitions are already clean from JSON source - no cleanup needed - - # Generate user docs - print("\nGenerating user documentation (docs.html)...") - user_docs_html = generate_user_docs_html(query_defs, transition_defs) - docs_file = script_dir / 'docs.html' - with open(docs_file, 'w', encoding='utf-8') as f: - f.write(user_docs_html) - print(f"Written to {docs_file}") - - # Generate AI reference - print("\nGenerating AI reference (AI_REFERENCE.md)...") - ai_reference_md = generate_ai_reference_md(query_defs, transition_defs) - ai_ref_file = script_dir / 'AI_REFERENCE.md' - with open(ai_ref_file, 'w', encoding='utf-8') as f: - f.write(ai_reference_md) - print(f"Written to {ai_ref_file}") - - # Generate documentation manifest for CI checks - manifest = { - "generated_at": datetime.now(timezone.utc).isoformat(), - "queries": {}, - "transitions": {} - } - - for cat_key, category in query_defs.items(): - for query_key in category.get('queries', {}).keys(): - manifest["queries"][query_key] = { - "category": cat_key, - "documented": True - } - - for cat_key, category in transition_defs.items(): - for trans_key in category.get('transitions', {}).keys(): - manifest["transitions"][trans_key] = { - "category": cat_key, - "documented": True - } - - manifest_file = script_dir / 'docs_manifest.json' - with open(manifest_file, 'w', encoding='utf-8') as f: - json.dump(manifest, f, indent=2) - print(f"\nGenerated documentation manifest at {manifest_file}") - - print("\nDocumentation generation complete!") - return 0 - -if __name__ == "__main__": - exit(main()) \ No newline at end of file diff --git a/packages/wasm-sdk/index.css b/packages/wasm-sdk/index.css deleted file mode 100644 index 3e74fba1400..00000000000 --- a/packages/wasm-sdk/index.css +++ /dev/null @@ -1,775 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - background-color: #f5f5f5; -} - -.header { - background-color: #2c3e50; - color: white; - padding: 15px 20px; - display: flex; - justify-content: space-between; - align-items: center; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} - -.header h1 { - margin: 0; - font-size: 24px; - font-weight: 500; -} - -.header-nav { - display: flex; - gap: 20px; -} - -.header-nav a { - color: white; - text-decoration: none; - font-weight: 500; - transition: opacity 0.2s; -} - -.header-nav a:hover { - opacity: 0.8; -} - -#preloader { - display: none; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: rgba(0, 0, 0, 0.9); - color: white; - padding: 30px; - border-radius: 8px; - z-index: 1000; - min-width: 300px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); -} - -.preloader-content { - text-align: center; -} - -.preloader-text { - font-size: 16px; - margin-bottom: 15px; -} - -.progress-bar { - width: 100%; - height: 20px; - background: rgba(255, 255, 255, 0.1); - border-radius: 10px; - overflow: hidden; - margin-bottom: 10px; -} - -.progress-fill { - height: 100%; - background: linear-gradient(90deg, #4CAF50, #45a049); - width: 0%; - transition: width 0.3s ease; - border-radius: 10px; -} - -.progress-percent { - font-size: 14px; - font-weight: bold; - color: #4CAF50; -} - -.app-container { - display: flex; - height: calc(100vh - 110px); /* Account for header and status banner height */ - overflow: hidden; -} - -.sidebar { - width: 450px; - background-color: white; - border-right: 1px solid #e0e0e0; - display: flex; - flex-direction: column; - overflow-y: auto; -} - -.network-toggle { - padding: 20px; - background-color: #f8f9fa; - border-bottom: 1px solid #e0e0e0; -} - -.network-toggle label { - margin-right: 15px; - font-weight: 500; - color: #333; -} - -.network-indicator { - display: inline-block; - padding: 5px 10px; - border-radius: 3px; - margin-left: 10px; - font-size: 0.9em; - font-weight: bold; -} - -.network-indicator.testnet { - background-color: #ff9800; - color: white; -} - -.network-indicator.mainnet { - background-color: #4caf50; - color: white; -} - -.proof-toggle { - padding: 15px 20px; - background-color: #f8f9fa; - border-bottom: 1px solid #e0e0e0; - display: flex; - align-items: center; - justify-content: space-between; -} - -.proof-toggle label { - font-weight: 500; - color: #333; - margin: 0; -} - -.toggle-switch { - position: relative; - display: inline-block; - width: 50px; - height: 24px; -} - -.toggle-switch input { - opacity: 0; - width: 0; - height: 0; -} - -.toggle-slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - transition: .4s; - border-radius: 34px; -} - -.toggle-slider:before { - position: absolute; - content: ""; - height: 16px; - width: 16px; - left: 4px; - bottom: 4px; - background-color: white; - transition: .4s; - border-radius: 50%; -} - -input:checked + .toggle-slider { - background-color: #4CAF50; -} - -input:checked + .toggle-slider:before { - transform: translateX(26px); -} - -/* Style for SDK config details */ -details.sdk-config summary::-webkit-details-marker { - display: none; -} - -details.sdk-config[open] summary span:last-child { - transform: rotate(180deg); - transition: transform 0.2s; -} - -details.sdk-config summary span:last-child { - transition: transform 0.2s; -} - - -.query-container { - padding: 20px; - flex: 1; - overflow-y: auto; -} - -.query-selector { - margin-bottom: 20px; -} - -.query-selector select { - width: 100%; - padding: 10px; - font-size: 14px; - border: 1px solid #ddd; - border-radius: 4px; - background-color: white; - margin-bottom: 10px; -} - -.query-inputs { - background-color: #f8f9fa; - padding: 15px; - border-radius: 8px; - margin-bottom: 15px; -} - -.input-group { - margin-bottom: 15px; -} - -.input-group label { - display: block; - margin-bottom: 5px; - font-weight: 500; - color: #333; -} - -.input-group input[type="text"], -.input-group input[type="password"] { - width: 100%; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-size: 14px; -} - -.query-inputs h4 { - margin: 0 0 15px 0; - color: #333; - font-size: 1em; -} - -.input-group { - margin-bottom: 15px; -} - -.input-group label { - display: block; - margin-bottom: 5px; - color: #555; - font-size: 0.9em; -} - -.input-group input, -.input-group select { - width: 100%; - padding: 8px 10px; - border: 1px solid #ddd; - border-radius: 4px; - font-size: 14px; - box-sizing: border-box; -} - -.input-group input[type="checkbox"] { - width: auto; - margin-right: 5px; -} - -.input-group input[type="number"] { - -moz-appearance: textfield; -} - -.input-group input[type="number"]::-webkit-inner-spin-button, -.input-group input[type="number"]::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0; -} - -.optional-label { - color: #888; - font-size: 0.85em; - font-style: italic; -} - -.execute-button { - width: 100%; - padding: 12px; - background-color: #1976d2; - color: white; - border: none; - border-radius: 4px; - font-size: 16px; - font-weight: 500; - cursor: pointer; - transition: background-color 0.3s; -} - -.execute-button:hover { - background-color: #1565c0; -} - -.execute-button:disabled { - background-color: #ccc; - cursor: not-allowed; -} - -.result-container { - flex: 1; - background-color: white; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.result-split-container { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.result-data-section, -.result-proof-section { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - border-bottom: 1px solid #e0e0e0; -} - -.result-proof-section { - border-bottom: none; - background-color: #f8f9fa; -} - -.section-header { - padding: 10px 20px; - background-color: #e3f2fd; - border-bottom: 1px solid #ccc; - font-weight: 600; - color: #1976d2; - font-size: 14px; -} - -.result-proof-section .section-header { - background-color: #f3e5f5; - color: #7b1fa2; -} - -.result-header { - padding: 20px; - background-color: #f8f9fa; - border-bottom: 1px solid #e0e0e0; - display: flex; - justify-content: space-between; - align-items: center; -} - -.result-header h2 { - margin: 0; - color: #333; - font-size: 1.3em; -} - -.result-actions button { - margin-left: 10px; - padding: 8px 16px; - border: 1px solid #ddd; - background-color: white; - border-radius: 4px; - cursor: pointer; - font-size: 14px; -} - -.result-actions button:hover { - background-color: #f5f5f5; -} - -.result-content { - flex: 1; - padding: 20px; - overflow-y: auto; - font-family: 'Monaco', 'Consolas', 'Courier New', monospace; - font-size: 13px; - line-height: 1.6; - white-space: pre-wrap; - word-wrap: break-word; -} - -.result-content.empty { - color: #888; - font-style: italic; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; -} - -.status-banner { - position: fixed; - bottom: 0; - left: 0; - right: 0; - height: 50px; - padding: 0 20px; - display: flex; - align-items: center; - justify-content: center; - text-align: center; - font-weight: 500; - font-size: 14px; - z-index: 999; - transition: all 0.3s ease; - box-sizing: border-box; -} - -.status-banner.success { - background-color: #4caf50; - color: white; -} - -.status-banner.error { - background-color: #f44336; - color: white; -} - -.status-banner.loading { - background-color: #ff9800; - color: white; -} - -.error-result { - color: #d32f2f; - background-color: #ffebee; - border: 1px solid #ffcdd2; - border-radius: 4px; - padding: 10px; -} - -.credits-value { - color: #1976d2; - cursor: help; - text-decoration: underline; - text-decoration-style: dotted; - text-underline-offset: 3px; - position: relative; -} - -.credits-value:hover { - background-color: #e3f2fd; - border-radius: 3px; - padding: 0 2px; -} - -.credits-value::after { - content: attr(title); - position: absolute; - bottom: 100%; - left: 50%; - transform: translateX(-50%); - background-color: #333; - color: white; - padding: 4px 8px; - border-radius: 4px; - font-size: 12px; - white-space: nowrap; - opacity: 0; - pointer-events: none; - transition: opacity 0.2s; - margin-bottom: 5px; - z-index: 1000; -} - -.credits-value:hover::after { - opacity: 1; -} - -.nonce-value { - color: #9c27b0; - cursor: help; - position: relative; -} - -.nonce-value:hover { - background-color: #f3e5f5; - border-radius: 3px; - padding: 0 2px; -} - -/* Enhanced tooltip for nonce with better formatting */ -.nonce-value::after { - content: attr(title); - position: absolute; - bottom: 100%; - left: 50%; - transform: translateX(-50%); - background-color: #333; - color: white; - padding: 10px; - border-radius: 4px; - font-size: 12px; - font-family: 'Consolas', 'Monaco', 'Courier New', monospace; - white-space: pre; - opacity: 0; - pointer-events: none; - transition: opacity 0.2s; - margin-bottom: 5px; - z-index: 1000; - max-width: 500px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); -} - -.nonce-value:hover::after { - opacity: 1; -} - -.json-key { - color: #d73a49; - font-weight: 500; -} - -.result-content pre { - margin: 0; - font-family: 'Consolas', 'Monaco', 'Courier New', monospace; - font-size: 13px; - line-height: 1.5; -} - -.mode-selector { - margin-bottom: 15px; - display: none; -} - -.array-input-container { - border: 1px solid #ddd; - border-radius: 4px; - padding: 10px; - background-color: #fafafa; -} - -.array-item { - display: flex; - gap: 10px; - margin-bottom: 5px; -} - -.array-item input { - flex: 1; -} - -.array-item button { - padding: 5px 10px; - background-color: #ff5252; - color: white; - border: none; - border-radius: 3px; - cursor: pointer; -} - -.add-array-item { - padding: 5px 15px; - background-color: #4caf50; - color: white; - border: none; - border-radius: 3px; - cursor: pointer; - margin-top: 5px; -} - -.query-description { - font-size: 0.85em; - color: #666; - margin-bottom: 15px; - padding: 10px; - background-color: #e3f2fd; - border-radius: 4px; - line-height: 1.4; -} - -.action-button { - padding: 8px 16px; - background-color: #2196f3; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - margin-top: 10px; -} - -.action-button:hover { - background-color: #1976d2; -} - -.dynamic-fields-container { - margin-top: 10px; - padding: 10px; - border: 1px solid #ddd; - border-radius: 4px; - background-color: #fafafa; -} - -.field-group { - margin-bottom: 10px; - padding: 8px; - background-color: white; - border-radius: 4px; -} - -.field-group label { - display: inline-block; - width: 150px; - font-weight: 500; -} - -.where-field-group { - display: flex; - align-items: center; - gap: 10px; -} - -.operator-select { - width: 80px; - background-color: white; - color: #333; - padding: 8px 35px 8px 10px; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right 10px center; - background-size: 20px; -} - -.operator-select.has-value { - background-image: none; - padding-right: 10px; -} - -.operator-select option { - background-color: white; - color: #333; -} - -.operator-select option:checked { - background-color: #007bff; - color: white; -} - -.where-value-input { - flex: 1; - min-width: 200px; -} - -/* New styles for improved where clause UI */ -.index-property-badge { - display: inline-block; - padding: 2px 8px; - border-radius: 3px; - font-size: 0.85em; - margin-left: 5px; - font-weight: 500; -} - -.index-property-first { - background-color: #4caf50; - color: white; -} - -.index-property-second { - background-color: #2196f3; - color: white; -} - -.index-property-third { - background-color: #ff9800; - color: white; -} - -.compound-index-warning { - background-color: #fff3cd; - border: 1px solid #ffeaa7; - color: #856404; - padding: 10px; - border-radius: 4px; - margin: 10px 0; - font-size: 0.9em; -} - -.query-preview { - background-color: #f5f5f5; - border: 1px solid #ddd; - padding: 10px; - border-radius: 4px; - margin-top: 15px; - font-family: monospace; - font-size: 0.9em; -} - -.query-preview h5 { - margin-top: 0; - color: #333; -} - -.operator-help { - font-size: 0.85em; - color: #666; - margin-left: 5px; - cursor: help; -} - -.range-group { - border: 1px solid #e0e0e0; - padding: 10px; - border-radius: 4px; - margin-bottom: 10px; - background-color: #f9f9f9; -} - -.range-group-header { - font-weight: 500; - margin-bottom: 8px; - color: #1976d2; -} - -.add-range-button { - padding: 4px 12px; - background-color: #1976d2; - color: white; - border: none; - border-radius: 3px; - cursor: pointer; - font-size: 0.85em; - margin-left: 10px; -} - -.add-range-button:hover { - background-color: #1565c0; -} - -.between-info { - background-color: #e3f2fd; - padding: 8px; - border-radius: 3px; - margin-top: 8px; - font-size: 0.85em; - color: #1565c0; -} - -.validation-error { - color: #d32f2f; - font-size: 0.85em; - margin-top: 5px; -} - -.field-type-indicator { - font-size: 0.8em; - color: #666; - margin-left: 5px; - font-style: italic; -} \ No newline at end of file diff --git a/packages/wasm-sdk/index.html b/packages/wasm-sdk/index.html deleted file mode 100644 index f5597f4bbb1..00000000000 --- a/packages/wasm-sdk/index.html +++ /dev/null @@ -1,5167 +0,0 @@ - - - - - - - - Dash Platform WASM JS SDK - - - - - - -
    -

    Dash Platform WASM JS SDK

    - -
    - - - - -
    -
    -
    Loading WASM module...
    -
    -
    -
    -
    -
    0%
    -
    -
    -
    - -
    - - -
    -
    -

    Results

    -
    - - - -
    -
    -
    -
    No data fetched yet. Select a query category and type to begin.
    -
    -
    -
    - -
    Initializing WASM SDK...
    - - - - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/package.json b/packages/wasm-sdk/package.json index 40e11909145..10e86a6f69e 100644 --- a/packages/wasm-sdk/package.json +++ b/packages/wasm-sdk/package.json @@ -1,14 +1,68 @@ { - "name": "wasm-sdk", - "version": "0.0.0", - "private": true, + "name": "@dashevo/wasm-sdk", + "version": "2.0.0", + "type": "module", + "main": "./dist/sdk.js", + "types": "./dist/sdk.d.ts", + "exports": { + ".": { + "types": "./dist/sdk.d.ts", + "import": "./dist/sdk.js" + }, + "./raw": { + "types": "./dist/raw/wasm_sdk.d.ts", + "import": "./dist/raw/wasm_sdk.js" + }, + "./raw/*": "./dist/raw/*" + }, + "files": [ + "dist/**", + "README.md" + ], + "sideEffects": false, + "engines": { + "node": ">=18.18" + }, "scripts": { - "build": "./build.sh", - "build:release": "./build-optimized.sh", - "test": "echo 'Tests run from web interface at index.html'", - "prepublishOnly": "echo 'Publishing handled by pkg/package.json'" + "build:dev": "./scripts/build.sh && node ./scripts/bundle.cjs", + "build:release": "./scripts/build-optimized.sh && node ./scripts/bundle.cjs", + "build": "yarn build:dev", + "prepack": "yarn build:release", + "test": "yarn run test:unit && yarn run test:functional", + "test:unit": "mocha tests/unit/**/*.spec.mjs && karma start ./tests/karma/karma.conf.cjs --single-run", + "test:functional": "mocha tests/functional/**/*.spec.mjs && karma start ./tests/karma/karma.functional.conf.cjs --single-run", + "lint": "eslint tests/**/*.*js" }, "ultra": { - "concurrent": ["test"] + "concurrent": [ + "test" + ] + }, + "devDependencies": { + "assert": "^2.0.0", + "buffer": "^6.0.3", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", + "dirty-chai": "^2.0.1", + "eslint": "^8.53.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jsdoc": "^46.9.0", + "events": "^3.3.0", + "karma": "^6.4.3", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^2.1.2", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-webpack": "^5.0.0", + "mocha": "^11.1.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "string_decoder": "^1.3.0", + "url": "^0.11.3", + "util": "^0.12.4", + "webpack": "^5.94.0", + "webpack-cli": "^4.9.1" } -} \ No newline at end of file +} diff --git a/packages/wasm-sdk/build-optimized.sh b/packages/wasm-sdk/scripts/build-optimized.sh similarity index 77% rename from packages/wasm-sdk/build-optimized.sh rename to packages/wasm-sdk/scripts/build-optimized.sh index 4bd7a1f1f23..760e4b6c1f8 100755 --- a/packages/wasm-sdk/build-optimized.sh +++ b/packages/wasm-sdk/scripts/build-optimized.sh @@ -11,12 +11,12 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" echo "Building wasm-sdk with full optimization for npm release..." # Call unified build script with full optimization -"$SCRIPT_DIR/../scripts/build-wasm.sh" --package wasm-sdk --opt-level full +"$SCRIPT_DIR/../../scripts/build-wasm.sh" --package wasm-sdk --opt-level full # Additional post-processing for npm release echo "Post-processing for npm release..." -cd "$SCRIPT_DIR/pkg" +cd "$SCRIPT_DIR/../pkg" # Ensure the package.json is correct if [ ! -f "package.json" ]; then @@ -41,10 +41,3 @@ ls -lah WASM_SIZE=$(wc -c < wasm_sdk_bg.wasm) WASM_SIZE_KB=$((WASM_SIZE / 1024)) echo "WASM file size: ${WASM_SIZE_KB}KB" - -# Verify the package.json has correct name -if ! grep -q '"name": "dash"' package.json; then - echo "Warning: package.json does not have 'dash' as the package name" -fi - -echo "Ready for npm publish!" \ No newline at end of file diff --git a/packages/wasm-sdk/build.sh b/packages/wasm-sdk/scripts/build.sh similarity index 84% rename from packages/wasm-sdk/build.sh rename to packages/wasm-sdk/scripts/build.sh index 82dda6e01d4..c063fd387f1 100755 --- a/packages/wasm-sdk/build.sh +++ b/packages/wasm-sdk/scripts/build.sh @@ -17,4 +17,4 @@ if [ "${CARGO_BUILD_PROFILE:-}" = "dev" ] || [ "${CI:-}" != "true" ]; then fi # Call unified build script with default features (no need to specify) -exec "$SCRIPT_DIR/../scripts/build-wasm.sh" --package wasm-sdk --opt-level "$OPT_LEVEL" +exec "$SCRIPT_DIR/../../scripts/build-wasm.sh" --package wasm-sdk --opt-level "$OPT_LEVEL" diff --git a/packages/wasm-sdk/scripts/bundle.cjs b/packages/wasm-sdk/scripts/bundle.cjs new file mode 100644 index 00000000000..3e9455de0ad --- /dev/null +++ b/packages/wasm-sdk/scripts/bundle.cjs @@ -0,0 +1,70 @@ +#!/usr/bin/env node +/* + Prepare publishable artifacts with a single-file SDK wrapper and raw outputs. + - Input: pkg/wasm_sdk.js, pkg/wasm_sdk_bg.wasm, pkg/wasm_sdk.d.ts + - Output: dist/raw/* (unaltered wasm-bindgen outputs), dist/sdk.js (single-file wrapper with inlined WASM), dist/sdk.d.ts + + Notes: + - We keep the exported API identical to wasm_bindgen output, including default export (init) and initSync. + - Wrapper inlines WASM as base64 and provides `await init()` for both Node and browser. + - Node: uses inlined bytes with initSync under the hood (still awaitable). + - Browser: compiles in a Web Worker (fallback to async compile on main thread) and then instantiates. +*/ + +const fs = require('fs'); +const path = require('path'); + +const root = process.cwd(); +const pkgDir = path.join(root, 'pkg'); +const distDir = path.join(root, 'dist'); +const rawDir = path.join(distDir, 'raw'); +const typesDir = path.join(root, 'types'); + +const jsPath = path.join(pkgDir, 'wasm_sdk.js'); +const wasmPath = path.join(pkgDir, 'wasm_sdk_bg.wasm'); +const dtsPath = path.join(pkgDir, 'wasm_sdk.d.ts'); +const wasmDtsPath = path.join(pkgDir, 'wasm_sdk_bg.wasm.d.ts'); + +if (!fs.existsSync(jsPath) || !fs.existsSync(wasmPath) || !fs.existsSync(dtsPath)) { + console.error('Missing build artifacts in pkg/. Run build first.'); + process.exit(1); +} + +// Ensure dist directories and write outputs +fs.mkdirSync(distDir, { recursive: true }); +fs.mkdirSync(rawDir, { recursive: true }); +// Copy raw wasm-bindgen outputs +const rawJs = fs.readFileSync(jsPath, 'utf8'); +fs.writeFileSync(path.join(rawDir, 'wasm_sdk.js'), rawJs); +fs.copyFileSync(wasmPath, path.join(rawDir, 'wasm_sdk_bg.wasm')); +fs.copyFileSync(dtsPath, path.join(rawDir, 'wasm_sdk.d.ts')); +if (fs.existsSync(wasmDtsPath)) { + fs.copyFileSync(wasmDtsPath, path.join(rawDir, 'wasm_sdk_bg.wasm.d.ts')); +} + +// Produce a sanitized variant of wasm_bindgen JS without the default new URL('...wasm') path, +// so downstream bundlers importing the wrapper won't see a literal asset URL and won't emit the .wasm file. +const defaultUrlRegex = /if\s*\(\s*typeof\s+module_or_path\s*===\s*'undefined'\s*\)\s*\{\s*module_or_path\s*=\s*new\s+URL\('wasm_sdk_bg\\.wasm',\s*import\\.meta\\.url\);\s*\}/; +const sanitizedJs = rawJs.replace(defaultUrlRegex, "if (typeof module_or_path === 'undefined') { }"); +fs.writeFileSync(path.join(rawDir, 'wasm_sdk.no_url.js'), sanitizedJs); + +// Build single-file wrapper with inlined WASM and worker-based compile in the browser +const wasmBase64 = fs.readFileSync(wasmPath).toString('base64'); +const wrapper = `// Single-file ESM wrapper around wasm-bindgen output.\n// - Inlines WASM bytes as base64.\n// - Exposes async default init() for both Node and browser.\n// - Browser compiles in a Web Worker and instantiates on main thread (fallback to async compile).\n// - Node uses initSync with inlined bytes (still awaitable for uniform API).\n\nimport rawInit, { initSync as rawInitSync } from './raw/wasm_sdk.no_url.js';\n\nexport * from './raw/wasm_sdk.no_url.js';\nexport { initSync } from './raw/wasm_sdk.no_url.js';\n\nconst __WASM_BASE64 = '${wasmBase64}';\nfunction __wasmBytes() {\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\n return Buffer.from(__WASM_BASE64, 'base64');\n }\n const atobFn = (typeof atob === 'function') ? atob : (s) => globalThis.atob(s);\n const bin = atobFn(__WASM_BASE64);\n const len = bin.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\nfunction __supportsWorker() {\n return typeof Worker !== 'undefined' && typeof Blob !== 'undefined' && typeof URL !== 'undefined';\n}\n\nasync function __compileInWorker(bytes) {\n if (!__supportsWorker()) {\n return WebAssembly.compile(bytes);\n }\n const src = 'self.onmessage=async(e)=>{try{const m=await WebAssembly.compile(e.data);self.postMessage({ok:1,mod:m});}catch(err){self.postMessage({ok:0,err:String(err)});}}';\n const blob = new Blob([src], { type: 'application/javascript' });\n const url = URL.createObjectURL(blob);\n return new Promise((resolve) => {\n const w = new Worker(url);\n w.onmessage = (ev) => {\n URL.revokeObjectURL(url);\n w.terminate();\n const d = ev.data || {};\n if (d.ok && d.mod) {\n resolve(d.mod);\n } else {\n resolve(WebAssembly.compile(bytes));\n }\n };\n // Transfer the underlying buffer to avoid copy\n try {\n w.postMessage(bytes.buffer, [bytes.buffer]);\n } catch (_) {\n // If transfer fails (detached), send a copy\n w.postMessage(new Uint8Array(bytes));\n }\n });\n}\n\nconst isNode = typeof window === 'undefined' && typeof process !== 'undefined' && !!(process.versions && process.versions.node);\n\nexport default async function init(moduleOrPath) {\n if (isNode) {\n if (typeof moduleOrPath === 'undefined') {\n const bytes = __wasmBytes();\n return rawInitSync({ module: bytes });\n }\n return rawInit(moduleOrPath);\n }\n if (typeof moduleOrPath === 'undefined') {\n const bytes = __wasmBytes();\n let mod;\n try {\n mod = await __compileInWorker(bytes);\n } catch (_) {\n mod = await WebAssembly.compile(bytes);\n }\n return rawInit({ module_or_path: mod });\n }\n return rawInit(moduleOrPath);\n}\n`; +fs.writeFileSync(path.join(distDir, 'sdk.js'), wrapper); +fs.copyFileSync(dtsPath, path.join(distDir, 'sdk.d.ts')); + +// Basic report +const outStat = fs.statSync(path.join(distDir, 'sdk.js')); +console.log(`Wrote dist/sdk.js (${outStat.size} bytes) single-file wrapper (inline WASM)`); +console.log('Wrote dist/sdk.d.ts'); +console.log('Copied extra type declarations (if any)'); +console.log('Wrote dist/raw/* (separate JS + WASM)'); + +// Clean up: remove pkg directory after bundling to avoid publishing it +try { + fs.rmSync(pkgDir, { recursive: true, force: true }); + console.log('Removed pkg/ directory after bundling'); +} catch (e) { + console.warn('Warning: failed to remove pkg/ directory:', e?.message || e); +} diff --git a/packages/wasm-sdk/sdk-manager.js b/packages/wasm-sdk/sdk-manager.js deleted file mode 100644 index badd12aee5c..00000000000 --- a/packages/wasm-sdk/sdk-manager.js +++ /dev/null @@ -1,103 +0,0 @@ -// SDK Manager - Simple approach using iframe -class SdkManager { - constructor() { - this.iframe = null; - this.sdk = null; - this.initialized = false; - this.initPromise = null; - } - - async initialize(onProgress) { - if (this.initialized) return this.sdk; - - if (this.initPromise) return this.initPromise; - - this.initPromise = new Promise((resolve, reject) => { - // Check if SDK iframe already exists - let iframe = document.getElementById('sdk-iframe'); - - if (!iframe) { - // Create hidden iframe to hold SDK - iframe = document.createElement('iframe'); - iframe.id = 'sdk-iframe'; - iframe.style.display = 'none'; - iframe.src = '/sdk-loader.html'; - document.body.appendChild(iframe); - } - - this.iframe = iframe; - - // Listen for messages from iframe - const messageHandler = (event) => { - if (event.source !== iframe.contentWindow) return; - - const { type, data } = event.data; - - switch (type) { - case 'sdk-ready': - this.initialized = true; - window.removeEventListener('message', messageHandler); - resolve(true); - break; - - case 'sdk-progress': - if (onProgress) { - onProgress(data.percent, data.text); - } - break; - - case 'sdk-error': - window.removeEventListener('message', messageHandler); - reject(new Error(data.error)); - break; - } - }; - - window.addEventListener('message', messageHandler); - - // Check if iframe is already loaded - if (iframe.contentWindow && iframe.contentWindow.sdkReady) { - this.initialized = true; - window.removeEventListener('message', messageHandler); - resolve(true); - } - }); - - return this.initPromise; - } - - async execute(method, args) { - if (!this.initialized) { - await this.initialize(); - } - - return new Promise((resolve, reject) => { - const id = Math.random().toString(36).substr(2, 9); - - const messageHandler = (event) => { - if (event.source !== this.iframe.contentWindow) return; - if (event.data.id !== id) return; - - window.removeEventListener('message', messageHandler); - - if (event.data.type === 'result') { - resolve(event.data.result); - } else if (event.data.type === 'error') { - reject(new Error(event.data.error)); - } - }; - - window.addEventListener('message', messageHandler); - - this.iframe.contentWindow.postMessage({ - type: 'execute', - id, - method, - args - }, '*'); - }); - } -} - -// Export singleton -export const sdkManager = new SdkManager(); \ No newline at end of file diff --git a/packages/wasm-sdk/service-worker-simple.js b/packages/wasm-sdk/service-worker-simple.js deleted file mode 100644 index 00c4bdaf13f..00000000000 --- a/packages/wasm-sdk/service-worker-simple.js +++ /dev/null @@ -1,106 +0,0 @@ -// Simple cache-first service worker -const CACHE_NAME = 'wasm-sdk-cache-v7'; // Increment to force cache update - -// Files to cache -const urlsToCache = [ - '/pkg/wasm_sdk.js', - '/pkg/wasm_sdk_bg.wasm', - '/pkg/wasm_sdk.d.ts' -]; - -// Install event - pre-cache resources -self.addEventListener('install', event => { - console.log('[SW] Installing and caching files...'); - event.waitUntil( - caches.open(CACHE_NAME) - .then(cache => cache.addAll(urlsToCache)) - .then(() => { - console.log('[SW] All files cached'); - return self.skipWaiting(); - }) - ); -}); - -// Activate event - clean up old caches -self.addEventListener('activate', event => { - console.log('[SW] Activating...'); - event.waitUntil( - caches.keys().then(cacheNames => { - return Promise.all( - cacheNames.map(cacheName => { - if (cacheName !== CACHE_NAME && cacheName.startsWith('wasm-sdk-cache-')) { - console.log('[SW] Deleting old cache:', cacheName); - return caches.delete(cacheName); - } - }) - ); - }).then(() => { - console.log('[SW] Claiming all clients'); - return self.clients.claim(); - }) - ); -}); - -// Fetch event - cache first, with background update -self.addEventListener('fetch', event => { - const url = new URL(event.request.url); - - // Only handle our cached files - if (!urlsToCache.some(path => url.pathname === path)) { - return; - } - - event.respondWith( - caches.match(event.request).then(cachedResponse => { - // Always return from cache if available - if (cachedResponse) { - console.log('[SW] Cache hit:', url.pathname); - - // Update cache in background - event.waitUntil( - fetch(event.request).then(response => { - if (response && response.status === 200) { - const responseToCache = response.clone(); - caches.open(CACHE_NAME).then(cache => { - cache.put(event.request, responseToCache); - console.log('[SW] Cache updated in background:', url.pathname); - }); - } - }).catch(() => { - console.log('[SW] Background update failed, keeping cached version'); - }) - ); - - return cachedResponse; - } - - // Cache miss - fetch and cache - console.log('[SW] Cache miss, fetching:', url.pathname); - return fetch(event.request).then(response => { - if (!response || response.status !== 200) { - return response; - } - - const responseToCache = response.clone(); - caches.open(CACHE_NAME).then(cache => { - cache.put(event.request, responseToCache); - console.log('[SW] Cached:', url.pathname); - }); - - return response; - }); - }) - ); -}); - -// Handle cache clear message -self.addEventListener('message', event => { - if (event.data.action === 'clearCache') { - caches.delete(CACHE_NAME).then(() => { - console.log('[SW] Cache cleared'); - if (event.ports[0]) { - event.ports[0].postMessage({ success: true }); - } - }); - } -}); \ No newline at end of file diff --git a/packages/wasm-sdk/shared-sdk-client.js b/packages/wasm-sdk/shared-sdk-client.js deleted file mode 100644 index 4ee16b9bffa..00000000000 --- a/packages/wasm-sdk/shared-sdk-client.js +++ /dev/null @@ -1,141 +0,0 @@ -// Client for SharedWorker SDK -export class SharedSdkClient { - constructor() { - this.worker = null; - this.port = null; - this.requests = new Map(); - this.requestId = 0; - this.onProgress = null; - this.isConnected = false; - } - - async connect() { - if (this.isConnected) return; - - return new Promise((resolve, reject) => { - try { - // Create or connect to shared worker - this.worker = new SharedWorker('./shared-sdk-worker.js', { - type: 'module', - name: 'dash-wasm-sdk' - }); - - this.port = this.worker.port; - - // Handle messages from worker - this.port.onmessage = (e) => { - const { type, id, result, error, percent, text, initialized } = e.data; - - switch (type) { - case 'connected': - this.isConnected = true; - resolve(initialized); - break; - - case 'progress': - if (this.onProgress) { - this.onProgress(percent, text); - } - break; - - case 'initialized': - // SDK initialization complete - break; - - case 'result': - const request = this.requests.get(id); - if (request) { - request.resolve(result); - this.requests.delete(id); - } - break; - - case 'error': - const errRequest = this.requests.get(id); - if (errRequest) { - errRequest.reject(new Error(error)); - this.requests.delete(id); - } - break; - - case 'status': - const statusRequest = this.requests.get(id); - if (statusRequest) { - statusRequest.resolve(initialized); - this.requests.delete(id); - } - break; - } - }; - - this.port.start(); - - } catch (error) { - reject(error); - } - }); - } - - async initialize() { - if (!this.isConnected) { - await this.connect(); - } - - return this.sendRequest('init'); - } - - async checkStatus() { - if (!this.isConnected) { - await this.connect(); - } - - return this.sendRequest('checkStatus'); - } - - async execute(method, ...args) { - if (!this.isConnected) { - await this.connect(); - } - - return this.sendRequest('execute', { method, args }); - } - - sendRequest(type, data = {}) { - return new Promise((resolve, reject) => { - const id = this.requestId++; - this.requests.set(id, { resolve, reject }); - - this.port.postMessage({ - type, - id, - ...data - }); - }); - } - - // Proxy methods to match SDK interface - get_identity(...args) { - return this.execute('get_identity', ...args); - } - - get_identity_keys(...args) { - return this.execute('get_identity_keys', ...args); - } - - get_identity_balance(...args) { - return this.execute('get_identity_balance', ...args); - } - - get_data_contract(...args) { - return this.execute('get_data_contract', ...args); - } - - get_documents(...args) { - return this.execute('get_documents', ...args); - } - - // Add more proxy methods as needed... -} - -// Create a singleton instance -export const sharedSdk = new SharedSdkClient(); \ No newline at end of file diff --git a/packages/wasm-sdk/shared-sdk-worker.js b/packages/wasm-sdk/shared-sdk-worker.js deleted file mode 100644 index 63c1ce67d23..00000000000 --- a/packages/wasm-sdk/shared-sdk-worker.js +++ /dev/null @@ -1,115 +0,0 @@ -// Shared Worker for WASM SDK -let sdk = null; -let isInitialized = false; -let initializationPromise = null; -const connections = new Set(); - -// Import the WASM SDK -importScripts('./pkg/wasm_sdk.js'); - -async function initializeSdk() { - if (isInitialized) return sdk; - - if (initializationPromise) { - return initializationPromise; - } - - initializationPromise = (async () => { - try { - postMessage({ type: 'progress', percent: 10, text: 'Loading WASM module...' }); - - await wasm_bindgen('./pkg/wasm_sdk_bg.wasm'); - - postMessage({ type: 'progress', percent: 50, text: 'Initializing SDK...' }); - - // Use the same initialization as index.html - sdk = await wasm_bindgen.WasmSdkBuilder.new_testnet().build(); - - postMessage({ type: 'progress', percent: 90, text: 'Finalizing...' }); - - isInitialized = true; - - postMessage({ type: 'progress', percent: 100, text: 'Ready!' }); - postMessage({ type: 'initialized', success: true }); - - return sdk; - } catch (error) { - postMessage({ type: 'initialized', success: false, error: error.message }); - throw error; - } - })(); - - return initializationPromise; -} - -// Broadcast message to all connected ports -function postMessage(message) { - connections.forEach(port => { - port.postMessage(message); - }); -} - -// Handle connections -self.onconnect = function(e) { - const port = e.ports[0]; - connections.add(port); - - port.onmessage = async function(e) { - const { type, id, method, args } = e.data; - - try { - switch (type) { - case 'init': - await initializeSdk(); - port.postMessage({ type: 'initComplete', id }); - break; - - case 'checkStatus': - port.postMessage({ - type: 'status', - id, - initialized: isInitialized - }); - break; - - case 'execute': - if (!sdk) { - await initializeSdk(); - } - - // Execute the SDK method - const result = await sdk[method](...args); - port.postMessage({ - type: 'result', - id, - result - }); - break; - - default: - port.postMessage({ - type: 'error', - id, - error: 'Unknown message type' - }); - } - } catch (error) { - port.postMessage({ - type: 'error', - id, - error: error.message - }); - } - }; - - // Remove port when disconnected - port.onmessageerror = () => { - connections.delete(port); - }; - - // Send initial status - port.postMessage({ - type: 'connected', - initialized: isInitialized - }); -}; \ No newline at end of file diff --git a/packages/wasm-sdk/src/dpns.rs b/packages/wasm-sdk/src/dpns.rs index d38d9d6eda2..89945e17695 100644 --- a/packages/wasm-sdk/src/dpns.rs +++ b/packages/wasm-sdk/src/dpns.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use dash_sdk::dpp::document::{Document, DocumentV0Getters}; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; @@ -10,7 +11,7 @@ use dash_sdk::platform::{Fetch, Identity}; use serde::{Deserialize, Serialize}; use simple_signer::SingleKeySigner; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -20,159 +21,156 @@ pub struct RegisterDpnsNameResult { pub full_domain_name: String, } -/// Convert a string to homograph-safe characters #[wasm_bindgen] -pub fn dpns_convert_to_homograph_safe(input: &str) -> String { - convert_to_homograph_safe_chars(input) -} - -/// Check if a username is valid according to DPNS rules -#[wasm_bindgen] -pub fn dpns_is_valid_username(label: &str) -> bool { - is_valid_username(label) -} - -/// Check if a username is contested (requires masternode voting) -#[wasm_bindgen] -pub fn dpns_is_contested_username(label: &str) -> bool { - is_contested_username(label) -} +impl WasmSdk { + /// Convert a string to homograph-safe characters + #[wasm_bindgen(js_name = "dpnsConvertToHomographSafe")] + pub fn dpns_convert_to_homograph_safe(input: &str) -> String { + convert_to_homograph_safe_chars(input) + } -/// Register a DPNS username -#[wasm_bindgen] -pub async fn dpns_register_name( - sdk: &WasmSdk, - label: &str, - identity_id: &str, - public_key_id: u32, - private_key_wif: &str, - preorder_callback: Option, -) -> Result { - // Parse identity ID - let identity_id_parsed = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .map_err(|e| JsError::new(&format!("Invalid identity ID: {}", e)))?; - - // Fetch the identity - let identity = Identity::fetch(sdk.as_ref(), identity_id_parsed) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsError::new("Identity not found"))?; - - // Create signer - let signer = SingleKeySigner::new(private_key_wif) - .map_err(|e| JsError::new(&format!("Invalid private key WIF: {}", e)))?; - - // Get the specific identity public key - let identity_public_key = identity - .get_public_key_by_id(public_key_id.into()) - .ok_or_else(|| JsError::new(&format!("Public key with ID {} not found", public_key_id)))? - .clone(); - - // Store the JS callback in a thread-local variable that we can access from the closure - thread_local! { - static PREORDER_CALLBACK: std::cell::RefCell> = std::cell::RefCell::new(None); + /// Check if a username is valid according to DPNS rules + #[wasm_bindgen(js_name = "dpnsIsValidUsername")] + pub fn dpns_is_valid_username(label: &str) -> bool { + is_valid_username(label) } - // Set the callback if provided - if let Some(ref js_callback) = preorder_callback { - PREORDER_CALLBACK.with(|cb| { - *cb.borrow_mut() = Some(js_callback.clone()); - }); + /// Check if a username is contested (requires masternode voting) + #[wasm_bindgen(js_name = "dpnsIsContestedUsername")] + pub fn dpns_is_contested_username(label: &str) -> bool { + is_contested_username(label) } - // Create a Rust callback that will call the JavaScript callback - let callback_box = if preorder_callback.is_some() { - Some(Box::new(move |doc: &Document| { + /// Register a DPNS username + #[wasm_bindgen(js_name = "dpnsRegisterName")] + pub async fn dpns_register_name( + &self, + label: &str, + identity_id: &str, + public_key_id: u32, + private_key_wif: &str, + preorder_callback: Option, + ) -> Result { + // Parse identity ID + let identity_id_parsed = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Fetch the identity + let identity = Identity::fetch(self.as_ref(), identity_id_parsed) + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; + + // Create signer + let signer = SingleKeySigner::new(private_key_wif).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid private key WIF: {}", e)) + })?; + + // Get the specific identity public key + let identity_public_key = identity + .get_public_key_by_id(public_key_id) + .ok_or_else(|| { + WasmSdkError::not_found(format!("Public key with ID {} not found", public_key_id)) + })? + .clone(); + + // Store the JS callback in a thread-local variable that we can access from the closure + thread_local! { + static PREORDER_CALLBACK: std::cell::RefCell> = const { std::cell::RefCell::new(None) }; + } + + // Set the callback if provided + if let Some(ref js_callback) = preorder_callback { PREORDER_CALLBACK.with(|cb| { - if let Some(js_callback) = cb.borrow().as_ref() { - let preorder_info = serde_json::json!({ - "documentId": doc.id().to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - "ownerId": doc.owner_id().to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - "revision": doc.revision().unwrap_or(0), - "createdAt": doc.created_at(), - "createdAtBlockHeight": doc.created_at_block_height(), - "createdAtCoreBlockHeight": doc.created_at_core_block_height(), - "message": "Preorder document submitted successfully", - }); - - if let Ok(js_value) = serde_wasm_bindgen::to_value(&preorder_info) { - let _ = js_callback.call1(&JsValue::NULL, &js_value); - } - } + *cb.borrow_mut() = Some(js_callback.clone()); }); - }) as Box) - } else { - None - }; - - // Create registration input with the callback - let input = RegisterDpnsNameInput { - label: label.to_string(), - identity, - identity_public_key, - signer, - preorder_callback: callback_box, - }; - - // Register the name - let result = sdk - .as_ref() - .register_dpns_name(input) - .await - .map_err(|e| JsError::new(&format!("Failed to register DPNS name: {}", e)))?; - - // Clear the thread-local callback - PREORDER_CALLBACK.with(|cb| { - *cb.borrow_mut() = None; - }); - - // Convert result to JS-friendly format - let js_result = RegisterDpnsNameResult { - preorder_document_id: result - .preorder_document - .id() - .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - domain_document_id: result - .domain_document - .id() - .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - full_domain_name: result.full_domain_name, - }; - - // Serialize to JsValue - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - js_result - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize result: {}", e))) -} + } -/// Check if a DPNS name is available -#[wasm_bindgen] -pub async fn dpns_is_name_available(sdk: &WasmSdk, label: &str) -> Result { - sdk.as_ref() - .is_dpns_name_available(label) - .await - .map_err(|e| JsError::new(&format!("Failed to check name availability: {}", e))) -} + // Create a Rust callback that will call the JavaScript callback + let callback_box = if preorder_callback.is_some() { + Some(Box::new(move |doc: &Document| { + PREORDER_CALLBACK.with(|cb| { + if let Some(js_callback) = cb.borrow().as_ref() { + let preorder_info = serde_json::json!({ + "documentId": doc.id().to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + "ownerId": doc.owner_id().to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + "revision": doc.revision().unwrap_or(0), + "createdAt": doc.created_at(), + "createdAtBlockHeight": doc.created_at_block_height(), + "createdAtCoreBlockHeight": doc.created_at_core_block_height(), + "message": "Preorder document submitted successfully", + }); + + if let Ok(js_value) = serde_wasm_bindgen::to_value(&preorder_info) { + let _ = js_callback.call1(&wasm_bindgen::JsValue::NULL, &js_value); + } + } + }); + }) as Box) + } else { + None + }; + + // Create registration input with the callback + let input = RegisterDpnsNameInput { + label: label.to_string(), + identity, + identity_public_key, + signer, + preorder_callback: callback_box, + }; + + // Register the name + let result = self.as_ref().register_dpns_name(input).await?; + + // Clear the thread-local callback + PREORDER_CALLBACK.with(|cb| { + *cb.borrow_mut() = None; + }); -/// Resolve a DPNS name to an identity ID -#[wasm_bindgen] -pub async fn dpns_resolve_name(sdk: &WasmSdk, name: &str) -> Result { - let result = sdk - .as_ref() - .resolve_dpns_name(name) - .await - .map_err(|e| JsError::new(&format!("Failed to resolve DPNS name: {}", e)))?; - - match result { - Some(identity_id) => { - let id_string = identity_id - .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58); - Ok(JsValue::from_str(&id_string)) + // Convert result to JS-friendly format + let js_result = RegisterDpnsNameResult { + preorder_document_id: result + .preorder_document + .id() + .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + domain_document_id: result + .domain_document + .id() + .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + full_domain_name: result.full_domain_name, + }; + + // Serialize to JsValue + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + js_result + .serialize(&serializer) + .map_err(|e| WasmSdkError::serialization(format!("Failed to serialize result: {}", e))) + } + + /// Check if a DPNS name is available + #[wasm_bindgen(js_name = "dpnsIsNameAvailable")] + pub async fn dpns_is_name_available(&self, label: &str) -> Result { + self.as_ref() + .is_dpns_name_available(label) + .await + .map_err(WasmSdkError::from) + } + + /// Resolve a DPNS name to an identity ID + #[wasm_bindgen(js_name = "dpnsResolveName")] + pub async fn dpns_resolve_name(&self, name: &str) -> Result { + let result = self.as_ref().resolve_dpns_name(name).await?; + + match result { + Some(identity_id) => { + let id_string = identity_id + .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58); + Ok(wasm_bindgen::JsValue::from_str(&id_string)) + } + None => Ok(wasm_bindgen::JsValue::NULL), } - None => Ok(JsValue::NULL), } } diff --git a/packages/wasm-sdk/src/dpp.rs b/packages/wasm-sdk/src/dpp.rs index 376a5a9a89d..4dc42d8a994 100644 --- a/packages/wasm-sdk/src/dpp.rs +++ b/packages/wasm-sdk/src/dpp.rs @@ -3,7 +3,7 @@ use dash_sdk::dpp::platform_value::ReplacementType; use dash_sdk::dpp::serialization::PlatformDeserializable; use dash_sdk::dpp::serialization::ValueConvertible; -use crate::error::to_js_error; +use crate::WasmSdkError; use dash_sdk::dpp::dashcore::hashes::serde::Serialize; use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; use dash_sdk::dpp::data_contract::conversion::json::DataContractJsonConversionMethodsV0; @@ -11,7 +11,6 @@ use dash_sdk::dpp::version::PlatformVersion; use dash_sdk::platform::{DataContract, Identity}; use platform_value::string_encoding::Encoding; use wasm_bindgen::prelude::*; -use wasm_bindgen::JsValue; use web_sys::js_sys; #[wasm_bindgen] @@ -38,12 +37,16 @@ impl From for IdentityWasm { #[wasm_bindgen] impl IdentityWasm { #[wasm_bindgen(constructor)] - pub fn new(platform_version: u32) -> Result { - let platform_version = &PlatformVersion::get(platform_version).map_err(to_js_error)?; + pub fn new(platform_version: u32) -> Result { + let platform_version = &PlatformVersion::get(platform_version).map_err(|e| { + WasmSdkError::invalid_argument(format!( + "unknown platform version {platform_version}: {e}" + )) + })?; - Identity::default_versioned(platform_version) - .map(Into::into) - .map_err(to_js_error) + let identity = Identity::default_versioned(platform_version)?; + + Ok(identity.into()) } // // #[wasm_bindgen(js_name=getId)] @@ -57,9 +60,9 @@ impl IdentityWasm { // } #[wasm_bindgen(js_name=setPublicKeys)] - pub fn set_public_keys(&mut self, public_keys: js_sys::Array) -> Result { + pub fn set_public_keys(&mut self, public_keys: js_sys::Array) -> Result { if public_keys.length() == 0 { - return Err(format!("Setting public keys failed. The input ('{}') is invalid. You must use array of PublicKeys", public_keys.to_string()).into()); + return Err(WasmSdkError::invalid_argument(format!("Setting public keys failed. The input ('{}') is invalid. You must use array of PublicKeys", public_keys.to_string()))); } // let public_keys = public_keys @@ -158,35 +161,38 @@ impl IdentityWasm { // } #[wasm_bindgen(js_name=toJSON)] - pub fn to_json(&self) -> Result { - let mut value = self.inner.to_object().map_err(to_js_error)?; + pub fn to_json(&self) -> Result { + let mut value = self.inner.to_object().map_err(|e| { + WasmSdkError::serialization(format!("failed to convert identity to Object: {e}")) + })?; value .replace_at_paths( dash_sdk::dpp::identity::IDENTIFIER_FIELDS_RAW_OBJECT, ReplacementType::TextBase58, ) - .map_err(|e| e.to_string())?; + .map_err(|e| WasmSdkError::serialization(e.to_string()))?; // Monkey patch public keys data to be deserializable let public_keys = value .get_array_mut_ref(dash_sdk::dpp::identity::property_names::PUBLIC_KEYS) - .map_err(|e| e.to_string())?; + .map_err(|e| WasmSdkError::serialization(e.to_string()))?; for key in public_keys.iter_mut() { key.replace_at_paths( dash_sdk::dpp::identity::identity_public_key::BINARY_DATA_FIELDS, ReplacementType::TextBase64, ) - .map_err(|e| e.to_string())?; + .map_err(|e| WasmSdkError::serialization(e.to_string()))?; } let json = value .try_into_validating_json() - .map_err(|e| e.to_string())? + .map_err(|e| WasmSdkError::serialization(e.to_string()))? .to_string(); js_sys::JSON::parse(&json) + .map_err(|e| WasmSdkError::serialization(format!("failed to parse JSON: {:?}", e))) } // // #[wasm_bindgen(js_name=toObject)] @@ -222,9 +228,11 @@ impl IdentityWasm { // Ok(Buffer::from_bytes(&bytes)) // } - #[wasm_bindgen] - pub fn hash(&self) -> Result, JsError> { - self.inner.hash().map_err(to_js_error) + #[wasm_bindgen(js_name = "hash")] + pub fn hash(&self) -> Result, WasmSdkError> { + let hash_bytes = self.inner.hash()?; + + Ok(hash_bytes) } // #[wasm_bindgen(js_name=addPublicKey)] @@ -259,9 +267,13 @@ impl IdentityWasm { } #[wasm_bindgen(js_name=fromBuffer)] - pub fn from_buffer(buffer: Vec) -> Result { + pub fn from_buffer(buffer: Vec) -> Result { let identity: Identity = PlatformDeserializable::deserialize_from_bytes(buffer.as_slice()) - .map_err(to_js_error)?; + .map_err(|e| { + WasmSdkError::serialization(format!( + "failed to deserialize identity from bytes: {e}" + )) + })?; Ok(identity.into()) } } @@ -298,11 +310,17 @@ impl DataContractWasm { } #[wasm_bindgen(js_name=toJSON)] - pub fn to_json(&self) -> Result { + pub fn to_json(&self) -> Result { let platform_version = PlatformVersion::first(); - let json = self.0.to_json(platform_version)?; + let json = self.0.to_json(platform_version).map_err(|e| { + WasmSdkError::serialization(format!( + "failed to convert data contract convert to json: {}", + e + )) + })?; let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - json.serialize(&serializer).map_err(to_js_error) + json.serialize(&serializer) + .map_err(|e| WasmSdkError::serialization(format!("can't serialize to json: {}", e))) } } diff --git a/packages/wasm-sdk/src/error.rs b/packages/wasm-sdk/src/error.rs index 0e3742b368f..a2b2e264462 100644 --- a/packages/wasm-sdk/src/error.rs +++ b/packages/wasm-sdk/src/error.rs @@ -1,13 +1,250 @@ -use dash_sdk::Error; -use std::fmt::Display; +use dash_sdk::dpp::ProtocolError; +use dash_sdk::{error::StateTransitionBroadcastError, Error as SdkError}; +use rs_dapi_client::CanRetry; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsError; +/// Structured error surfaced to JS consumers #[wasm_bindgen] -#[derive(thiserror::Error, Debug)] -#[error("Dash SDK error: {0:?}")] -pub struct WasmError(#[from] Error); +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum WasmSdkErrorKind { + // SDK error kinds + Config, + Drive, + DriveProofError, + Protocol, + Proof, + InvalidProvedResponse, + DapiClientError, + DapiMocksError, + CoreError, + MerkleBlockError, + CoreClientError, + MissingDependency, + TotalCreditsNotFound, + EpochNotFound, + TimeoutReached, + AlreadyExists, + Generic, + ContextProviderError, + Cancelled, + StaleNode, + StateTransitionBroadcastError, -pub(crate) fn to_js_error(e: impl Display) -> JsError { - JsError::new(&format!("{}", e)) + // Local helper kinds + InvalidArgument, + SerializationError, + NotFound, +} + +/// Structured error surfaced to JS consumers +#[wasm_bindgen] +#[derive(thiserror::Error, Debug, Clone)] +#[error("{message}")] +pub struct WasmSdkError { + kind: WasmSdkErrorKind, + message: String, + /// Optional numeric code for some errors (e.g., broadcast error code). + code: i32, + /// Indicates if the operation can be retried safely. + retriable: bool, +} + +// wasm-bindgen getters defined below in the second impl block + +impl WasmSdkError { + fn new>( + kind: WasmSdkErrorKind, + message: M, + code: Option, + retriable: bool, + ) -> Self { + Self { + kind, + message: message.into(), + code: code.unwrap_or(-1), + retriable, + } + } + + pub(crate) fn generic(message: impl Into) -> Self { + Self::new(WasmSdkErrorKind::Generic, message, None, false) + } + + pub(crate) fn invalid_argument(message: impl Into) -> Self { + Self::new(WasmSdkErrorKind::InvalidArgument, message, None, false) + } + + pub(crate) fn serialization(message: impl Into) -> Self { + Self::new(WasmSdkErrorKind::SerializationError, message, None, false) + } + + pub(crate) fn not_found(message: impl Into) -> Self { + Self::new(WasmSdkErrorKind::NotFound, message, None, false) + } +} + +impl From for WasmSdkError { + fn from(err: SdkError) -> Self { + use SdkError::*; + let retriable = err.can_retry(); + match err { + AlreadyExists(msg) => Self::new(WasmSdkErrorKind::AlreadyExists, msg, None, retriable), + Config(msg) => Self::new(WasmSdkErrorKind::Config, msg, None, retriable), + Drive(e) => Self::new(WasmSdkErrorKind::Drive, e.to_string(), None, retriable), + DriveProofError(e, _proof, _block_info) => Self::new( + WasmSdkErrorKind::DriveProofError, + e.to_string(), + None, + retriable, + ), + Protocol(e) => Self::new(WasmSdkErrorKind::Protocol, e.to_string(), None, retriable), + Proof(e) => Self::new(WasmSdkErrorKind::Proof, e.to_string(), None, retriable), + InvalidProvedResponse(msg) => Self::new( + WasmSdkErrorKind::InvalidProvedResponse, + msg, + None, + retriable, + ), + DapiClientError(e) => Self::new( + WasmSdkErrorKind::DapiClientError, + e.to_string(), + None, + retriable, + ), + #[cfg(feature = "mocks")] + DapiMocksError(e) => Self::new( + WasmSdkErrorKind::DapiMocksError, + e.to_string(), + None, + retriable, + ), + CoreError(e) => Self::new(WasmSdkErrorKind::CoreError, e.to_string(), None, retriable), + MerkleBlockError(e) => Self::new( + WasmSdkErrorKind::MerkleBlockError, + e.to_string(), + None, + retriable, + ), + CoreClientError(e) => Self::new( + WasmSdkErrorKind::CoreClientError, + e.to_string(), + None, + retriable, + ), + MissingDependency(kind, id) => Self::new( + WasmSdkErrorKind::MissingDependency, + format!("Required {} not found: {}", kind, id), + None, + retriable, + ), + TotalCreditsNotFound => Self::new( + WasmSdkErrorKind::TotalCreditsNotFound, + "Total credits in Platform are not found; it should never happen".to_string(), + None, + retriable, + ), + EpochNotFound => Self::new( + WasmSdkErrorKind::EpochNotFound, + "No epoch found on Platform; it should never happen".to_string(), + None, + retriable, + ), + TimeoutReached(duration, msg) => Self::new( + WasmSdkErrorKind::TimeoutReached, + format!( + "SDK operation timeout {} secs reached: {}", + duration.as_secs(), + msg + ), + None, + retriable, + ), + Generic(msg) => Self::new(WasmSdkErrorKind::Generic, msg, None, retriable), + ContextProviderError(e) => Self::new( + WasmSdkErrorKind::ContextProviderError, + e.to_string(), + None, + retriable, + ), + Cancelled(msg) => Self::new(WasmSdkErrorKind::Cancelled, msg, None, retriable), + StaleNode(e) => Self::new(WasmSdkErrorKind::StaleNode, e.to_string(), None, retriable), + StateTransitionBroadcastError(e) => WasmSdkError::from(e), + } + } +} +impl From for WasmSdkError { + fn from(err: ProtocolError) -> Self { + Self::new(WasmSdkErrorKind::Protocol, err.to_string(), None, false) + } +} + +impl From for WasmSdkError { + fn from(err: StateTransitionBroadcastError) -> Self { + Self::new( + WasmSdkErrorKind::StateTransitionBroadcastError, + err.to_string(), + Some(err.code as i32), + false, + ) + } +} + +#[wasm_bindgen] +impl WasmSdkError { + /// Error kind (enum) + #[wasm_bindgen(getter)] + pub fn kind(&self) -> WasmSdkErrorKind { + self.kind + } + + /// Backwards-compatible name string for the kind + #[wasm_bindgen(getter)] + pub fn name(&self) -> String { + use WasmSdkErrorKind as K; + match self.kind { + K::Config => "Config", + K::Drive => "Drive", + K::DriveProofError => "DriveProofError", + K::Protocol => "Protocol", + K::Proof => "Proof", + K::InvalidProvedResponse => "InvalidProvedResponse", + K::DapiClientError => "DapiClientError", + K::DapiMocksError => "DapiMocksError", + K::CoreError => "CoreError", + K::MerkleBlockError => "MerkleBlockError", + K::CoreClientError => "CoreClientError", + K::MissingDependency => "MissingDependency", + K::TotalCreditsNotFound => "TotalCreditsNotFound", + K::EpochNotFound => "EpochNotFound", + K::TimeoutReached => "TimeoutReached", + K::AlreadyExists => "AlreadyExists", + K::Generic => "Generic", + K::ContextProviderError => "ContextProviderError", + K::Cancelled => "Cancelled", + K::StaleNode => "StaleNode", + K::StateTransitionBroadcastError => "StateTransitionBroadcastError", + K::InvalidArgument => "InvalidArgument", + K::SerializationError => "SerializationError", + K::NotFound => "NotFound", + } + .to_string() + } + + /// Human-readable message + #[wasm_bindgen(getter)] + pub fn message(&self) -> String { + self.message.clone() + } + + /// Optional numeric code. -1 means absent/not applicable + #[wasm_bindgen(getter)] + pub fn code(&self) -> i32 { + self.code + } + + /// Whether the error is retryable + #[wasm_bindgen(getter)] + pub fn retriable(&self) -> bool { + self.retriable + } } diff --git a/packages/wasm-sdk/src/lib.rs b/packages/wasm-sdk/src/lib.rs index 6cd53dde7c3..a8df479f868 100644 --- a/packages/wasm-sdk/src/lib.rs +++ b/packages/wasm-sdk/src/lib.rs @@ -1,9 +1,10 @@ -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen::prelude::wasm_bindgen; pub mod context_provider; pub mod dpns; pub mod dpp; pub mod error; +pub mod logging; pub mod queries; pub mod sdk; pub mod state_transitions; @@ -12,10 +13,8 @@ pub mod wallet; // Re-export commonly used items pub use dpns::*; -pub use queries::{ - data_contract::*, document::*, dpns::*, epoch::*, group::*, identity as query_identity, - protocol::*, system::*, token::*, voting::*, -}; +pub use error::{WasmSdkError, WasmSdkErrorKind}; +pub use queries::{group::*, identity as query_identity}; pub use sdk::{WasmSdk, WasmSdkBuilder}; pub use state_transitions::identity as state_transition_identity; pub use wallet::*; @@ -24,14 +23,8 @@ pub use wallet::*; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[wasm_bindgen(start)] -pub async fn start() -> Result<(), JsValue> { - // We use tracing-wasm together with console_error_panic_hook to get logs from the wasm module. - // Other alternatives are: - // * https://github.com/jquesada2016/tracing_subscriber_wasm - // * https://crates.io/crates/tracing-web +pub async fn start() -> Result<(), WasmSdkError> { console_error_panic_hook::set_once(); - tracing_wasm::set_as_global_default(); - Ok(()) } diff --git a/packages/wasm-sdk/src/logging.rs b/packages/wasm-sdk/src/logging.rs new file mode 100644 index 00000000000..74bcb0cd58b --- /dev/null +++ b/packages/wasm-sdk/src/logging.rs @@ -0,0 +1,52 @@ +use crate::error::WasmSdkError; +use once_cell::sync::OnceCell; +use tracing_subscriber::filter::EnvFilter; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::reload; +use tracing_subscriber::Registry; + +static RELOAD_HANDLE: OnceCell> = OnceCell::new(); + +fn normalize_level_or_filter(input: &str) -> Result { + let s = input.trim(); + if s.is_empty() { + return Err(WasmSdkError::invalid_argument("Empty log level/filter")); + } + + // Accept simple levels (case-insensitive) + match s.to_ascii_lowercase().as_str() { + "off" | "error" | "warn" | "info" | "debug" | "trace" => Ok(s.to_ascii_lowercase()), + _ => { + // Otherwise treat as EnvFilter string + // Try constructing an EnvFilter to validate + EnvFilter::try_new(s) + .map(|_| s.to_string()) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid log filter: {}", e))) + } + } +} + +pub fn set_log_level(level_or_filter: &str) -> Result<(), WasmSdkError> { + let filter_str = normalize_level_or_filter(level_or_filter)?; + + if let Some(handle) = RELOAD_HANDLE.get() { + // Update existing filter + handle + .modify(|f| *f = EnvFilter::new(filter_str.clone())) + .map_err(|e| WasmSdkError::generic(format!("Failed to update log filter: {}", e)))?; + return Ok(()); + } + + // Initialize subscriber for the first time + let env_filter = EnvFilter::new(filter_str); + let (layer, handle) = reload::Layer::new(env_filter); + let wasm_layer = tracing_wasm::WASMLayer::new(tracing_wasm::WASMLayerConfig::default()); + + let subscriber = Registry::default().with(layer).with(wasm_layer); + + tracing::subscriber::set_global_default(subscriber) + .map_err(|e| WasmSdkError::generic(format!("Failed to set global logger: {}", e)))?; + + let _ = RELOAD_HANDLE.set(handle); + Ok(()) +} diff --git a/packages/wasm-sdk/src/queries/data_contract.rs b/packages/wasm-sdk/src/queries/data_contract.rs index 547499f8166..67bcbe5d0eb 100644 --- a/packages/wasm-sdk/src/queries/data_contract.rs +++ b/packages/wasm-sdk/src/queries/data_contract.rs @@ -1,6 +1,7 @@ use crate::dpp::DataContractWasm; use crate::queries::ProofMetadataResponse; use crate::sdk::WasmSdk; +use crate::WasmSdkError; use dash_sdk::dpp::data_contract::conversion::json::DataContractJsonConversionMethodsV0; use dash_sdk::platform::query::LimitQuery; use dash_sdk::platform::{DataContract, Fetch, FetchMany, Identifier}; @@ -8,56 +9,7 @@ use drive_proof_verifier::types::{DataContractHistory, DataContracts}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; - -#[wasm_bindgen] -pub async fn data_contract_fetch( - sdk: &WasmSdk, - base58_id: &str, -) -> Result { - let id = Identifier::from_string( - base58_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - DataContract::fetch_by_identifier(sdk, id) - .await? - .ok_or_else(|| JsError::new("Data contract not found")) - .map(Into::into) -} - -#[wasm_bindgen] -pub async fn data_contract_fetch_with_proof_info( - sdk: &WasmSdk, - base58_id: &str, -) -> Result { - let id = Identifier::from_string( - base58_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let (contract, metadata, proof) = - DataContract::fetch_with_metadata_and_proof(sdk, id, None).await?; - - match contract { - Some(contract) => { - let response = ProofMetadataResponse { - data: contract.to_json( - &dash_sdk::dpp::version::PlatformVersion::get(sdk.version()).unwrap(), - )?, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } - None => Err(JsError::new("Data contract not found")), - } -} +use wasm_bindgen::JsValue; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -65,51 +17,6 @@ struct DataContractHistoryResponse { versions: BTreeMap, } -#[wasm_bindgen] -pub async fn get_data_contract_history( - sdk: &WasmSdk, - id: &str, - limit: Option, - _offset: Option, - start_at_ms: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create query with start timestamp - let query = LimitQuery { - query: (contract_id, start_at_ms.unwrap_or(0)), - start_info: None, - limit, - }; - - // Fetch contract history - let history_result = DataContractHistory::fetch(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch data contract history: {}", e)))?; - - // Convert to response format - let mut versions = BTreeMap::new(); - let platform_version = sdk.as_ref().version(); - - if let Some(history) = history_result { - for (revision, contract) in history { - versions.insert(revision.to_string(), contract.to_json(platform_version)?); - } - } - - let response = DataContractHistoryResponse { versions }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct DataContractsResponse { @@ -117,152 +24,281 @@ struct DataContractsResponse { } #[wasm_bindgen] -pub async fn get_data_contracts(sdk: &WasmSdk, ids: Vec) -> Result { - // Parse all contract IDs - let identifiers: Result, _> = ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let identifiers = identifiers?; - - // Fetch all contracts - let contracts_result: DataContracts = DataContract::fetch_many(sdk.as_ref(), identifiers) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch data contracts: {}", e)))?; - - // Convert to response format - let mut data_contracts = BTreeMap::new(); - let platform_version = sdk.as_ref().version(); - for (id, contract_opt) in contracts_result { - let id_str = id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58); - let contract_json = match contract_opt { - Some(contract) => Some(contract.to_json(platform_version)?), - None => None, - }; - data_contracts.insert(id_str, contract_json); +impl WasmSdk { + #[wasm_bindgen(js_name = "getDataContract")] + pub async fn get_data_contract( + &self, + base58_id: &str, + ) -> Result { + let id = Identifier::from_string( + base58_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + DataContract::fetch_by_identifier(self.as_ref(), id) + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found")) + .map(Into::into) } - let response = DataContractsResponse { data_contracts }; + #[wasm_bindgen(js_name = "getDataContractWithProofInfo")] + pub async fn get_data_contract_with_proof_info( + &self, + base58_id: &str, + ) -> Result { + let id = Identifier::from_string( + base58_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + let (contract, metadata, proof) = + DataContract::fetch_with_metadata_and_proof(self.as_ref(), id, None).await?; + + match contract { + Some(contract) => { + let response = ProofMetadataResponse { + data: contract + .to_json( + dash_sdk::dpp::version::PlatformVersion::get(self.version()).unwrap(), + ) + .map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert contract to JSON: {}", + e + )) + })?, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => Err(WasmSdkError::not_found("Data contract not found")), + } + } - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getDataContractHistory")] + pub async fn get_data_contract_history( + &self, + id: &str, + limit: Option, + _offset: Option, + start_at_ms: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + // Create query with start timestamp + let query = LimitQuery { + query: (contract_id, start_at_ms.unwrap_or(0)), + start_info: None, + limit, + }; -// Proof info versions for data contract queries + // Fetch contract history + let history_result = DataContractHistory::fetch(self.as_ref(), query).await?; + + // Convert to response format + let mut versions = BTreeMap::new(); + let platform_version = self.as_ref().version(); + + if let Some(history) = history_result { + for (revision, contract) in history { + versions.insert( + revision.to_string(), + contract.to_json(platform_version).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert contract to JSON: {}", + e + )) + })?, + ); + } + } -#[wasm_bindgen] -pub async fn get_data_contract_history_with_proof_info( - sdk: &WasmSdk, - id: &str, - limit: Option, - _offset: Option, - start_at_ms: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create query with start timestamp - let query = LimitQuery { - query: (contract_id, start_at_ms.unwrap_or(0)), - start_info: None, - limit, - }; - - // Fetch contract history with proof - let (history_result, metadata, proof) = - DataContractHistory::fetch_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch data contract history with proof: {}", - e - )) - })?; - - // Convert to response format - let mut versions = BTreeMap::new(); - let platform_version = sdk.as_ref().version(); - - if let Some(history) = history_result { - for (revision, contract) in history { - versions.insert(revision.to_string(), contract.to_json(platform_version)?); + let response = DataContractHistoryResponse { versions }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getDataContracts")] + pub async fn get_data_contracts(&self, ids: Vec) -> Result { + // Parse all contract IDs + let identifiers: Result, WasmSdkError> = ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)) + }) + }) + .collect(); + let identifiers = identifiers?; + + // Fetch all contracts + let contracts_result: DataContracts = + DataContract::fetch_many(self.as_ref(), identifiers).await?; + + // Convert to response format + let mut data_contracts = BTreeMap::new(); + let platform_version = self.as_ref().version(); + for (id, contract_opt) in contracts_result { + let id_str = + id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58); + let contract_json = match contract_opt { + Some(contract) => Some(contract.to_json(platform_version).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert contract to JSON: {}", + e + )) + })?), + None => None, + }; + data_contracts.insert(id_str, contract_json); } + + let response = DataContractsResponse { data_contracts }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let data = DataContractHistoryResponse { versions }; + // Proof info versions for data contract queries + + #[wasm_bindgen(js_name = "getDataContractHistoryWithProofInfo")] + pub async fn get_data_contract_history_with_proof_info( + &self, + id: &str, + limit: Option, + _offset: Option, + start_at_ms: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + // Create query with start timestamp + let query = LimitQuery { + query: (contract_id, start_at_ms.unwrap_or(0)), + start_info: None, + limit, + }; - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; + // Fetch contract history with proof + let (history_result, metadata, proof) = + DataContractHistory::fetch_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Convert to response format + let mut versions = BTreeMap::new(); + let platform_version = self.as_ref().version(); + + if let Some(history) = history_result { + for (revision, contract) in history { + versions.insert( + revision.to_string(), + contract.to_json(platform_version).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert contract to JSON: {}", + e + )) + })?, + ); + } + } - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let data = DataContractHistoryResponse { versions }; -#[wasm_bindgen] -pub async fn get_data_contracts_with_proof_info( - sdk: &WasmSdk, - ids: Vec, -) -> Result { - // Parse all contract IDs - let identifiers: Result, _> = ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let identifiers = identifiers?; - - // Fetch all contracts with proof - let (contracts_result, metadata, proof) = - DataContract::fetch_many_with_metadata_and_proof(sdk.as_ref(), identifiers, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch data contracts with proof: {}", e)) - })?; - - // Convert to response format - let mut data_contracts = BTreeMap::new(); - let platform_version = sdk.as_ref().version(); - for (id, contract_opt) in contracts_result { - let id_str = id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58); - let contract_json = match contract_opt { - Some(contract) => Some(contract.to_json(platform_version)?), - None => None, + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), }; - data_contracts.insert(id_str, contract_json); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let data = DataContractsResponse { data_contracts }; + #[wasm_bindgen(js_name = "getDataContractsWithProofInfo")] + pub async fn get_data_contracts_with_proof_info( + &self, + ids: Vec, + ) -> Result { + // Parse all contract IDs + let identifiers: Result, WasmSdkError> = ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)) + }) + }) + .collect(); + let identifiers = identifiers?; + + // Fetch all contracts with proof + let (contracts_result, metadata, proof) = + DataContract::fetch_many_with_metadata_and_proof(self.as_ref(), identifiers, None) + .await?; + + // Convert to response format + let mut data_contracts = BTreeMap::new(); + let platform_version = self.as_ref().version(); + for (id, contract_opt) in contracts_result { + let id_str = + id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58); + let contract_json = match contract_opt { + Some(contract) => Some(contract.to_json(platform_version).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert contract to JSON: {}", + e + )) + })?), + None => None, + }; + data_contracts.insert(id_str, contract_json); + } + + let data = DataContractsResponse { data_contracts }; - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } } diff --git a/packages/wasm-sdk/src/queries/document.rs b/packages/wasm-sdk/src/queries/document.rs index a666176bb2f..9c453209d8d 100644 --- a/packages/wasm-sdk/src/queries/document.rs +++ b/packages/wasm-sdk/src/queries/document.rs @@ -1,5 +1,6 @@ use crate::queries::ProofMetadataResponse; use crate::sdk::WasmSdk; +use crate::WasmSdkError; use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; use dash_sdk::dpp::document::Document; use dash_sdk::dpp::document::DocumentV0Getters; @@ -10,7 +11,7 @@ use drive::query::{OrderClause, WhereClause, WhereOperator}; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsCast, JsError, JsValue}; +use wasm_bindgen::{JsCast, JsValue}; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -44,7 +45,7 @@ impl DocumentResponse { doc: &Document, _data_contract: &dash_sdk::platform::DataContract, _document_type: dash_sdk::dpp::data_contract::document_type::DocumentTypeRef, - ) -> Result { + ) -> Result { use dash_sdk::dpp::document::DocumentV0Getters; // For now, we'll continue with the existing approach @@ -56,10 +57,9 @@ impl DocumentResponse { for (key, value) in properties { // Convert platform Value to JSON - let json_value: JsonValue = value - .clone() - .try_into() - .map_err(|e| JsError::new(&format!("Failed to convert value to JSON: {:?}", e)))?; + let json_value: JsonValue = value.clone().try_into().map_err(|e| { + WasmSdkError::generic(format!("Failed to convert value to JSON: {:?}", e)) + })?; data.insert(key.clone(), json_value); } @@ -89,25 +89,25 @@ impl DocumentResponse { } /// Parse JSON where clause into WhereClause -fn parse_where_clause(json_clause: &JsonValue) -> Result { +fn parse_where_clause(json_clause: &JsonValue) -> Result { let clause_array = json_clause .as_array() - .ok_or_else(|| JsError::new("where clause must be an array"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("where clause must be an array"))?; if clause_array.len() != 3 { - return Err(JsError::new( + return Err(WasmSdkError::invalid_argument( "where clause must have exactly 3 elements: [field, operator, value]", )); } let field = clause_array[0] .as_str() - .ok_or_else(|| JsError::new("where clause field must be a string"))? + .ok_or_else(|| WasmSdkError::invalid_argument("where clause field must be a string"))? .to_string(); let operator_str = clause_array[1] .as_str() - .ok_or_else(|| JsError::new("where clause operator must be a string"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("where clause operator must be a string"))?; let operator = match operator_str { "==" | "=" => WhereOperator::Equal, @@ -121,7 +121,12 @@ fn parse_where_clause(json_clause: &JsonValue) -> Result { "BetweenExcludeRight" => WhereOperator::BetweenExcludeRight, "in" | "In" => WhereOperator::In, "startsWith" | "StartsWith" => WhereOperator::StartsWith, - _ => return Err(JsError::new(&format!("Unknown operator: {}", operator_str))), + _ => { + return Err(WasmSdkError::invalid_argument(format!( + "Unknown operator: {}", + operator_str + ))) + } }; // Convert JSON value to platform Value @@ -135,37 +140,41 @@ fn parse_where_clause(json_clause: &JsonValue) -> Result { } /// Parse JSON order by clause into OrderClause -fn parse_order_clause(json_clause: &JsonValue) -> Result { +fn parse_order_clause(json_clause: &JsonValue) -> Result { let clause_array = json_clause .as_array() - .ok_or_else(|| JsError::new("order by clause must be an array"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("order by clause must be an array"))?; if clause_array.len() != 2 { - return Err(JsError::new( + return Err(WasmSdkError::invalid_argument( "order by clause must have exactly 2 elements: [field, direction]", )); } let field = clause_array[0] .as_str() - .ok_or_else(|| JsError::new("order by field must be a string"))? + .ok_or_else(|| WasmSdkError::invalid_argument("order by field must be a string"))? .to_string(); let direction = clause_array[1] .as_str() - .ok_or_else(|| JsError::new("order by direction must be a string"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("order by direction must be a string"))?; let ascending = match direction { "asc" => true, "desc" => false, - _ => return Err(JsError::new("order by direction must be 'asc' or 'desc'")), + _ => { + return Err(WasmSdkError::invalid_argument( + "order by direction must be 'asc' or 'desc'", + )) + } }; Ok(OrderClause { field, ascending }) } /// Convert JSON value to platform Value -fn json_to_platform_value(json_val: &JsonValue) -> Result { +fn json_to_platform_value(json_val: &JsonValue) -> Result { match json_val { JsonValue::Null => Ok(Value::Null), JsonValue::Bool(b) => Ok(Value::Bool(*b)), @@ -177,7 +186,7 @@ fn json_to_platform_value(json_val: &JsonValue) -> Result { } else if let Some(f) = n.as_f64() { Ok(Value::Float(f)) } else { - Err(JsError::new("Unsupported number type")) + Err(WasmSdkError::invalid_argument("Unsupported number type")) } } JsonValue::String(s) => { @@ -196,7 +205,7 @@ fn json_to_platform_value(json_val: &JsonValue) -> Result { } } JsonValue::Array(arr) => { - let values: Result, JsError> = + let values: Result, WasmSdkError> = arr.iter().map(json_to_platform_value).collect(); Ok(Value::Array(values?)) } @@ -211,579 +220,600 @@ fn json_to_platform_value(json_val: &JsonValue) -> Result { } #[wasm_bindgen] -pub async fn get_documents( - sdk: &WasmSdk, - data_contract_id: &str, - document_type: &str, - where_clause: Option, - order_by: Option, - limit: Option, - start_after: Option, - start_at: Option, -) -> Result { - use dash_sdk::platform::documents::document_query::DocumentQuery; - use dash_sdk::platform::FetchMany; - use drive_proof_verifier::types::Documents; - - // Parse data contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create base document query - let mut query = - DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, document_type) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))?; - - // Set limit if provided - if let Some(limit_val) = limit { - query.limit = limit_val; - } else { - query.limit = 100; // Default limit - } - - // Handle start parameters - if let Some(start_after_id) = start_after { - let doc_id = Identifier::from_string( - &start_after_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAfter( - doc_id.to_vec() - )); - } else if let Some(start_at_id) = start_at { - let doc_id = Identifier::from_string( - &start_at_id, +impl WasmSdk { + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getDocuments")] + pub async fn get_documents( + &self, + data_contract_id: &str, + document_type: &str, + where_clause: Option, + order_by: Option, + limit: Option, + start_after: Option, + start_at: Option, + ) -> Result { + use dash_sdk::platform::documents::document_query::DocumentQuery; + use dash_sdk::platform::FetchMany; + use drive_proof_verifier::types::Documents; + + // Parse data contract ID + let contract_id = Identifier::from_string( + data_contract_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAt( - doc_id.to_vec() - )); - } + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + // Create base document query + let mut query = + DocumentQuery::new_with_data_contract_id(self.as_ref(), contract_id, document_type) + .await?; + + // Set limit if provided + if let Some(limit_val) = limit { + query.limit = limit_val; + } else { + query.limit = 100; // Default limit + } - // Parse and apply where clauses - if let Some(where_json) = where_clause { - let json_value: JsonValue = serde_json::from_str(&where_json) - .map_err(|e| JsError::new(&format!("Failed to parse where clause JSON: {}", e)))?; + // Handle start parameters + if let Some(start_after_id) = start_after { + let doc_id = Identifier::from_string( + &start_after_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid startAfter document ID: {}", e)) + })?; + query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAfter( + doc_id.to_vec() + )); + } else if let Some(start_at_id) = start_at { + let doc_id = Identifier::from_string( + &start_at_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid startAt document ID: {}", e)) + })?; + query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAt( + doc_id.to_vec() + )); + } - // Expect an array of where clauses - let where_array = json_value - .as_array() - .ok_or_else(|| JsError::new("where clause must be an array of clauses"))?; + // Parse and apply where clauses + if let Some(where_json) = where_clause { + let json_value: JsonValue = serde_json::from_str(&where_json).map_err(|e| { + WasmSdkError::invalid_argument(format!("Failed to parse where clause JSON: {}", e)) + })?; - for clause_json in where_array { - let where_clause = parse_where_clause(clause_json)?; - query = query.with_where(where_clause); + // Expect an array of where clauses + let where_array = json_value.as_array().ok_or_else(|| { + WasmSdkError::invalid_argument("where clause must be an array of clauses") + })?; + + for clause_json in where_array { + let where_clause = parse_where_clause(clause_json)?; + query = query.with_where(where_clause); + } } - } - // Parse and apply order by clauses - if let Some(order_json) = order_by { - let json_value: JsonValue = serde_json::from_str(&order_json) - .map_err(|e| JsError::new(&format!("Failed to parse order by JSON: {}", e)))?; + // Parse and apply order by clauses + if let Some(order_json) = order_by { + let json_value: JsonValue = serde_json::from_str(&order_json).map_err(|e| { + WasmSdkError::invalid_argument(format!("Failed to parse order by JSON: {}", e)) + })?; - // Expect an array of order clauses - let order_array = json_value - .as_array() - .ok_or_else(|| JsError::new("order by must be an array of clauses"))?; + // Expect an array of order clauses + let order_array = json_value.as_array().ok_or_else(|| { + WasmSdkError::invalid_argument("order by must be an array of clauses") + })?; - for clause_json in order_array { - let order_clause = parse_order_clause(clause_json)?; - query = query.with_order_by(order_clause); + for clause_json in order_array { + let order_clause = parse_order_clause(clause_json)?; + query = query.with_order_by(order_clause); + } } - } - // Execute query - let documents_result: Documents = Document::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch documents: {}", e)))?; - - // Fetch the data contract to get the document type - let data_contract = dash_sdk::platform::DataContract::fetch(sdk.as_ref(), contract_id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch data contract: {}", e)))? - .ok_or_else(|| JsError::new("Data contract not found"))?; - - // Get the document type - let document_type_ref = data_contract - .document_type_for_name(document_type) - .map_err(|e| JsError::new(&format!("Document type not found: {}", e)))?; - - // Convert documents to response format - let mut responses: Vec = Vec::new(); - for (_, doc_opt) in documents_result { - if let Some(doc) = doc_opt { - responses.push(DocumentResponse::from_document( - &doc, - &data_contract, - document_type_ref, - )?); + // Execute query + let documents_result: Documents = Document::fetch_many(self.as_ref(), query).await?; + + // Fetch the data contract to get the document type + let data_contract = dash_sdk::platform::DataContract::fetch(self.as_ref(), contract_id) + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found"))?; + + // Get the document type + let document_type_ref = data_contract + .document_type_for_name(document_type) + .map_err(|e| WasmSdkError::not_found(format!("Document type not found: {}", e)))?; + + // Convert documents to response format + let mut responses: Vec = Vec::new(); + for (_, doc_opt) in documents_result { + if let Some(doc) = doc_opt { + responses.push(DocumentResponse::from_document( + &doc, + &data_contract, + document_type_ref, + )?); + } } - } - - // Use json_compatible serializer to convert maps to objects - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - responses - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} -#[wasm_bindgen] -pub async fn get_documents_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, - document_type: &str, - where_clause: Option, - order_by: Option, - limit: Option, - start_after: Option, - start_at: Option, -) -> Result { - use dash_sdk::platform::documents::document_query::DocumentQuery; - use dash_sdk::platform::FetchMany; - - // Parse data contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create base document query - let mut query = - DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, document_type) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))?; - - // Set limit if provided - if let Some(limit_val) = limit { - query.limit = limit_val; - } else { - query.limit = 100; // Default limit + // Use json_compatible serializer to convert maps to objects + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + responses.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - // Handle start parameters - if let Some(start_after_id) = start_after { - let doc_id = Identifier::from_string( - &start_after_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAfter( - doc_id.to_vec() - )); - } else if let Some(start_at_id) = start_at { - let doc_id = Identifier::from_string( - &start_at_id, + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getDocumentsWithProofInfo")] + pub async fn get_documents_with_proof_info( + &self, + data_contract_id: &str, + document_type: &str, + where_clause: Option, + order_by: Option, + limit: Option, + start_after: Option, + start_at: Option, + ) -> Result { + use dash_sdk::platform::documents::document_query::DocumentQuery; + use dash_sdk::platform::FetchMany; + + // Parse data contract ID + let contract_id = Identifier::from_string( + data_contract_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAt( - doc_id.to_vec() - )); - } + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + // Create base document query + let mut query = + DocumentQuery::new_with_data_contract_id(self.as_ref(), contract_id, document_type) + .await?; + + // Set limit if provided + if let Some(limit_val) = limit { + query.limit = limit_val; + } else { + query.limit = 100; // Default limit + } - // Parse and set where clauses if provided - if let Some(where_json) = where_clause { - let clauses: Vec = serde_json::from_str(&where_json) - .map_err(|e| JsError::new(&format!("Invalid where clause JSON: {}", e)))?; + // Handle start parameters + if let Some(start_after_id) = start_after { + let doc_id = Identifier::from_string( + &start_after_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid startAfter document ID: {}", e)) + })?; + query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAfter( + doc_id.to_vec() + )); + } else if let Some(start_at_id) = start_at { + let doc_id = Identifier::from_string( + &start_at_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid startAt document ID: {}", e)) + })?; + query.start = Some(dash_sdk::dapi_grpc::platform::v0::get_documents_request::get_documents_request_v0::Start::StartAt( + doc_id.to_vec() + )); + } + + // Parse and set where clauses if provided + if let Some(where_json) = where_clause { + let clauses: Vec = serde_json::from_str(&where_json).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid where clause JSON: {}", e)) + })?; - for clause_json in clauses { - let where_clause = parse_where_clause(&clause_json)?; - query = query.with_where(where_clause); + for clause_json in clauses { + let where_clause = parse_where_clause(&clause_json)?; + query = query.with_where(where_clause); + } } - } - // Parse and set order by clauses if provided - if let Some(order_json) = order_by { - let clauses: Vec = serde_json::from_str(&order_json) - .map_err(|e| JsError::new(&format!("Invalid order by JSON: {}", e)))?; + // Parse and set order by clauses if provided + if let Some(order_json) = order_by { + let clauses: Vec = serde_json::from_str(&order_json).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid order by JSON: {}", e)) + })?; - for clause_json in clauses { - let order_clause = parse_order_clause(&clause_json)?; - query = query.with_order_by(order_clause); + for clause_json in clauses { + let order_clause = parse_order_clause(&clause_json)?; + query = query.with_order_by(order_clause); + } } - } - // Execute query with proof - let (documents_result, metadata, proof) = - Document::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch documents: {}", e)))?; - - // Fetch the data contract to get the document type - let data_contract = dash_sdk::platform::DataContract::fetch(sdk.as_ref(), contract_id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch data contract: {}", e)))? - .ok_or_else(|| JsError::new("Data contract not found"))?; - - // Get the document type - let document_type_ref = data_contract - .document_type_for_name(document_type) - .map_err(|e| JsError::new(&format!("Document type not found: {}", e)))?; - - // Convert documents to response format - let mut responses: Vec = Vec::new(); - for (_, doc_opt) in documents_result { - if let Some(doc) = doc_opt { - responses.push(DocumentResponse::from_document( - &doc, - &data_contract, - document_type_ref, - )?); + // Execute query with proof + let (documents_result, metadata, proof) = + Document::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Fetch the data contract to get the document type + let data_contract = dash_sdk::platform::DataContract::fetch(self.as_ref(), contract_id) + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found"))?; + + // Get the document type + let document_type_ref = data_contract + .document_type_for_name(document_type) + .map_err(|e| WasmSdkError::not_found(format!("Document type not found: {}", e)))?; + + // Convert documents to response format + let mut responses: Vec = Vec::new(); + for (_, doc_opt) in documents_result { + if let Some(doc) = doc_opt { + responses.push(DocumentResponse::from_document( + &doc, + &data_contract, + document_type_ref, + )?); + } } - } - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_document( - sdk: &WasmSdk, - data_contract_id: &str, - document_type: &str, - document_id: &str, -) -> Result { - use dash_sdk::platform::documents::document_query::DocumentQuery; - - // Parse IDs - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let doc_id = Identifier::from_string( - document_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create document query - let query = DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, document_type) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))? - .with_document_id(&doc_id); - - // Fetch the data contract to get the document type - let data_contract = dash_sdk::platform::DataContract::fetch(sdk.as_ref(), contract_id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch data contract: {}", e)))? - .ok_or_else(|| JsError::new("Data contract not found"))?; - - // Get the document type - let document_type = data_contract - .document_type_for_name(document_type) - .map_err(|e| JsError::new(&format!("Document type not found: {}", e)))?; - - // Execute query - let document_result: Option = Document::fetch(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch document: {}", e)))?; - - match document_result { - Some(doc) => { - let response = DocumentResponse::from_document(&doc, &data_contract, document_type)?; - - // Use json_compatible serializer to convert maps to objects - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + #[wasm_bindgen(js_name = "getDocument")] + pub async fn get_document( + &self, + data_contract_id: &str, + document_type: &str, + document_id: &str, + ) -> Result { + use dash_sdk::platform::documents::document_query::DocumentQuery; + + // Parse IDs + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + let doc_id = Identifier::from_string( + document_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid document ID: {}", e)))?; + + // Create document query + let query = + DocumentQuery::new_with_data_contract_id(self.as_ref(), contract_id, document_type) + .await? + .with_document_id(&doc_id); + + // Fetch the data contract to get the document type + let data_contract = dash_sdk::platform::DataContract::fetch(self.as_ref(), contract_id) + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found"))?; + + // Get the document type + let document_type = data_contract + .document_type_for_name(document_type) + .map_err(|e| WasmSdkError::not_found(format!("Document type not found: {}", e)))?; + + // Execute query + let document_result: Option = Document::fetch(self.as_ref(), query).await?; + + match document_result { + Some(doc) => { + let response = + DocumentResponse::from_document(&doc, &data_contract, document_type)?; + + // Use json_compatible serializer to convert maps to objects + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => Ok(JsValue::NULL), } - None => Ok(JsValue::NULL), } -} -#[wasm_bindgen] -pub async fn get_document_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, - document_type: &str, - document_id: &str, -) -> Result { - use dash_sdk::platform::documents::document_query::DocumentQuery; - - // Parse IDs - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let doc_id = Identifier::from_string( - document_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create document query - let query = DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, document_type) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))? - .with_document_id(&doc_id); - - // Fetch the data contract to get the document type - let data_contract = dash_sdk::platform::DataContract::fetch(sdk.as_ref(), contract_id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch data contract: {}", e)))? - .ok_or_else(|| JsError::new("Data contract not found"))?; - - // Get the document type - let document_type_ref = data_contract - .document_type_for_name(document_type) - .map_err(|e| JsError::new(&format!("Document type not found: {}", e)))?; - - // Execute query with proof - let (document_result, metadata, proof) = - Document::fetch_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch document: {}", e)))?; - - match document_result { - Some(doc) => { - let doc_response = - DocumentResponse::from_document(&doc, &data_contract, document_type_ref)?; - - let response = ProofMetadataResponse { - data: doc_response, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } - None => { - // Return null data with proof - let response = ProofMetadataResponse { - data: Option::::None, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + #[wasm_bindgen(js_name = "getDocumentWithProofInfo")] + pub async fn get_document_with_proof_info( + &self, + data_contract_id: &str, + document_type: &str, + document_id: &str, + ) -> Result { + use dash_sdk::platform::documents::document_query::DocumentQuery; + + // Parse IDs + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid data contract ID: {}", e)))?; + + let doc_id = Identifier::from_string( + document_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid document ID: {}", e)))?; + + // Create document query + let query = + DocumentQuery::new_with_data_contract_id(self.as_ref(), contract_id, document_type) + .await? + .with_document_id(&doc_id); + + // Fetch the data contract to get the document type + let data_contract = dash_sdk::platform::DataContract::fetch(self.as_ref(), contract_id) + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found"))?; + + // Get the document type + let document_type_ref = data_contract + .document_type_for_name(document_type) + .map_err(|e| WasmSdkError::not_found(format!("Document type not found: {}", e)))?; + + // Execute query with proof + let (document_result, metadata, proof) = + Document::fetch_with_metadata_and_proof(self.as_ref(), query, None).await?; + + match document_result { + Some(doc) => { + let doc_response = + DocumentResponse::from_document(&doc, &data_contract, document_type_ref)?; + + let response = ProofMetadataResponse { + data: doc_response, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => { + // Return null data with proof + let response = ProofMetadataResponse { + data: Option::::None, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } } } -} -#[wasm_bindgen] -pub async fn get_dpns_usernames( - sdk: &WasmSdk, - identity_id: &str, - limit: Option, -) -> Result { - use dash_sdk::platform::documents::document_query::DocumentQuery; - use dash_sdk::platform::FetchMany; - use drive_proof_verifier::types::Documents; - - // DPNS contract ID on testnet - const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - const DPNS_DOCUMENT_TYPE: &str = "domain"; - - // Parse identity ID - let identity_id_parsed = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse DPNS contract ID - let contract_id = Identifier::from_string( - DPNS_CONTRACT_ID, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create document query for DPNS domains owned by this identity - let mut query = - DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, DPNS_DOCUMENT_TYPE) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))?; - - // Query by records.identity using the identityId index - let where_clause = WhereClause { - field: "records.identity".to_string(), - operator: WhereOperator::Equal, - value: Value::Identifier(identity_id_parsed.to_buffer()), - }; + #[wasm_bindgen(js_name = "getDpnsUsernames")] + pub async fn get_dpns_usernames( + &self, + identity_id: &str, + limit: Option, + ) -> Result { + use dash_sdk::platform::documents::document_query::DocumentQuery; + use dash_sdk::platform::FetchMany; + use drive_proof_verifier::types::Documents; + + // DPNS contract ID on testnet + const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; + const DPNS_DOCUMENT_TYPE: &str = "domain"; + + // Parse identity ID + let identity_id_parsed = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - query = query.with_where(where_clause); + // Parse DPNS contract ID + let contract_id = Identifier::from_string( + DPNS_CONTRACT_ID, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid DPNS contract ID: {}", e)))?; + + // Create document query for DPNS domains owned by this identity + let mut query = DocumentQuery::new_with_data_contract_id( + self.as_ref(), + contract_id, + DPNS_DOCUMENT_TYPE, + ) + .await?; + + // Query by records.identity using the identityId index + let where_clause = WhereClause { + field: "records.identity".to_string(), + operator: WhereOperator::Equal, + value: Value::Identifier(identity_id_parsed.to_buffer()), + }; + + query = query.with_where(where_clause); - // Set limit from parameter or default to 10 - query.limit = limit.unwrap_or(10); + // Set limit from parameter or default to 10 + query.limit = limit.unwrap_or(10); - // Execute query - let documents_result: Documents = Document::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch DPNS documents: {}", e)))?; + // Execute query + let documents_result: Documents = Document::fetch_many(self.as_ref(), query).await?; - // Collect all usernames - let mut usernames: Vec = Vec::new(); + // Collect all usernames + let mut usernames: Vec = Vec::new(); - // Process all results - for (_, doc_opt) in documents_result { - if let Some(doc) = doc_opt { - // Extract the username from the document - let properties = doc.properties(); + // Process all results + for (_, doc_opt) in documents_result { + if let Some(doc) = doc_opt { + // Extract the username from the document + let properties = doc.properties(); - if let (Some(Value::Text(label)), Some(Value::Text(parent_domain))) = ( - properties.get("label"), - properties.get("normalizedParentDomainName"), - ) { - // Construct the full username - let username = format!("{}.{}", label, parent_domain); - usernames.push(username); + if let (Some(Value::Text(label)), Some(Value::Text(parent_domain))) = ( + properties.get("label"), + properties.get("normalizedParentDomainName"), + ) { + // Construct the full username + let username = format!("{}.{}", label, parent_domain); + usernames.push(username); + } } } + + // Return usernames as a JSON array + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + usernames.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize usernames: {}", e)) + }) } - // Return usernames as a JSON array - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - usernames - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize usernames: {}", e))) -} + // Keep the old function for backward compatibility but have it call the new one + #[wasm_bindgen(js_name = "getDpnsUsername")] + pub async fn get_dpns_username(&self, identity_id: &str) -> Result { + // Call the new function with limit 1 + let result = self.get_dpns_usernames(identity_id, Some(1)).await?; -// Keep the old function for backward compatibility but have it call the new one -#[wasm_bindgen] -pub async fn get_dpns_username(sdk: &WasmSdk, identity_id: &str) -> Result { - // Call the new function with limit 1 - let result = get_dpns_usernames(sdk, identity_id, Some(1)).await?; - - // Extract the first username from the array - if let Some(array) = result.dyn_ref::() { - if array.length() > 0 { - return Ok(array.get(0)); + // Extract the first username from the array + if let Some(array) = result.dyn_ref::() { + if array.length() > 0 { + return Ok(array.get(0)); + } } + + Ok(JsValue::NULL) } - Ok(JsValue::NULL) -} + // Proof info versions for DPNS queries -// Proof info versions for DPNS queries + #[wasm_bindgen(js_name = "getDpnsUsernamesWithProofInfo")] + pub async fn get_dpns_usernames_with_proof_info( + &self, + identity_id: &str, + limit: Option, + ) -> Result { + use dash_sdk::platform::documents::document_query::DocumentQuery; + use dash_sdk::platform::FetchMany; -#[wasm_bindgen] -pub async fn get_dpns_usernames_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - limit: Option, -) -> Result { - use dash_sdk::platform::documents::document_query::DocumentQuery; - use dash_sdk::platform::FetchMany; - - // DPNS contract ID on testnet - const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - const DPNS_DOCUMENT_TYPE: &str = "domain"; - - // Parse identity ID - let identity_id_parsed = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse DPNS contract ID - let contract_id = Identifier::from_string( - DPNS_CONTRACT_ID, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create document query for DPNS domains owned by this identity - let mut query = - DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, DPNS_DOCUMENT_TYPE) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))?; - - // Query by records.identity using the identityId index - let where_clause = WhereClause { - field: "records.identity".to_string(), - operator: WhereOperator::Equal, - value: Value::Identifier(identity_id_parsed.to_buffer()), - }; + // DPNS contract ID on testnet + const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; + const DPNS_DOCUMENT_TYPE: &str = "domain"; - query = query.with_where(where_clause); + // Parse identity ID + let identity_id_parsed = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - // Set limit from parameter or default to 10 - query.limit = limit.unwrap_or(10); + // Parse DPNS contract ID + let contract_id = Identifier::from_string( + DPNS_CONTRACT_ID, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid DPNS contract ID: {}", e)))?; + + // Create document query for DPNS domains owned by this identity + let mut query = DocumentQuery::new_with_data_contract_id( + self.as_ref(), + contract_id, + DPNS_DOCUMENT_TYPE, + ) + .await?; + + // Query by records.identity using the identityId index + let where_clause = WhereClause { + field: "records.identity".to_string(), + operator: WhereOperator::Equal, + value: Value::Identifier(identity_id_parsed.to_buffer()), + }; - // Execute query with proof - let (documents_result, metadata, proof) = - Document::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch DPNS documents with proof: {}", e)) - })?; + query = query.with_where(where_clause); + + // Set limit from parameter or default to 10 + query.limit = limit.unwrap_or(10); + + // Execute query with proof + let (documents_result, metadata, proof) = + Document::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; - // Collect all usernames - let mut usernames: Vec = Vec::new(); - - // Process all results - for (_, doc_opt) in documents_result { - if let Some(doc) = doc_opt { - // Extract the username from the document - let properties = doc.properties(); - - if let (Some(Value::Text(label)), Some(Value::Text(parent_domain))) = ( - properties.get("label"), - properties.get("normalizedParentDomainName"), - ) { - // Construct the full username - let username = format!("{}.{}", label, parent_domain); - usernames.push(username); + // Collect all usernames + let mut usernames: Vec = Vec::new(); + + // Process all results + for (_, doc_opt) in documents_result { + if let Some(doc) = doc_opt { + // Extract the username from the document + let properties = doc.properties(); + + if let (Some(Value::Text(label)), Some(Value::Text(parent_domain))) = ( + properties.get("label"), + properties.get("normalizedParentDomainName"), + ) { + // Construct the full username + let username = format!("{}.{}", label, parent_domain); + usernames.push(username); + } } } - } - let response = ProofMetadataResponse { - data: usernames, - metadata: metadata.into(), - proof: proof.into(), - }; + let response = ProofMetadataResponse { + data: usernames, + metadata: metadata.into(), + proof: proof.into(), + }; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_dpns_username_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, -) -> Result { - // Call the new function with limit 1 - let result = get_dpns_usernames_with_proof_info(sdk, identity_id, Some(1)).await?; - - // The result already contains proof info, just modify the data field - // Parse the result to extract first username - let result_obj: serde_json::Value = serde_wasm_bindgen::from_value(result.clone())?; - - if let Some(data_array) = result_obj.get("data").and_then(|d| d.as_array()) { - if let Some(first_username) = data_array.first() { - // Create a new response with just the first username - let mut modified_result = result_obj.clone(); - modified_result["data"] = first_username.clone(); - - return serde_wasm_bindgen::to_value(&modified_result) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))); + #[wasm_bindgen(js_name = "getDpnsUsernameWithProofInfo")] + pub async fn get_dpns_username_with_proof_info( + &self, + identity_id: &str, + ) -> Result { + // Call the new function with limit 1 + let result = self + .get_dpns_usernames_with_proof_info(identity_id, Some(1)) + .await?; + + // The result already contains proof info, just modify the data field + // Parse the result to extract first username + let result_obj: serde_json::Value = serde_wasm_bindgen::from_value(result.clone()) + .map_err(|e| WasmSdkError::serialization(format!("Failed to parse JSON: {}", e)))?; + + if let Some(data_array) = result_obj.get("data").and_then(|d| d.as_array()) { + if let Some(first_username) = data_array.first() { + // Create a new response with just the first username + let mut modified_result = result_obj.clone(); + modified_result["data"] = first_username.clone(); + + return serde_wasm_bindgen::to_value(&modified_result).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }); + } } - } - // If no username found, return null data with proof info - let mut modified_result = result_obj.clone(); - modified_result["data"] = serde_json::Value::Null; + // If no username found, return null data with proof info + let mut modified_result = result_obj.clone(); + modified_result["data"] = serde_json::Value::Null; - serde_wasm_bindgen::to_value(&modified_result) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + serde_wasm_bindgen::to_value(&modified_result).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } } diff --git a/packages/wasm-sdk/src/queries/dpns.rs b/packages/wasm-sdk/src/queries/dpns.rs index 16a98a3923e..41b836b36a1 100644 --- a/packages/wasm-sdk/src/queries/dpns.rs +++ b/packages/wasm-sdk/src/queries/dpns.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::queries::ProofMetadataResponse; use crate::sdk::WasmSdk; use dash_sdk::dpp::document::DocumentV0Getters; @@ -5,7 +6,6 @@ use dash_sdk::dpp::platform_value::string_encoding::Encoding; use dash_sdk::platform::{Document, FetchMany}; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -16,132 +16,155 @@ struct DpnsUsernameInfo { } #[wasm_bindgen] -pub async fn get_dpns_username_by_name(sdk: &WasmSdk, username: &str) -> Result { - use dash_sdk::dpp::platform_value::Value; - use dash_sdk::platform::documents::document_query::DocumentQuery; - use drive::query::{WhereClause, WhereOperator}; - - // DPNS contract ID on testnet - const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - const DPNS_DOCUMENT_TYPE: &str = "domain"; - - // Parse username into label and domain - let parts: Vec<&str> = username.split('.').collect(); - if parts.len() != 2 { - return Err(JsError::new( - "Invalid username format. Expected format: label.dash", - )); +impl WasmSdk { + #[wasm_bindgen(js_name = "getDpnsUsernameByName")] + pub async fn get_dpns_username_by_name( + &self, + username: &str, + ) -> Result { + use dash_sdk::dpp::platform_value::Value; + use dash_sdk::platform::documents::document_query::DocumentQuery; + use drive::query::{WhereClause, WhereOperator}; + + // DPNS contract ID on testnet + const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; + const DPNS_DOCUMENT_TYPE: &str = "domain"; + + // Parse username into label and domain + let parts: Vec<&str> = username.split('.').collect(); + if parts.len() != 2 { + return Err(WasmSdkError::invalid_argument( + "Invalid username format. Expected format: label.dash", + )); + } + let label = parts[0]; + let domain = parts[1]; + + // Parse DPNS contract ID + let contract_id = + dash_sdk::dpp::prelude::Identifier::from_string(DPNS_CONTRACT_ID, Encoding::Base58) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid DPNS contract ID: {}", e)) + })?; + + // Create document query + let mut query = DocumentQuery::new_with_data_contract_id( + self.as_ref(), + contract_id, + DPNS_DOCUMENT_TYPE, + ) + .await?; + + // Query by label and normalizedParentDomainName + query = query.with_where(WhereClause { + field: "normalizedLabel".to_string(), + operator: WhereOperator::Equal, + value: Value::Text(label.to_lowercase()), + }); + + query = query.with_where(WhereClause { + field: "normalizedParentDomainName".to_string(), + operator: WhereOperator::Equal, + value: Value::Text(domain.to_lowercase()), + }); + + let documents = Document::fetch_many(self.as_ref(), query).await?; + + if let Some((_, Some(document))) = documents.into_iter().next() { + let result = DpnsUsernameInfo { + username: username.to_string(), + identity_id: document.owner_id().to_string(Encoding::Base58), + document_id: document.id().to_string(Encoding::Base58), + }; + + serde_wasm_bindgen::to_value(&result).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Err(WasmSdkError::not_found(format!( + "Username '{}' not found", + username + ))) + } } - let label = parts[0]; - let domain = parts[1]; - - // Parse DPNS contract ID - let contract_id = - dash_sdk::dpp::prelude::Identifier::from_string(DPNS_CONTRACT_ID, Encoding::Base58)?; - - // Create document query - let mut query = - DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, DPNS_DOCUMENT_TYPE) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))?; - - // Query by label and normalizedParentDomainName - query = query.with_where(WhereClause { - field: "normalizedLabel".to_string(), - operator: WhereOperator::Equal, - value: Value::Text(label.to_lowercase()), - }); - - query = query.with_where(WhereClause { - field: "normalizedParentDomainName".to_string(), - operator: WhereOperator::Equal, - value: Value::Text(domain.to_lowercase()), - }); - - let documents = Document::fetch_many(sdk.as_ref(), query).await?; - - if let Some((_, Some(document))) = documents.into_iter().next() { - let result = DpnsUsernameInfo { - username: username.to_string(), - identity_id: document.owner_id().to_string(Encoding::Base58), - document_id: document.id().to_string(Encoding::Base58), - }; - - serde_wasm_bindgen::to_value(&result) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new(&format!("Username '{}' not found", username))) - } -} -#[wasm_bindgen] -pub async fn get_dpns_username_by_name_with_proof_info( - sdk: &WasmSdk, - username: &str, -) -> Result { - use dash_sdk::dpp::platform_value::Value; - use dash_sdk::platform::documents::document_query::DocumentQuery; - use drive::query::{WhereClause, WhereOperator}; - - // DPNS contract ID on testnet - const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - const DPNS_DOCUMENT_TYPE: &str = "domain"; - - // Parse username into label and domain - let parts: Vec<&str> = username.split('.').collect(); - if parts.len() != 2 { - return Err(JsError::new( - "Invalid username format. Expected format: label.dash", - )); - } - let label = parts[0]; - let domain = parts[1]; - - // Parse DPNS contract ID - let contract_id = - dash_sdk::dpp::prelude::Identifier::from_string(DPNS_CONTRACT_ID, Encoding::Base58)?; - - // Create document query - let mut query = - DocumentQuery::new_with_data_contract_id(sdk.as_ref(), contract_id, DPNS_DOCUMENT_TYPE) - .await - .map_err(|e| JsError::new(&format!("Failed to create document query: {}", e)))?; - - // Query by label and normalizedParentDomainName - query = query.with_where(WhereClause { - field: "normalizedLabel".to_string(), - operator: WhereOperator::Equal, - value: Value::Text(label.to_lowercase()), - }); - - query = query.with_where(WhereClause { - field: "normalizedParentDomainName".to_string(), - operator: WhereOperator::Equal, - value: Value::Text(domain.to_lowercase()), - }); - - let (documents, metadata, proof) = - Document::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None).await?; - - if let Some((_, Some(document))) = documents.into_iter().next() { - let result = DpnsUsernameInfo { - username: username.to_string(), - identity_id: document.owner_id().to_string(Encoding::Base58), - document_id: document.id().to_string(Encoding::Base58), - }; - - let response = ProofMetadataResponse { - data: result, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new(&format!("Username '{}' not found", username))) + #[wasm_bindgen(js_name = "getDpnsUsernameByNameWithProofInfo")] + pub async fn get_dpns_username_by_name_with_proof_info( + &self, + username: &str, + ) -> Result { + use dash_sdk::dpp::platform_value::Value; + use dash_sdk::platform::documents::document_query::DocumentQuery; + use drive::query::{WhereClause, WhereOperator}; + + // DPNS contract ID on testnet + const DPNS_CONTRACT_ID: &str = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; + const DPNS_DOCUMENT_TYPE: &str = "domain"; + + // Parse username into label and domain + let parts: Vec<&str> = username.split('.').collect(); + if parts.len() != 2 { + return Err(WasmSdkError::invalid_argument( + "Invalid username format. Expected format: label.dash", + )); + } + let label = parts[0]; + let domain = parts[1]; + + // Parse DPNS contract ID + let contract_id = + dash_sdk::dpp::prelude::Identifier::from_string(DPNS_CONTRACT_ID, Encoding::Base58) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid DPNS contract ID: {}", e)) + })?; + + // Create document query + let mut query = DocumentQuery::new_with_data_contract_id( + self.as_ref(), + contract_id, + DPNS_DOCUMENT_TYPE, + ) + .await?; + + // Query by label and normalizedParentDomainName + query = query.with_where(WhereClause { + field: "normalizedLabel".to_string(), + operator: WhereOperator::Equal, + value: Value::Text(label.to_lowercase()), + }); + + query = query.with_where(WhereClause { + field: "normalizedParentDomainName".to_string(), + operator: WhereOperator::Equal, + value: Value::Text(domain.to_lowercase()), + }); + + let (documents, metadata, proof) = + Document::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + + if let Some((_, Some(document))) = documents.into_iter().next() { + let result = DpnsUsernameInfo { + username: username.to_string(), + identity_id: document.owner_id().to_string(Encoding::Base58), + document_id: document.id().to_string(Encoding::Base58), + }; + + let response = ProofMetadataResponse { + data: result, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Err(WasmSdkError::not_found(format!( + "Username '{}' not found", + username + ))) + } } } diff --git a/packages/wasm-sdk/src/queries/epoch.rs b/packages/wasm-sdk/src/queries/epoch.rs index 14ba731c943..4e5e82cec17 100644 --- a/packages/wasm-sdk/src/queries/epoch.rs +++ b/packages/wasm-sdk/src/queries/epoch.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::queries::ProofMetadataResponse; use crate::sdk::WasmSdk; use dash_sdk::dpp::block::extended_epoch_info::v0::ExtendedEpochInfoV0Getters; @@ -10,7 +11,7 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::str::FromStr; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -42,123 +43,6 @@ impl From for EpochInfo { } } -#[wasm_bindgen] -pub async fn get_epochs_info( - sdk: &WasmSdk, - start_epoch: Option, - count: Option, - ascending: Option, -) -> Result { - use dash_sdk::platform::types::epoch::EpochQuery; - - let query = LimitQuery { - query: EpochQuery { - start: start_epoch, - ascending: ascending.unwrap_or(true), - }, - limit: count, - start_info: None, - }; - - let epochs_result: drive_proof_verifier::types::ExtendedEpochInfos = - ExtendedEpochInfo::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch epochs info: {}", e)))?; - - // Convert to our response format - let epochs: Vec = epochs_result - .into_iter() - .filter_map(|(_, epoch_opt)| epoch_opt.map(Into::into)) - .collect(); - - serde_wasm_bindgen::to_value(&epochs) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_finalized_epoch_infos( - sdk: &WasmSdk, - start_epoch: Option, - count: Option, - ascending: Option, -) -> Result { - use dash_sdk::platform::types::finalized_epoch::FinalizedEpochQuery; - - if start_epoch.is_none() { - return Err(JsError::new( - "start_epoch is required for finalized epoch queries", - )); - } - - let start = start_epoch.unwrap(); - let is_ascending = ascending.unwrap_or(true); - let limit = count.unwrap_or(100); - - // Ensure limit is at least 1 to avoid underflow - let limit = limit.max(1); - - // Calculate end epoch based on direction and limit - let end_epoch = if is_ascending { - start.saturating_add((limit - 1) as u16) - } else { - start.saturating_sub((limit - 1) as u16) - }; - - let query = if is_ascending { - FinalizedEpochQuery { - start_epoch_index: start, - start_epoch_index_included: true, - end_epoch_index: end_epoch, - end_epoch_index_included: true, - } - } else { - FinalizedEpochQuery { - start_epoch_index: end_epoch, - start_epoch_index_included: true, - end_epoch_index: start, - end_epoch_index_included: true, - } - }; - - let epochs_result: drive_proof_verifier::types::FinalizedEpochInfos = - dash_sdk::dpp::block::finalized_epoch_info::FinalizedEpochInfo::fetch_many( - sdk.as_ref(), - query, - ) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch finalized epochs info: {}", e)))?; - - // Convert to our response format and sort by epoch index - let mut epochs: Vec = epochs_result - .into_iter() - .filter_map(|(epoch_index, epoch_opt)| { - epoch_opt.map(|epoch| { - use dash_sdk::dpp::block::finalized_epoch_info::v0::getters::FinalizedEpochInfoGettersV0; - EpochInfo { - index: epoch_index as u16, - first_core_block_height: epoch.first_core_block_height(), - first_block_height: epoch.first_block_height(), - start_time: epoch.first_block_time(), - fee_multiplier: epoch.fee_multiplier_permille() as f64 / 1000.0, - protocol_version: epoch.protocol_version(), - } - }) - }) - .collect(); - - // Sort based on ascending flag - epochs.sort_by(|a, b| { - if is_ascending { - a.index.cmp(&b.index) - } else { - b.index.cmp(&a.index) - } - }); - - serde_wasm_bindgen::to_value(&epochs) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct ProposerBlockCount { @@ -167,310 +51,419 @@ struct ProposerBlockCount { } #[wasm_bindgen] -pub async fn get_evonodes_proposed_epoch_blocks_by_ids( - sdk: &WasmSdk, - epoch: u16, - ids: Vec, -) -> Result { - use drive_proof_verifier::types::ProposerBlockCountById; - - // Parse the ProTxHash strings - let pro_tx_hashes: Vec = ids - .into_iter() - .map(|hash_str| { - ProTxHash::from_str(&hash_str) - .map_err(|e| JsError::new(&format!("Invalid ProTxHash '{}': {}", hash_str, e))) +impl WasmSdk { + #[wasm_bindgen(js_name = "getEpochsInfo")] + pub async fn get_epochs_info( + &self, + start_epoch: Option, + count: Option, + ascending: Option, + ) -> Result { + use dash_sdk::platform::types::epoch::EpochQuery; + + let query = LimitQuery { + query: EpochQuery { + start: start_epoch, + ascending: ascending.unwrap_or(true), + }, + limit: count, + start_info: None, + }; + + let epochs_result: drive_proof_verifier::types::ExtendedEpochInfos = + ExtendedEpochInfo::fetch_many(self.as_ref(), query).await?; + + // Convert to our response format + let epochs: Vec = epochs_result + .into_iter() + .filter_map(|(_, epoch_opt)| epoch_opt.map(Into::into)) + .collect(); + + serde_wasm_bindgen::to_value(&epochs).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect::, _>>()?; - - // Use FetchMany to get block counts for specific IDs - let counts = ProposerBlockCountById::fetch_many(sdk.as_ref(), (epoch, pro_tx_hashes)) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch evonode proposed blocks: {}", e)))?; - - // Convert to response format - let mut evonodes_proposed_block_counts = BTreeMap::new(); - for (identifier, count) in counts.0 { - // Convert Identifier to ProTxHash for consistent output format - let bytes = identifier.to_buffer(); - if bytes.len() == 32 { - let hash_array: [u8; 32] = bytes.try_into().unwrap(); - let pro_tx_hash = ProTxHash::from_byte_array(hash_array); - evonodes_proposed_block_counts.insert(pro_tx_hash.to_string(), count); - } } - let response = EvonodesProposedBlocksResponse { - evonodes_proposed_block_counts, - }; + #[wasm_bindgen(js_name = "getFinalizedEpochInfos")] + pub async fn get_finalized_epoch_infos( + &self, + start_epoch: Option, + count: Option, + ascending: Option, + ) -> Result { + use dash_sdk::platform::types::finalized_epoch::FinalizedEpochQuery; + + if start_epoch.is_none() { + return Err(WasmSdkError::invalid_argument( + "start_epoch is required for finalized epoch queries", + )); + } + + let start = start_epoch.unwrap(); + let is_ascending = ascending.unwrap_or(true); + let limit = count.unwrap_or(100); - serde_wasm_bindgen::to_value(&response) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Ensure limit is at least 1 to avoid underflow + let limit = limit.max(1); -#[wasm_bindgen] -pub async fn get_evonodes_proposed_epoch_blocks_by_range( - sdk: &WasmSdk, - epoch: u16, - limit: Option, - start_after: Option, - order_ascending: Option, -) -> Result { - use dash_sdk::platform::types::proposed_blocks::ProposedBlockCountEx; - use dash_sdk::platform::QueryStartInfo; - use drive_proof_verifier::types::ProposerBlockCounts; - - // Parse start_after if provided - let start_info = if let Some(start) = start_after { - let pro_tx_hash = ProTxHash::from_str(&start) - .map_err(|e| JsError::new(&format!("Invalid start_after ProTxHash: {}", e)))?; - Some(QueryStartInfo { - start_key: pro_tx_hash.to_byte_array().to_vec(), - start_included: false, - }) - } else { - None - }; - - let counts_result = ProposerBlockCounts::fetch_proposed_blocks_by_range( - sdk.as_ref(), - Some(epoch), - limit, - start_info, - ) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch evonode proposed blocks by range: {}", - e - )) - })?; - - // Convert to response format - let mut responses: Vec = counts_result - .0 - .into_iter() - .map(|(identifier, count)| { - // Convert Identifier back to ProTxHash - let bytes = identifier.to_buffer(); - let hash = dash_sdk::dpp::dashcore::hashes::sha256d::Hash::from_slice(&bytes).unwrap(); - let pro_tx_hash = ProTxHash::from_raw_hash(hash); - ProposerBlockCount { - proposer_pro_tx_hash: pro_tx_hash.to_string(), - count, + // Calculate end epoch based on direction and limit + let end_epoch = if is_ascending { + start.saturating_add((limit - 1) as u16) + } else { + start.saturating_sub((limit - 1) as u16) + }; + + let query = if is_ascending { + FinalizedEpochQuery { + start_epoch_index: start, + start_epoch_index_included: true, + end_epoch_index: end_epoch, + end_epoch_index_included: true, } + } else { + FinalizedEpochQuery { + start_epoch_index: end_epoch, + start_epoch_index_included: true, + end_epoch_index: start, + end_epoch_index_included: true, + } + }; + + let epochs_result: drive_proof_verifier::types::FinalizedEpochInfos = + dash_sdk::dpp::block::finalized_epoch_info::FinalizedEpochInfo::fetch_many( + self.as_ref(), + query, + ) + .await?; + + // Convert to our response format and sort by epoch index + let mut epochs: Vec = epochs_result + .into_iter() + .filter_map(|(epoch_index, epoch_opt)| { + epoch_opt.map(|epoch| { + use dash_sdk::dpp::block::finalized_epoch_info::v0::getters::FinalizedEpochInfoGettersV0; + EpochInfo { + index: epoch_index, + first_core_block_height: epoch.first_core_block_height(), + first_block_height: epoch.first_block_height(), + start_time: epoch.first_block_time(), + fee_multiplier: epoch.fee_multiplier_permille() as f64 / 1000.0, + protocol_version: epoch.protocol_version(), + } + }) + }) + .collect(); + + // Sort based on ascending flag + epochs.sort_by(|a, b| { + if is_ascending { + a.index.cmp(&b.index) + } else { + b.index.cmp(&a.index) + } + }); + + serde_wasm_bindgen::to_value(&epochs).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); + } - // Sort based on order_ascending (default is true) - let ascending = order_ascending.unwrap_or(true); - responses.sort_by(|a, b| { - if ascending { - a.proposer_pro_tx_hash.cmp(&b.proposer_pro_tx_hash) - } else { - b.proposer_pro_tx_hash.cmp(&a.proposer_pro_tx_hash) + #[wasm_bindgen(js_name = "getEvonodesProposedEpochBlocksByIds")] + pub async fn get_evonodes_proposed_epoch_blocks_by_ids( + &self, + epoch: u16, + ids: Vec, + ) -> Result { + use drive_proof_verifier::types::ProposerBlockCountById; + + // Parse the ProTxHash strings + let pro_tx_hashes: Vec = ids + .into_iter() + .map(|hash_str| { + ProTxHash::from_str(&hash_str).map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid ProTxHash '{}': {}", + hash_str, e + )) + }) + }) + .collect::, WasmSdkError>>()?; + + // Use FetchMany to get block counts for specific IDs + let counts = + ProposerBlockCountById::fetch_many(self.as_ref(), (epoch, pro_tx_hashes)).await?; + + // Convert to response format + let mut evonodes_proposed_block_counts = BTreeMap::new(); + for (identifier, count) in counts.0 { + // Convert Identifier to ProTxHash for consistent output format + let pro_tx_hash = ProTxHash::from_byte_array(identifier.to_buffer()); + evonodes_proposed_block_counts.insert(pro_tx_hash.to_string(), count); } - }); - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let response = EvonodesProposedBlocksResponse { + evonodes_proposed_block_counts, + }; -#[wasm_bindgen] -pub async fn get_current_epoch(sdk: &WasmSdk) -> Result { - let epoch = ExtendedEpochInfo::fetch_current(sdk.as_ref()) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch current epoch: {}", e)))?; + serde_wasm_bindgen::to_value(&response).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } - let epoch_info = EpochInfo::from(epoch); + #[wasm_bindgen(js_name = "getEvonodesProposedEpochBlocksByRange")] + pub async fn get_evonodes_proposed_epoch_blocks_by_range( + &self, + epoch: u16, + limit: Option, + start_after: Option, + order_ascending: Option, + ) -> Result { + use dash_sdk::platform::types::proposed_blocks::ProposedBlockCountEx; + use dash_sdk::platform::QueryStartInfo; + use drive_proof_verifier::types::ProposerBlockCounts; + + // Parse start_after if provided + let start_info = if let Some(start) = start_after { + let pro_tx_hash = ProTxHash::from_str(&start).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid start_after ProTxHash: {}", e)) + })?; + Some(QueryStartInfo { + start_key: pro_tx_hash.to_byte_array().to_vec(), + start_included: false, + }) + } else { + None + }; + + let counts_result = ProposerBlockCounts::fetch_proposed_blocks_by_range( + self.as_ref(), + Some(epoch), + limit, + start_info, + ) + .await?; + + // Convert to response format + let mut responses: Vec = counts_result + .0 + .into_iter() + .map(|(identifier, count)| { + // Convert Identifier back to ProTxHash + let bytes = identifier.to_buffer(); + let hash = + dash_sdk::dpp::dashcore::hashes::sha256d::Hash::from_slice(&bytes).unwrap(); + let pro_tx_hash = ProTxHash::from_raw_hash(hash); + ProposerBlockCount { + proposer_pro_tx_hash: pro_tx_hash.to_string(), + count, + } + }) + .collect(); + + // Sort based on order_ascending (default is true) + let ascending = order_ascending.unwrap_or(true); + responses.sort_by(|a, b| { + if ascending { + a.proposer_pro_tx_hash.cmp(&b.proposer_pro_tx_hash) + } else { + b.proposer_pro_tx_hash.cmp(&a.proposer_pro_tx_hash) + } + }); - serde_wasm_bindgen::to_value(&epoch_info) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_epochs_info_with_proof_info( - sdk: &WasmSdk, - start_epoch: Option, - count: Option, - ascending: Option, -) -> Result { - use dash_sdk::platform::types::epoch::EpochQuery; - - let query = LimitQuery { - query: EpochQuery { - start: start_epoch, - ascending: ascending.unwrap_or(true), - }, - limit: count, - start_info: None, - }; - - let (epochs_result, metadata, proof) = - ExtendedEpochInfo::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch epochs info with proof: {}", e)))?; - - // Convert to our response format - let epochs: Vec = epochs_result - .into_iter() - .filter_map(|(_, epoch_opt)| epoch_opt.map(Into::into)) - .collect(); - - let response = ProofMetadataResponse { - data: epochs, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getCurrentEpoch")] + pub async fn get_current_epoch(&self) -> Result { + let epoch = ExtendedEpochInfo::fetch_current(self.as_ref()).await?; -#[wasm_bindgen] -pub async fn get_current_epoch_with_proof_info(sdk: &WasmSdk) -> Result { - let (epoch, metadata, proof) = - ExtendedEpochInfo::fetch_current_with_metadata_and_proof(sdk.as_ref()) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch current epoch with proof: {}", e)) - })?; + let epoch_info = EpochInfo::from(epoch); - let epoch_info = EpochInfo::from(epoch); + serde_wasm_bindgen::to_value(&epoch_info).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } - let response = ProofMetadataResponse { - data: epoch_info, - metadata: metadata.into(), - proof: proof.into(), - }; + #[wasm_bindgen(js_name = "getEpochsInfoWithProofInfo")] + pub async fn get_epochs_info_with_proof_info( + &self, + start_epoch: Option, + count: Option, + ascending: Option, + ) -> Result { + use dash_sdk::platform::types::epoch::EpochQuery; + + let query = LimitQuery { + query: EpochQuery { + start: start_epoch, + ascending: ascending.unwrap_or(true), + }, + limit: count, + start_info: None, + }; + + let (epochs_result, metadata, proof) = + ExtendedEpochInfo::fetch_many_with_metadata_and_proof(self.as_ref(), query, None) + .await?; + + // Convert to our response format + let epochs: Vec = epochs_result + .into_iter() + .filter_map(|(_, epoch_opt)| epoch_opt.map(Into::into)) + .collect(); + + let response = ProofMetadataResponse { + data: epochs, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getCurrentEpochWithProofInfo")] + pub async fn get_current_epoch_with_proof_info(&self) -> Result { + let (epoch, metadata, proof) = + ExtendedEpochInfo::fetch_current_with_metadata_and_proof(self.as_ref()).await?; -// Additional proof info versions for epoch queries + let epoch_info = EpochInfo::from(epoch); -#[wasm_bindgen] -pub async fn get_finalized_epoch_infos_with_proof_info( - sdk: &WasmSdk, - start_epoch: Option, - count: Option, - ascending: Option, -) -> Result { - use dash_sdk::platform::types::finalized_epoch::FinalizedEpochQuery; - - if start_epoch.is_none() { - return Err(JsError::new( - "start_epoch is required for finalized epoch queries", - )); + let response = ProofMetadataResponse { + data: epoch_info, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let start = start_epoch.unwrap(); - let is_ascending = ascending.unwrap_or(true); - let limit = count.unwrap_or(100); - - // Ensure limit is at least 1 to avoid underflow - let limit = limit.max(1); - - // Calculate end epoch based on direction and limit - let end_epoch = if is_ascending { - start.saturating_add((limit - 1) as u16) - } else { - start.saturating_sub((limit - 1) as u16) - }; - - let query = if is_ascending { - FinalizedEpochQuery { - start_epoch_index: start, - start_epoch_index_included: true, - end_epoch_index: end_epoch, - end_epoch_index_included: true, - } - } else { - FinalizedEpochQuery { - start_epoch_index: end_epoch, - start_epoch_index_included: true, - end_epoch_index: start, - end_epoch_index_included: true, + // Additional proof info versions for epoch queries + + #[wasm_bindgen(js_name = "getFinalizedEpochInfosWithProofInfo")] + pub async fn get_finalized_epoch_infos_with_proof_info( + &self, + start_epoch: Option, + count: Option, + ascending: Option, + ) -> Result { + use dash_sdk::platform::types::finalized_epoch::FinalizedEpochQuery; + + if start_epoch.is_none() { + return Err(WasmSdkError::invalid_argument( + "start_epoch is required for finalized epoch queries", + )); } - }; - - let (epochs_result, metadata, proof) = dash_sdk::dpp::block::finalized_epoch_info::FinalizedEpochInfo::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch finalized epochs info with proof: {}", e)))?; - - // Convert to our response format and sort by epoch index - let mut epochs: Vec = epochs_result - .into_iter() - .filter_map(|(epoch_index, epoch_opt)| { - epoch_opt.map(|epoch| { - use dash_sdk::dpp::block::finalized_epoch_info::v0::getters::FinalizedEpochInfoGettersV0; - EpochInfo { - index: epoch_index as u16, - first_core_block_height: epoch.first_core_block_height(), - first_block_height: epoch.first_block_height(), - start_time: epoch.first_block_time(), - fee_multiplier: epoch.fee_multiplier_permille() as f64 / 1000.0, - protocol_version: epoch.protocol_version(), - } - }) - }) - .collect(); - // Sort based on ascending flag - epochs.sort_by(|a, b| { - if is_ascending { - a.index.cmp(&b.index) + let start = start_epoch.unwrap(); + let is_ascending = ascending.unwrap_or(true); + let limit = count.unwrap_or(100); + + // Ensure limit is at least 1 to avoid underflow + let limit = limit.max(1); + + // Calculate end epoch based on direction and limit + let end_epoch = if is_ascending { + start.saturating_add((limit - 1) as u16) } else { - b.index.cmp(&a.index) - } - }); - - let response = ProofMetadataResponse { - data: epochs, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + start.saturating_sub((limit - 1) as u16) + }; + + let query = if is_ascending { + FinalizedEpochQuery { + start_epoch_index: start, + start_epoch_index_included: true, + end_epoch_index: end_epoch, + end_epoch_index_included: true, + } + } else { + FinalizedEpochQuery { + start_epoch_index: end_epoch, + start_epoch_index_included: true, + end_epoch_index: start, + end_epoch_index_included: true, + } + }; + + let (epochs_result, metadata, proof) = dash_sdk::dpp::block::finalized_epoch_info::FinalizedEpochInfo::fetch_many_with_metadata_and_proof(self.as_ref(), query, None) + .await?; + + // Convert to our response format and sort by epoch index + let mut epochs: Vec = epochs_result + .into_iter() + .filter_map(|(epoch_index, epoch_opt)| { + epoch_opt.map(|epoch| { + use dash_sdk::dpp::block::finalized_epoch_info::v0::getters::FinalizedEpochInfoGettersV0; + EpochInfo { + index: epoch_index, + first_core_block_height: epoch.first_core_block_height(), + first_block_height: epoch.first_block_height(), + start_time: epoch.first_block_time(), + fee_multiplier: epoch.fee_multiplier_permille() as f64 / 1000.0, + protocol_version: epoch.protocol_version(), + } + }) + }) + .collect(); + + // Sort based on ascending flag + epochs.sort_by(|a, b| { + if is_ascending { + a.index.cmp(&b.index) + } else { + b.index.cmp(&a.index) + } + }); + + let response = ProofMetadataResponse { + data: epochs, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_evonodes_proposed_epoch_blocks_by_ids_with_proof_info( - sdk: &WasmSdk, - epoch: u16, - pro_tx_hashes: Vec, -) -> Result { - // TODO: Implement once SDK Query trait is implemented for ProposerBlockCountById - // Currently not supported due to query format issues - let _ = (sdk, epoch, pro_tx_hashes); // Parameters will be used when implemented - Err(JsError::new( - "get_evonodes_proposed_epoch_blocks_by_ids_with_proof_info is not yet implemented", - )) -} + #[wasm_bindgen(js_name = "getEvonodesProposedEpochBlocksByIdsWithProofInfo")] + pub async fn get_evonodes_proposed_epoch_blocks_by_ids_with_proof_info( + &self, + epoch: u16, + pro_tx_hashes: Vec, + ) -> Result { + // TODO: Implement once SDK Query trait is implemented for ProposerBlockCountById + // Currently not supported due to query format issues + let _ = (self, epoch, pro_tx_hashes); // Parameters will be used when implemented + Err(WasmSdkError::generic( + "get_evonodes_proposed_epoch_blocks_by_ids_with_proof_info is not yet implemented", + )) + } -#[wasm_bindgen] -pub async fn get_evonodes_proposed_epoch_blocks_by_range_with_proof_info( - sdk: &WasmSdk, - epoch: u16, - limit: Option, - start_after: Option, - order_ascending: Option, -) -> Result { - // TODO: Implement once SDK Query trait is implemented for ProposerBlockCountByRange - // Currently not supported due to query format issues - let _ = (sdk, epoch, limit, start_after, order_ascending); // Parameters will be used when implemented - Err(JsError::new( - "get_evonodes_proposed_epoch_blocks_by_range_with_proof_info is not yet implemented", - )) + #[wasm_bindgen(js_name = "getEvonodesProposedEpochBlocksByRangeWithProofInfo")] + pub async fn get_evonodes_proposed_epoch_blocks_by_range_with_proof_info( + &self, + epoch: u16, + limit: Option, + start_after: Option, + order_ascending: Option, + ) -> Result { + // TODO: Implement once SDK Query trait is implemented for ProposerBlockCountByRange + // Currently not supported due to query format issues + let _ = (self, epoch, limit, start_after, order_ascending); // Parameters will be used when implemented + Err(WasmSdkError::generic( + "get_evonodes_proposed_epoch_blocks_by_range_with_proof_info is not yet implemented", + )) + } } diff --git a/packages/wasm-sdk/src/queries/group.rs b/packages/wasm-sdk/src/queries/group.rs index df9d276d51a..a680b29426f 100644 --- a/packages/wasm-sdk/src/queries/group.rs +++ b/packages/wasm-sdk/src/queries/group.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::queries::{ProofInfo, ProofMetadataResponse, ResponseMetadata}; use crate::sdk::WasmSdk; use dash_sdk::dpp::data_contract::group::accessors::v0::GroupV0Getters; @@ -13,7 +14,7 @@ use dash_sdk::platform::{Fetch, FetchMany, Identifier}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; // Proof info functions are now included below @@ -44,43 +45,6 @@ impl GroupInfoResponse { } } -#[wasm_bindgen] -pub async fn get_group_info( - sdk: &WasmSdk, - data_contract_id: &str, - group_contract_position: u32, -) -> Result { - // Parse data contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create group query - let query = GroupQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - }; - - // Fetch the group - let group_result: Option = Group::fetch(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch group: {}", e)))?; - - match group_result { - Some(group) => { - let response = GroupInfoResponse::from_group(&group); - - // Use json_compatible serializer to convert maps to objects - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } - None => Ok(JsValue::NULL), - } -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct GroupMember { @@ -88,138 +52,496 @@ struct GroupMember { power: u32, } -#[wasm_bindgen] -pub async fn get_group_members( - sdk: &WasmSdk, - data_contract_id: &str, +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct IdentityGroupInfo { + data_contract_id: String, group_contract_position: u32, - member_ids: Option>, - start_at: Option, - limit: Option, -) -> Result { - // Parse data contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create group query - let query = GroupQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - }; - - // Fetch the group - let group_result: Option = Group::fetch(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch group: {}", e)))?; - - match group_result { - Some(group) => { - let mut members: Vec = Vec::new(); - - // If specific member IDs are requested, filter by them - if let Some(requested_ids) = member_ids { - let requested_identifiers: Result, _> = requested_ids - .iter() - .map(|id| { - Identifier::from_string( - id, + role: String, // "member", "owner", or "moderator" + power: Option, // Only for members +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GroupsDataContractInfo { + data_contract_id: String, + groups: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GroupContractPositionInfo { + position: u32, + group: GroupInfoResponse, +} + +#[wasm_bindgen] +impl WasmSdk { + #[wasm_bindgen(js_name = "getGroupInfo")] + pub async fn get_group_info( + &self, + data_contract_id: &str, + group_contract_position: u32, + ) -> Result { + // Parse data contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Create group query + let query = GroupQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + }; + + // Fetch the group + let group_result: Option = Group::fetch(self.as_ref(), query).await?; + + match group_result { + Some(group) => { + let response = GroupInfoResponse::from_group(&group); + + // Use json_compatible serializer to convert maps to objects + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => Ok(JsValue::NULL), + } + } + + #[wasm_bindgen(js_name = "getGroupMembers")] + pub async fn get_group_members( + &self, + data_contract_id: &str, + group_contract_position: u32, + member_ids: Option>, + start_at: Option, + limit: Option, + ) -> Result { + // Parse data contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Create group query + let query = GroupQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + }; + + // Fetch the group + let group_result: Option = Group::fetch(self.as_ref(), query).await?; + + match group_result { + Some(group) => { + let mut members: Vec = Vec::new(); + + // If specific member IDs are requested, filter by them + if let Some(requested_ids) = member_ids { + let requested_identifiers: Result, _> = requested_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let requested_identifiers = requested_identifiers.map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid member identity ID: {}", e)) + })?; + + for id in requested_identifiers { + if let Ok(power) = group.member_power(id) { + members.push(GroupMember { + member_id: id.to_string( + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ), + power, + }); + } + } + } else { + // Return all members with pagination + let all_members = group.members(); + let mut sorted_members: Vec<_> = all_members.iter().collect(); + sorted_members.sort_by_key(|(id, _)| *id); + + // Apply start_at if provided + let start_index = if let Some(start_id) = start_at { + let start_identifier = Identifier::from_string( + &start_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, ) - }) - .collect(); - let requested_identifiers = requested_identifiers?; - - for id in requested_identifiers { - if let Ok(power) = group.member_power(id) { + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start identity ID: {}", + e + )) + })?; + sorted_members + .iter() + .position(|(id, _)| **id > start_identifier) + .unwrap_or(sorted_members.len()) + } else { + 0 + }; + + // Apply limit + let end_index = if let Some(lim) = limit { + (start_index + lim as usize).min(sorted_members.len()) + } else { + sorted_members.len() + }; + + for (id, power) in &sorted_members[start_index..end_index] { members.push(GroupMember { - member_id: id.to_string( + member_id: (*id).to_string( dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, ), - power, + power: **power, }); } } - } else { - // Return all members with pagination - let all_members = group.members(); - let mut sorted_members: Vec<_> = all_members.iter().collect(); - sorted_members.sort_by_key(|(id, _)| *id); - - // Apply start_at if provided - let start_index = if let Some(start_id) = start_at { - let start_identifier = Identifier::from_string( - &start_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - sorted_members - .iter() - .position(|(id, _)| **id > start_identifier) - .unwrap_or(sorted_members.len()) - } else { - 0 - }; - // Apply limit - let end_index = if let Some(lim) = limit { - (start_index + lim as usize).min(sorted_members.len()) - } else { - sorted_members.len() + // Use json_compatible serializer to convert response + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + members.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => Ok(JsValue::NULL), + } + } + + #[wasm_bindgen(js_name = "getIdentityGroups")] + pub async fn get_identity_groups( + &self, + identity_id: &str, + member_data_contracts: Option>, + owner_data_contracts: Option>, + moderator_data_contracts: Option>, + ) -> Result { + // Parse identity ID + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let mut groups: Vec = Vec::new(); + + // Check member data contracts + if let Some(contracts) = member_data_contracts { + for contract_id_str in contracts { + let contract_id = Identifier::from_string( + &contract_id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid contract ID '{}': {}", + contract_id_str, e + )) + })?; + + // Fetch all groups for this contract + let query = GroupInfosQuery { + contract_id, + start_group_contract_position: None, + limit: None, }; - for (id, power) in &sorted_members[start_index..end_index] { - members.push(GroupMember { - member_id: (*id).to_string( + let groups_result = Group::fetch_many(self.as_ref(), query).await?; + + // Check each group for the identity + for (position, group_opt) in groups_result { + if let Some(group) = group_opt { + if let Ok(power) = group.member_power(id) { + groups.push(IdentityGroupInfo { + data_contract_id: contract_id_str.clone(), + group_contract_position: position as u32, + role: "member".to_string(), + power: Some(power), + }); + } + } + } + } + } + + // Note: Owner and moderator roles would require additional contract queries + // which are not yet implemented in the SDK. For now, return a warning. + if owner_data_contracts.is_some() || moderator_data_contracts.is_some() { + tracing::warn!( + target = "wasm_sdk", + "Owner/moderator role queries are not yet implemented" + ); + } + + // Use json_compatible serializer to convert response + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + groups.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getGroupInfos")] + pub async fn get_group_infos( + &self, + contract_id: &str, + start_at_info: JsValue, + count: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse start at info if provided + let start_group_contract_position = + if !start_at_info.is_null() && !start_at_info.is_undefined() { + let info = serde_wasm_bindgen::from_value::(start_at_info); + match info { + Ok(json) => { + let position = json["position"].as_u64().ok_or_else(|| { + WasmSdkError::invalid_argument("Invalid start position") + })? as u32; + let included = json["included"].as_bool().unwrap_or(false); + Some((position as GroupContractPosition, included)) + } + Err(_) => None, + } + } else { + None + }; + + // Create query + let query = GroupInfosQuery { + contract_id, + start_group_contract_position, + limit: count.map(|c| c as u16), + }; + + // Fetch groups + let groups_result = Group::fetch_many(self.as_ref(), query).await?; + + // Convert result to response format + let mut group_infos = Vec::new(); + for (position, group_opt) in groups_result { + if let Some(group) = group_opt { + let members: Vec = group.members() + .iter() + .map(|(id, power)| { + serde_json::json!({ + "memberId": id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + "power": *power + }) + }) + .collect(); + + group_infos.push(serde_json::json!({ + "groupContractPosition": position, + "members": members, + "groupRequiredPower": group.required_power() + })); + } + } + + let response = serde_json::json!({ + "groupInfos": group_infos + }); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getGroupActions")] + pub async fn get_group_actions( + &self, + contract_id: &str, + group_contract_position: u32, + status: &str, + start_at_info: JsValue, + count: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse status + let status = match status { + "ACTIVE" => GroupActionStatus::ActionActive, + "CLOSED" => GroupActionStatus::ActionClosed, + _ => { + return Err(WasmSdkError::invalid_argument(format!( + "Invalid status: {}. Must be ACTIVE or CLOSED", + status + ))) + } + }; + + // Parse start action ID if provided + let start_at_action_id = if !start_at_info.is_null() && !start_at_info.is_undefined() { + let info = serde_wasm_bindgen::from_value::(start_at_info); + match info { + Ok(json) => { + let action_id = json["actionId"] + .as_str() + .ok_or_else(|| WasmSdkError::invalid_argument("Invalid action ID"))?; + let included = json["included"].as_bool().unwrap_or(false); + Some(( + Identifier::from_string( + action_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ), - power: **power, - }); + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid action ID: {}", e)) + })?, + included, + )) } + Err(_) => None, } + } else { + None + }; - // Use json_compatible serializer to convert response - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - members - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + // Create query + let query = GroupActionsQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + status, + start_at_action_id, + limit: count.map(|c| c as u16), + }; + + // Fetch actions + let actions_result = GroupAction::fetch_many(self.as_ref(), query).await?; + + // Convert result to response format + let mut group_actions = Vec::new(); + for (action_id, action_opt) in actions_result { + if let Some(_action) = action_opt { + // For now, just return the action ID + // The full action structure requires custom serialization + group_actions.push(serde_json::json!({ + "actionId": action_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + // TODO: Serialize the full action event structure + })); + } } - None => Ok(JsValue::NULL), + + let response = serde_json::json!({ + "groupActions": group_actions + }); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } -} -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -struct IdentityGroupInfo { - data_contract_id: String, - group_contract_position: u32, - role: String, // "member", "owner", or "moderator" - power: Option, // Only for members -} + #[wasm_bindgen(js_name = "getGroupActionSigners")] + pub async fn get_group_action_signers( + &self, + contract_id: &str, + group_contract_position: u32, + status: &str, + action_id: &str, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; -#[wasm_bindgen] -pub async fn get_identity_groups( - sdk: &WasmSdk, - identity_id: &str, - member_data_contracts: Option>, - owner_data_contracts: Option>, - moderator_data_contracts: Option>, -) -> Result { - // Parse identity ID - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let mut groups: Vec = Vec::new(); - - // Check member data contracts - if let Some(contracts) = member_data_contracts { - for contract_id_str in contracts { + // Parse action ID + let action_id = Identifier::from_string( + action_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid action ID: {}", e)))?; + + // Parse status + let status = match status { + "ACTIVE" => GroupActionStatus::ActionActive, + "CLOSED" => GroupActionStatus::ActionClosed, + _ => { + return Err(WasmSdkError::invalid_argument(format!( + "Invalid status: {}. Must be ACTIVE or CLOSED", + status + ))) + } + }; + + // Create query + let query = GroupActionSignersQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + status, + action_id, + }; + + // Fetch signers + let signers_result = GroupMemberPower::fetch_many(self.as_ref(), query).await?; + + // Convert result to response format + let mut signers = Vec::new(); + for (signer_id, power_opt) in signers_result { + if let Some(power) = power_opt { + signers.push(serde_json::json!({ + "signerId": signer_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + "power": power + })); + } + } + + let response = serde_json::json!({ + "signers": signers + }); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getGroupsDataContracts")] + pub async fn get_groups_data_contracts( + &self, + data_contract_ids: Vec, + ) -> Result { + let mut results: Vec = Vec::new(); + + for contract_id_str in data_contract_ids { let contract_id = Identifier::from_string( &contract_id_str, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid contract ID '{}': {}", + contract_id_str, e + )) + })?; // Fetch all groups for this contract let query = GroupInfosQuery { @@ -228,601 +550,567 @@ pub async fn get_identity_groups( limit: None, }; - let groups_result = Group::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch groups: {}", e)))?; + let groups_result = Group::fetch_many(self.as_ref(), query).await?; + + let mut groups: Vec = Vec::new(); - // Check each group for the identity for (position, group_opt) in groups_result { if let Some(group) = group_opt { - if let Ok(power) = group.member_power(id) { - groups.push(IdentityGroupInfo { - data_contract_id: contract_id_str.clone(), - group_contract_position: position as u32, - role: "member".to_string(), - power: Some(power), - }); - } + groups.push(GroupContractPositionInfo { + position: position as u32, + group: GroupInfoResponse::from_group(&group), + }); } } - } - } - - // Note: Owner and moderator roles would require additional contract queries - // which are not yet implemented in the SDK. For now, return a warning. - if owner_data_contracts.is_some() || moderator_data_contracts.is_some() { - web_sys::console::warn_1(&JsValue::from_str( - "Warning: Owner and moderator role queries are not yet implemented", - )); - } - // Use json_compatible serializer to convert response - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - groups - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct GroupsDataContractInfo { - data_contract_id: String, - groups: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct GroupContractPositionInfo { - position: u32, - group: GroupInfoResponse, -} - -#[wasm_bindgen] -pub async fn get_group_infos( - sdk: &WasmSdk, - contract_id: &str, - start_at_info: JsValue, - count: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse start at info if provided - let start_group_contract_position = if !start_at_info.is_null() && !start_at_info.is_undefined() - { - let info = serde_wasm_bindgen::from_value::(start_at_info); - match info { - Ok(json) => { - let position = json["position"] - .as_u64() - .ok_or_else(|| JsError::new("Invalid start position"))? - as u32; - let included = json["included"].as_bool().unwrap_or(false); - Some((position as GroupContractPosition, included)) - } - Err(_) => None, + results.push(GroupsDataContractInfo { + data_contract_id: contract_id_str, + groups, + }); } - } else { - None - }; - - // Create query - let query = GroupInfosQuery { - contract_id, - start_group_contract_position, - limit: count.map(|c| c as u16), - }; - - // Fetch groups - let groups_result = Group::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch groups: {}", e)))?; - - // Convert result to response format - let mut group_infos = Vec::new(); - for (position, group_opt) in groups_result { - if let Some(group) = group_opt { - let members: Vec = group.members() - .iter() - .map(|(id, power)| { - serde_json::json!({ - "memberId": id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - "power": *power - }) - }) - .collect(); - group_infos.push(serde_json::json!({ - "groupContractPosition": position, - "members": members, - "groupRequiredPower": group.required_power() - })); - } + // Use json_compatible serializer to convert response + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + results.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let response = serde_json::json!({ - "groupInfos": group_infos - }); + // Proof versions for group queries - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getGroupInfoWithProofInfo")] + pub async fn get_group_info_with_proof_info( + &self, + data_contract_id: &str, + group_contract_position: u32, + ) -> Result { + use crate::queries::ProofMetadataResponse; -#[wasm_bindgen] -pub async fn get_group_actions( - sdk: &WasmSdk, - contract_id: &str, - group_contract_position: u32, - status: &str, - start_at_info: JsValue, - count: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse status - let status = match status { - "ACTIVE" => GroupActionStatus::ActionActive, - "CLOSED" => GroupActionStatus::ActionClosed, - _ => { - return Err(JsError::new(&format!( - "Invalid status: {}. Must be ACTIVE or CLOSED", - status - ))) - } - }; - - // Parse start action ID if provided - let start_at_action_id = if !start_at_info.is_null() && !start_at_info.is_undefined() { - let info = serde_wasm_bindgen::from_value::(start_at_info); - match info { - Ok(json) => { - let action_id = json["actionId"] - .as_str() - .ok_or_else(|| JsError::new("Invalid action ID"))?; - let included = json["included"].as_bool().unwrap_or(false); - Some(( - Identifier::from_string( - action_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?, - included, - )) - } - Err(_) => None, - } - } else { - None - }; - - // Create query - let query = GroupActionsQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - status, - start_at_action_id, - limit: count.map(|c| c as u16), - }; - - // Fetch actions - let actions_result = GroupAction::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch group actions: {}", e)))?; - - // Convert result to response format - let mut group_actions = Vec::new(); - for (action_id, action_opt) in actions_result { - if let Some(_action) = action_opt { - // For now, just return the action ID - // The full action structure requires custom serialization - group_actions.push(serde_json::json!({ - "actionId": action_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - // TODO: Serialize the full action event structure - })); - } - } + // Parse data contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; - let response = serde_json::json!({ - "groupActions": group_actions - }); + // Create group query + let query = GroupQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + }; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Fetch group with proof + let (group_result, metadata, proof) = + Group::fetch_with_metadata_and_proof(self.as_ref(), query, None).await?; -#[wasm_bindgen] -pub async fn get_group_action_signers( - sdk: &WasmSdk, - contract_id: &str, - group_contract_position: u32, - status: &str, - action_id: &str, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse action ID - let action_id = Identifier::from_string( - action_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse status - let status = match status { - "ACTIVE" => GroupActionStatus::ActionActive, - "CLOSED" => GroupActionStatus::ActionClosed, - _ => { - return Err(JsError::new(&format!( - "Invalid status: {}. Must be ACTIVE or CLOSED", - status - ))) - } - }; - - // Create query - let query = GroupActionSignersQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - status, - action_id, - }; - - // Fetch signers - let signers_result = GroupMemberPower::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch group action signers: {}", e)))?; - - // Convert result to response format - let mut signers = Vec::new(); - for (signer_id, power_opt) in signers_result { - if let Some(power) = power_opt { - signers.push(serde_json::json!({ - "signerId": signer_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - "power": power - })); - } - } + let data = group_result.map(|group| GroupInfoResponse::from_group(&group)); - let response = serde_json::json!({ - "signers": signers - }); + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_groups_data_contracts( - sdk: &WasmSdk, - data_contract_ids: Vec, -) -> Result { - let mut results: Vec = Vec::new(); + #[wasm_bindgen(js_name = "getGroupInfosWithProofInfo")] + pub async fn get_group_infos_with_proof_info( + &self, + contract_id: &str, + start_at_info: JsValue, + count: Option, + ) -> Result { + use crate::queries::ProofMetadataResponse; - for contract_id_str in data_contract_ids { + // Parse contract ID let contract_id = Identifier::from_string( - &contract_id_str, + contract_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse start at info if provided + let start_group_contract_position = + if !start_at_info.is_null() && !start_at_info.is_undefined() { + let info = serde_wasm_bindgen::from_value::(start_at_info); + match info { + Ok(json) => { + let position = json["position"].as_u64().ok_or_else(|| { + WasmSdkError::invalid_argument("Invalid start position") + })? as u32; + let included = json["included"].as_bool().unwrap_or(false); + Some((position as GroupContractPosition, included)) + } + Err(_) => None, + } + } else { + None + }; - // Fetch all groups for this contract + // Create query let query = GroupInfosQuery { contract_id, - start_group_contract_position: None, - limit: None, + start_group_contract_position, + limit: count.map(|c| c as u16), }; - let groups_result = Group::fetch_many(sdk.as_ref(), query).await.map_err(|e| { - JsError::new(&format!( - "Failed to fetch groups for contract {}: {}", - contract_id_str, e - )) - })?; - - let mut groups: Vec = Vec::new(); + // Fetch groups with proof + let (groups_result, metadata, proof) = + Group::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + // Convert result to response format + let mut group_infos = Vec::new(); for (position, group_opt) in groups_result { if let Some(group) = group_opt { - groups.push(GroupContractPositionInfo { + group_infos.push(GroupContractPositionInfo { position: position as u32, group: GroupInfoResponse::from_group(&group), }); } } - results.push(GroupsDataContractInfo { - data_contract_id: contract_id_str, - groups, + let data = serde_json::json!({ + "groupInfos": group_infos }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - // Use json_compatible serializer to convert response - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - results - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Additional proof info versions for remaining group queries -// Proof versions for group queries + #[wasm_bindgen(js_name = "getGroupMembersWithProofInfo")] + pub async fn get_group_members_with_proof_info( + &self, + data_contract_id: &str, + group_contract_position: u32, + member_ids: Option>, + start_at: Option, + limit: Option, + ) -> Result { + // Parse data contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; -#[wasm_bindgen] -pub async fn get_group_info_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, - group_contract_position: u32, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Parse data contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create group query - let query = GroupQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - }; - - // Fetch group with proof - let (group_result, metadata, proof) = - Group::fetch_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch group with proof: {}", e)))?; - - let data = group_result.map(|group| GroupInfoResponse::from_group(&group)); - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Create group query + let query = GroupQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + }; -#[wasm_bindgen] -pub async fn get_group_infos_with_proof_info( - sdk: &WasmSdk, - contract_id: &str, - start_at_info: JsValue, - count: Option, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse start at info if provided - let start_group_contract_position = if !start_at_info.is_null() && !start_at_info.is_undefined() - { - let info = serde_wasm_bindgen::from_value::(start_at_info); - match info { - Ok(json) => { - let position = json["position"] - .as_u64() - .ok_or_else(|| JsError::new("Invalid start position"))? - as u32; - let included = json["included"].as_bool().unwrap_or(false); - Some((position as GroupContractPosition, included)) - } - Err(_) => None, - } - } else { - None - }; - - // Create query - let query = GroupInfosQuery { - contract_id, - start_group_contract_position, - limit: count.map(|c| c as u16), - }; - - // Fetch groups with proof - let (groups_result, metadata, proof) = - Group::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch groups with proof: {}", e)))?; - - // Convert result to response format - let mut group_infos = Vec::new(); - for (position, group_opt) in groups_result { - if let Some(group) = group_opt { - group_infos.push(GroupContractPositionInfo { - position: position as u32, - group: GroupInfoResponse::from_group(&group), - }); - } - } + // Fetch the group with proof + let (group_result, metadata, proof) = + Group::fetch_with_metadata_and_proof(self.as_ref(), query, None).await?; - let data = serde_json::json!({ - "groupInfos": group_infos - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let data = match group_result { + Some(group) => { + let mut members: Vec = Vec::new(); -// Additional proof info versions for remaining group queries + // If specific member IDs are requested, filter by them + if let Some(requested_ids) = member_ids { + let requested_identifiers: Result, _> = requested_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let requested_identifiers = requested_identifiers.map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid member identity ID: {}", e)) + })?; -#[wasm_bindgen] -pub async fn get_group_members_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, - group_contract_position: u32, - member_ids: Option>, - start_at: Option, - limit: Option, -) -> Result { - // Parse data contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create group query - let query = GroupQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - }; - - // Fetch the group with proof - let (group_result, metadata, proof) = - Group::fetch_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch group with proof: {}", e)))?; - - let data = match group_result { - Some(group) => { - let mut members: Vec = Vec::new(); - - // If specific member IDs are requested, filter by them - if let Some(requested_ids) = member_ids { - let requested_identifiers: Result, _> = requested_ids - .iter() - .map(|id| { - Identifier::from_string( - id, + for id in requested_identifiers { + if let Ok(power) = group.member_power(id) { + members.push(GroupMember { + member_id: id.to_string( + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ), + power, + }); + } + } + } else { + // Return all members with pagination + let all_members = group.members(); + let mut sorted_members: Vec<_> = all_members.iter().collect(); + sorted_members.sort_by_key(|(id, _)| *id); + + // Apply start_at if provided + let start_index = if let Some(start_id) = start_at { + let start_identifier = Identifier::from_string( + &start_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, ) - }) - .collect(); - let requested_identifiers = requested_identifiers?; - - for id in requested_identifiers { - if let Ok(power) = group.member_power(id) { + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start identity ID: {}", + e + )) + })?; + sorted_members + .iter() + .position(|(id, _)| **id > start_identifier) + .unwrap_or(sorted_members.len()) + } else { + 0 + }; + + // Apply limit + let end_index = if let Some(lim) = limit { + (start_index + lim as usize).min(sorted_members.len()) + } else { + sorted_members.len() + }; + + for (id, power) in &sorted_members[start_index..end_index] { members.push(GroupMember { - member_id: id.to_string( + member_id: (*id).to_string( dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, ), - power, + power: **power, }); } } - } else { - // Return all members with pagination - let all_members = group.members(); - let mut sorted_members: Vec<_> = all_members.iter().collect(); - sorted_members.sort_by_key(|(id, _)| *id); - - // Apply start_at if provided - let start_index = if let Some(start_id) = start_at { - let start_identifier = Identifier::from_string( - &start_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - sorted_members - .iter() - .position(|(id, _)| **id > start_identifier) - .unwrap_or(sorted_members.len()) - } else { - 0 - }; - // Apply limit - let end_index = if let Some(lim) = limit { - (start_index + lim as usize).min(sorted_members.len()) - } else { - sorted_members.len() + Some(members) + } + None => None, + }; + + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getIdentityGroupsWithProofInfo")] + pub async fn get_identity_groups_with_proof_info( + &self, + identity_id: &str, + member_data_contracts: Option>, + owner_data_contracts: Option>, + moderator_data_contracts: Option>, + ) -> Result { + #[derive(Serialize, Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + struct IdentityGroupInfo { + data_contract_id: String, + group_contract_position: u32, + role: String, // "member", "owner", or "moderator" + power: Option, // Only for members + } + + // Parse identity ID + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let mut groups: Vec = Vec::new(); + let mut combined_metadata: Option = None; + let mut combined_proof: Option = None; + + // Check member data contracts + if let Some(contracts) = member_data_contracts { + for contract_id_str in contracts { + let contract_id = Identifier::from_string( + &contract_id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid contract ID '{}': {}", + contract_id_str, e + )) + })?; + + // Fetch all groups for this contract with proof + let query = GroupInfosQuery { + contract_id, + start_group_contract_position: None, + limit: None, }; - for (id, power) in &sorted_members[start_index..end_index] { - members.push(GroupMember { - member_id: (*id).to_string( - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ), - power: **power, - }); + let (groups_result, metadata, proof) = + Group::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Store first metadata and proof + if combined_metadata.is_none() { + combined_metadata = Some(metadata.into()); + combined_proof = Some(proof.into()); + } + + // Check each group for the identity + for (position, group_opt) in groups_result { + if let Some(group) = group_opt { + if let Ok(power) = group.member_power(id) { + groups.push(IdentityGroupInfo { + data_contract_id: contract_id_str.clone(), + group_contract_position: position as u32, + role: "member".to_string(), + power: Some(power), + }); + } + } } } + } - Some(members) + // Note: Owner and moderator roles would require additional contract queries + // which are not yet implemented in the SDK. For now, return a warning. + if owner_data_contracts.is_some() || moderator_data_contracts.is_some() { + tracing::warn!( + target = "wasm_sdk", + "Owner/moderator role queries are not yet implemented" + ); } - None => None, - }; - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} -#[wasm_bindgen] -pub async fn get_identity_groups_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - member_data_contracts: Option>, - owner_data_contracts: Option>, - moderator_data_contracts: Option>, -) -> Result { - #[derive(Serialize, Deserialize, Debug)] - #[serde(rename_all = "camelCase")] - struct IdentityGroupInfo { - data_contract_id: String, + let response = ProofMetadataResponse { + data: groups, + metadata: combined_metadata.unwrap_or_else(|| ResponseMetadata { + height: 0, + core_chain_locked_height: 0, + epoch: 0, + time_ms: 0, + protocol_version: 0, + chain_id: String::new(), + }), + proof: combined_proof.unwrap_or_else(|| ProofInfo { + grovedb_proof: String::new(), + quorum_hash: String::new(), + signature: String::new(), + round: 0, + block_id_hash: String::new(), + quorum_type: 0, + }), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getGroupActionsWithProofInfo")] + pub async fn get_group_actions_with_proof_info( + &self, + contract_id: &str, group_contract_position: u32, - role: String, // "member", "owner", or "moderator" - power: Option, // Only for members + status: &str, + start_at_info: JsValue, + count: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse status + let status = match status { + "ACTIVE" => GroupActionStatus::ActionActive, + "CLOSED" => GroupActionStatus::ActionClosed, + _ => { + return Err(WasmSdkError::invalid_argument(format!( + "Invalid status: {}. Must be ACTIVE or CLOSED", + status + ))) + } + }; + + // Parse start action ID if provided + let start_at_action_id = if !start_at_info.is_null() && !start_at_info.is_undefined() { + let info = serde_wasm_bindgen::from_value::(start_at_info); + match info { + Ok(json) => { + let action_id = json["actionId"] + .as_str() + .ok_or_else(|| WasmSdkError::invalid_argument("Invalid action ID"))?; + let included = json["included"].as_bool().unwrap_or(false); + Some(( + Identifier::from_string( + action_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid action ID: {}", e)) + })?, + included, + )) + } + Err(_) => None, + } + } else { + None + }; + + // Create query + let query = GroupActionsQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + status, + start_at_action_id, + limit: count.map(|c| c as u16), + }; + + // Fetch actions with proof + let (actions_result, metadata, proof) = + GroupAction::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Convert result to response format + let mut group_actions = Vec::new(); + for (action_id, action_opt) in actions_result { + if let Some(_action) = action_opt { + // For now, just return the action ID + // The full action structure requires custom serialization + group_actions.push(serde_json::json!({ + "actionId": action_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + // TODO: Serialize the full action event structure + })); + } + } + + let data = serde_json::json!({ + "groupActions": group_actions + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - // Parse identity ID - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + #[wasm_bindgen(js_name = "getGroupActionSignersWithProofInfo")] + pub async fn get_group_action_signers_with_proof_info( + &self, + contract_id: &str, + group_contract_position: u32, + status: &str, + action_id: &str, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse action ID + let action_id = Identifier::from_string( + action_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid action ID: {}", e)))?; + + // Parse status + let status = match status { + "ACTIVE" => GroupActionStatus::ActionActive, + "CLOSED" => GroupActionStatus::ActionClosed, + _ => { + return Err(WasmSdkError::invalid_argument(format!( + "Invalid status: {}. Must be ACTIVE or CLOSED", + status + ))) + } + }; + + // Create query + let query = GroupActionSignersQuery { + contract_id, + group_contract_position: group_contract_position as GroupContractPosition, + status, + action_id, + }; - let mut groups: Vec = Vec::new(); - let mut combined_metadata: Option = None; - let mut combined_proof: Option = None; + // Fetch signers with proof + let (signers_result, metadata, proof) = + GroupMemberPower::fetch_many_with_metadata_and_proof(self.as_ref(), query, None) + .await?; + + // Convert result to response format + let mut signers = Vec::new(); + for (signer_id, power_opt) in signers_result { + if let Some(power) = power_opt { + signers.push(serde_json::json!({ + "signerId": signer_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + "power": power + })); + } + } - // Check member data contracts - if let Some(contracts) = member_data_contracts { - for contract_id_str in contracts { + let data = serde_json::json!({ + "signers": signers + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getGroupsDataContractsWithProofInfo")] + pub async fn get_groups_data_contracts_with_proof_info( + &self, + data_contract_ids: Vec, + ) -> Result { + let mut results: Vec = Vec::new(); + let mut combined_metadata: Option = None; + let mut combined_proof: Option = None; + + for contract_id_str in data_contract_ids { let contract_id = Identifier::from_string( &contract_id_str, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid contract ID '{}': {}", + contract_id_str, e + )) + })?; // Fetch all groups for this contract with proof let query = GroupInfosQuery { @@ -832,11 +1120,7 @@ pub async fn get_identity_groups_with_proof_info( }; let (groups_result, metadata, proof) = - Group::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch groups with proof: {}", e)) - })?; + Group::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; // Store first metadata and proof if combined_metadata.is_none() { @@ -844,311 +1128,47 @@ pub async fn get_identity_groups_with_proof_info( combined_proof = Some(proof.into()); } - // Check each group for the identity + let mut groups: Vec = Vec::new(); + for (position, group_opt) in groups_result { if let Some(group) = group_opt { - if let Ok(power) = group.member_power(id) { - groups.push(IdentityGroupInfo { - data_contract_id: contract_id_str.clone(), - group_contract_position: position as u32, - role: "member".to_string(), - power: Some(power), - }); - } + groups.push(GroupContractPositionInfo { + position: position as u32, + group: GroupInfoResponse::from_group(&group), + }); } } - } - } - - // Note: Owner and moderator roles would require additional contract queries - // which are not yet implemented in the SDK. For now, return a warning. - if owner_data_contracts.is_some() || moderator_data_contracts.is_some() { - web_sys::console::warn_1(&JsValue::from_str( - "Warning: Owner and moderator role queries are not yet implemented", - )); - } - - let response = ProofMetadataResponse { - data: groups, - metadata: combined_metadata.unwrap_or_else(|| ResponseMetadata { - height: 0, - core_chain_locked_height: 0, - epoch: 0, - time_ms: 0, - protocol_version: 0, - chain_id: String::new(), - }), - proof: combined_proof.unwrap_or_else(|| ProofInfo { - grovedb_proof: String::new(), - quorum_hash: String::new(), - signature: String::new(), - round: 0, - block_id_hash: String::new(), - quorum_type: 0, - }), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_group_actions_with_proof_info( - sdk: &WasmSdk, - contract_id: &str, - group_contract_position: u32, - status: &str, - start_at_info: JsValue, - count: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse status - let status = match status { - "ACTIVE" => GroupActionStatus::ActionActive, - "CLOSED" => GroupActionStatus::ActionClosed, - _ => { - return Err(JsError::new(&format!( - "Invalid status: {}. Must be ACTIVE or CLOSED", - status - ))) - } - }; - - // Parse start action ID if provided - let start_at_action_id = if !start_at_info.is_null() && !start_at_info.is_undefined() { - let info = serde_wasm_bindgen::from_value::(start_at_info); - match info { - Ok(json) => { - let action_id = json["actionId"] - .as_str() - .ok_or_else(|| JsError::new("Invalid action ID"))?; - let included = json["included"].as_bool().unwrap_or(false); - Some(( - Identifier::from_string( - action_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?, - included, - )) - } - Err(_) => None, - } - } else { - None - }; - - // Create query - let query = GroupActionsQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - status, - start_at_action_id, - limit: count.map(|c| c as u16), - }; - - // Fetch actions with proof - let (actions_result, metadata, proof) = - GroupAction::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch group actions with proof: {}", e)) - })?; - - // Convert result to response format - let mut group_actions = Vec::new(); - for (action_id, action_opt) in actions_result { - if let Some(_action) = action_opt { - // For now, just return the action ID - // The full action structure requires custom serialization - group_actions.push(serde_json::json!({ - "actionId": action_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - // TODO: Serialize the full action event structure - })); - } - } - let data = serde_json::json!({ - "groupActions": group_actions - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_group_action_signers_with_proof_info( - sdk: &WasmSdk, - contract_id: &str, - group_contract_position: u32, - status: &str, - action_id: &str, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse action ID - let action_id = Identifier::from_string( - action_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse status - let status = match status { - "ACTIVE" => GroupActionStatus::ActionActive, - "CLOSED" => GroupActionStatus::ActionClosed, - _ => { - return Err(JsError::new(&format!( - "Invalid status: {}. Must be ACTIVE or CLOSED", - status - ))) - } - }; - - // Create query - let query = GroupActionSignersQuery { - contract_id, - group_contract_position: group_contract_position as GroupContractPosition, - status, - action_id, - }; - - // Fetch signers with proof - let (signers_result, metadata, proof) = - GroupMemberPower::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch group action signers with proof: {}", - e - )) - })?; - - // Convert result to response format - let mut signers = Vec::new(); - for (signer_id, power_opt) in signers_result { - if let Some(power) = power_opt { - signers.push(serde_json::json!({ - "signerId": signer_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - "power": power - })); + results.push(GroupsDataContractInfo { + data_contract_id: contract_id_str, + groups, + }); } - } - - let data = serde_json::json!({ - "signers": signers - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} -#[wasm_bindgen] -pub async fn get_groups_data_contracts_with_proof_info( - sdk: &WasmSdk, - data_contract_ids: Vec, -) -> Result { - let mut results: Vec = Vec::new(); - let mut combined_metadata: Option = None; - let mut combined_proof: Option = None; - - for contract_id_str in data_contract_ids { - let contract_id = Identifier::from_string( - &contract_id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch all groups for this contract with proof - let query = GroupInfosQuery { - contract_id, - start_group_contract_position: None, - limit: None, + let response = ProofMetadataResponse { + data: results, + metadata: combined_metadata.unwrap_or_else(|| ResponseMetadata { + height: 0, + core_chain_locked_height: 0, + epoch: 0, + time_ms: 0, + protocol_version: 0, + chain_id: String::new(), + }), + proof: combined_proof.unwrap_or_else(|| ProofInfo { + grovedb_proof: String::new(), + quorum_hash: String::new(), + signature: String::new(), + round: 0, + block_id_hash: String::new(), + quorum_type: 0, + }), }; - let (groups_result, metadata, proof) = - Group::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch groups for contract {} with proof: {}", - contract_id_str, e - )) - })?; - - // Store first metadata and proof - if combined_metadata.is_none() { - combined_metadata = Some(metadata.into()); - combined_proof = Some(proof.into()); - } - - let mut groups: Vec = Vec::new(); - - for (position, group_opt) in groups_result { - if let Some(group) = group_opt { - groups.push(GroupContractPositionInfo { - position: position as u32, - group: GroupInfoResponse::from_group(&group), - }); - } - } - - results.push(GroupsDataContractInfo { - data_contract_id: contract_id_str, - groups, - }); + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - - let response = ProofMetadataResponse { - data: results, - metadata: combined_metadata.unwrap_or_else(|| ResponseMetadata { - height: 0, - core_chain_locked_height: 0, - epoch: 0, - time_ms: 0, - protocol_version: 0, - chain_id: String::new(), - }), - proof: combined_proof.unwrap_or_else(|| ProofInfo { - grovedb_proof: String::new(), - quorum_hash: String::new(), - signature: String::new(), - round: 0, - block_id_hash: String::new(), - quorum_type: 0, - }), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) } diff --git a/packages/wasm-sdk/src/queries/identity.rs b/packages/wasm-sdk/src/queries/identity.rs index 7dcb44d180b..9f7be0925ff 100644 --- a/packages/wasm-sdk/src/queries/identity.rs +++ b/packages/wasm-sdk/src/queries/identity.rs @@ -1,4 +1,5 @@ use crate::dpp::IdentityWasm; +use crate::error::WasmSdkError; use crate::queries::{ProofInfo, ProofMetadataResponse, ResponseMetadata}; use crate::sdk::WasmSdk; use dash_sdk::dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; @@ -9,1627 +10,1667 @@ use js_sys::Array; use rs_dapi_client::IntoInner; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; -// Proof info functions are now included below - -#[wasm_bindgen] -pub async fn identity_fetch(sdk: &WasmSdk, base58_id: &str) -> Result { - let id = Identifier::from_string( - base58_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - Identity::fetch_by_identifier(sdk, id) - .await? - .ok_or_else(|| JsError::new("Identity not found")) - .map(Into::into) +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub(crate) struct IdentityKeyResponse { + key_id: u32, + key_type: String, + public_key_data: String, + purpose: String, + security_level: String, + read_only: bool, + disabled: bool, } -#[wasm_bindgen] -pub async fn identity_fetch_with_proof_info( - sdk: &WasmSdk, - base58_id: &str, -) -> Result { - let id = Identifier::from_string( - base58_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let (identity, metadata, proof) = - Identity::fetch_with_metadata_and_proof(sdk, id, None).await?; - - match identity { - Some(identity) => { - // Convert identity to JSON value first - let identity_json = IdentityWasm::from(identity).to_json().map_err(|e| { - JsError::new(&format!("Failed to convert identity to JSON: {:?}", e)) - })?; - let identity_value: serde_json::Value = serde_wasm_bindgen::from_value(identity_json)?; - - let response = ProofMetadataResponse { - data: identity_value, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } - None => Err(JsError::new("Identity not found")), - } +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub(crate) struct IdentityBalanceResponse { + identity_id: String, + balance: String, // String to handle large numbers } -#[wasm_bindgen] -pub async fn identity_fetch_unproved( - sdk: &WasmSdk, - base58_id: &str, -) -> Result { - use dash_sdk::platform::proto::get_identity_request::{ - GetIdentityRequestV0, Version as GetIdentityRequestVersion, - }; - use dash_sdk::platform::proto::get_identity_response::{ - get_identity_response_v0, GetIdentityResponseV0, Version, - }; - use dash_sdk::platform::proto::{GetIdentityRequest, GetIdentityResponse}; - use rs_dapi_client::{DapiRequest, RequestSettings}; - - let id = Identifier::from_string( - base58_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let request = GetIdentityRequest { - version: Some(GetIdentityRequestVersion::V0(GetIdentityRequestV0 { - id: id.to_vec(), - prove: false, // Request without proof - })), - }; - - let response: GetIdentityResponse = request - .execute(sdk.as_ref(), RequestSettings::default()) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identity: {}", e)))? - .into_inner(); - - match response.version { - Some(Version::V0(GetIdentityResponseV0 { - result: Some(get_identity_response_v0::Result::Identity(identity_bytes)), - .. - })) => { - use dash_sdk::dpp::serialization::PlatformDeserializable; - let identity = Identity::deserialize_from_bytes(identity_bytes.as_slice())?; - Ok(identity.into()) - } - _ => Err(JsError::new("Identity not found")), - } +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub(crate) struct IdentityBalanceAndRevisionResponse { + balance: String, // String to handle large numbers + revision: u64, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] -pub(crate) struct IdentityKeyResponse { +pub(crate) struct IdentityContractKeyResponse { + identity_id: String, + purpose: u32, key_id: u32, key_type: String, public_key_data: String, - purpose: String, security_level: String, read_only: bool, disabled: bool, } +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub(crate) struct IdentityContractKeysResponse { + identity_id: String, + keys: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub(crate) struct TokenBalanceResponse { + token_id: String, + balance: String, // String to handle large numbers +} + #[wasm_bindgen] -pub async fn get_identity_keys( - sdk: &WasmSdk, - identity_id: &str, - key_request_type: &str, - specific_key_ids: Option>, - search_purpose_map: Option, // JSON string for SearchKey purpose map - limit: Option, - offset: Option, -) -> Result { - // DapiRequestExecutor not needed anymore - - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); +impl WasmSdk { + #[wasm_bindgen(js_name = "getIdentity")] + pub async fn get_identity(&self, base58_id: &str) -> Result { + let id = Identifier::from_string( + base58_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + Identity::fetch_by_identifier(self.as_ref(), id) + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found")) + .map(Into::into) } - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Handle different key request types - let keys_result = match key_request_type { - "all" => { - // Use existing all keys implementation - IdentityPublicKey::fetch_many(sdk.as_ref(), id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identity keys: {}", e)))? + #[wasm_bindgen(js_name = "getIdentityWithProofInfo")] + pub async fn get_identity_with_proof_info( + &self, + base58_id: &str, + ) -> Result { + let id = Identifier::from_string( + base58_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let (identity, metadata, proof) = + Identity::fetch_with_metadata_and_proof(self.as_ref(), id, None).await?; + + match identity { + Some(identity) => { + // Convert identity to JSON value first + let identity_json = IdentityWasm::from(identity).to_json().map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert identity to JSON: {:?}", + e + )) + })?; + let identity_value: serde_json::Value = + serde_wasm_bindgen::from_value(identity_json).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert to JSON value: {}", + e + )) + })?; + + let response = ProofMetadataResponse { + data: identity_value, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => Err(WasmSdkError::not_found("Identity not found")), } - "specific" => { - // Use direct gRPC request for specific keys - use dash_sdk::platform::proto::{ - get_identity_keys_request::{GetIdentityKeysRequestV0, Version}, - key_request_type::Request, - GetIdentityKeysRequest, KeyRequestType, SpecificKeys, - }; - use rs_dapi_client::{DapiRequest, RequestSettings}; + } - let key_ids = specific_key_ids.ok_or_else(|| { - JsError::new("specific_key_ids is required for 'specific' key request type") - })?; + #[wasm_bindgen(js_name = "getIdentityUnproved")] + pub async fn get_identity_unproved( + &self, + base58_id: &str, + ) -> Result { + use dash_sdk::platform::proto::get_identity_request::{ + GetIdentityRequestV0, Version as GetIdentityRequestVersion, + }; + use dash_sdk::platform::proto::get_identity_response::{ + get_identity_response_v0, GetIdentityResponseV0, Version, + }; + use dash_sdk::platform::proto::{GetIdentityRequest, GetIdentityResponse}; + use rs_dapi_client::{DapiRequest, RequestSettings}; - let request = GetIdentityKeysRequest { - version: Some(Version::V0(GetIdentityKeysRequestV0 { - identity_id: id.to_vec(), - prove: false, - limit: Some(limit.unwrap_or(100).into()), // Always provide a limit when prove=false - offset: None, // Offsets not supported when prove=false - request_type: Some(KeyRequestType { - request: Some(Request::SpecificKeys(SpecificKeys { key_ids })), - }), - })), - }; + let id = Identifier::from_string( + base58_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - let response = request - .execute(sdk.as_ref(), RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch specific identity keys: {}", e)) - })?; + let request = GetIdentityRequest { + version: Some(GetIdentityRequestVersion::V0(GetIdentityRequestV0 { + id: id.to_vec(), + prove: false, // Request without proof + })), + }; - // Process the response to extract keys - use dash_sdk::platform::proto::{ - get_identity_keys_response::Version as ResponseVersion, GetIdentityKeysResponse, - }; - use rs_dapi_client::IntoInner; - - let response: GetIdentityKeysResponse = response.into_inner(); - match response.version { - Some(ResponseVersion::V0(response_v0)) => { - if let Some(result) = response_v0.result { - match result { - dash_sdk::platform::proto::get_identity_keys_response::get_identity_keys_response_v0::Result::Keys(keys_response) => { - // Convert keys to the expected format - let mut key_map: IdentityPublicKeys = IndexMap::new(); - for key_bytes in keys_response.keys_bytes { - use dash_sdk::dpp::serialization::PlatformDeserializable; - let key = dash_sdk::dpp::identity::identity_public_key::IdentityPublicKey::deserialize_from_bytes(key_bytes.as_slice())?; - key_map.insert(key.id(), Some(key)); - } - key_map - } - _ => return Err(JsError::new("Unexpected response format")), - } - } else { - IndexMap::new() // Return empty map if no keys found - } - } - _ => return Err(JsError::new("Unexpected response version")), + let response: GetIdentityResponse = request + .execute(self.as_ref(), RequestSettings::default()) + .await + .map_err(|e| WasmSdkError::generic(format!("Failed to fetch identity: {}", e)))? + .into_inner(); + + match response.version { + Some(Version::V0(GetIdentityResponseV0 { + result: Some(get_identity_response_v0::Result::Identity(identity_bytes)), + .. + })) => { + use dash_sdk::dpp::serialization::PlatformDeserializable; + let identity = Identity::deserialize_from_bytes(identity_bytes.as_slice()) + .map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to deserialize identity: {}", + e + )) + })?; + Ok(identity.into()) } + _ => Err(WasmSdkError::not_found("Identity not found")), } - "search" => { - // Use direct gRPC request for search keys - use dash_sdk::platform::proto::{ - get_identity_keys_request::{GetIdentityKeysRequestV0, Version}, - key_request_type::Request, - security_level_map::KeyKindRequestType as GrpcKeyKindRequestType, - GetIdentityKeysRequest, KeyRequestType, SearchKey, SecurityLevelMap, - }; - use rs_dapi_client::{DapiRequest, RequestSettings}; - use std::collections::HashMap; + } - let purpose_map_str = search_purpose_map.ok_or_else(|| { - JsError::new("search_purpose_map is required for 'search' key request type") - })?; + #[wasm_bindgen(js_name = "getIdentityKeys")] + pub async fn get_identity_keys( + &self, + identity_id: &str, + key_request_type: &str, + specific_key_ids: Option>, + search_purpose_map: Option, // JSON string for SearchKey purpose map + limit: Option, + offset: Option, + ) -> Result { + // DapiRequestExecutor not needed anymore + + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } - // Parse the JSON purpose map - let purpose_map_json: serde_json::Value = serde_json::from_str(&purpose_map_str) - .map_err(|e| JsError::new(&format!("Invalid JSON in search_purpose_map: {}", e)))?; + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - // Convert JSON to gRPC structure - let mut purpose_map = HashMap::new(); + // Handle different key request types + let keys_result = match key_request_type { + "all" => { + // Use existing all keys implementation + IdentityPublicKey::fetch_many(self.as_ref(), id).await? + } + "specific" => { + // Use direct gRPC request for specific keys + use dash_sdk::platform::proto::{ + get_identity_keys_request::{GetIdentityKeysRequestV0, Version}, + key_request_type::Request, + GetIdentityKeysRequest, KeyRequestType, SpecificKeys, + }; + use rs_dapi_client::{DapiRequest, RequestSettings}; - if let serde_json::Value::Object(map) = purpose_map_json { - for (purpose_str, security_levels) in map { - let purpose = purpose_str.parse::().map_err(|_| { - JsError::new(&format!("Invalid purpose value: {}", purpose_str)) - })?; + let key_ids = specific_key_ids.ok_or_else(|| { + WasmSdkError::invalid_argument( + "specific_key_ids is required for 'specific' key request type", + ) + })?; - let mut security_level_map = HashMap::new(); + let request = GetIdentityKeysRequest { + version: Some(Version::V0(GetIdentityKeysRequestV0 { + identity_id: id.to_vec(), + prove: false, + limit: Some(limit.unwrap_or(100)), // Always provide a limit when prove=false + offset: None, // Offsets not supported when prove=false + request_type: Some(KeyRequestType { + request: Some(Request::SpecificKeys(SpecificKeys { key_ids })), + }), + })), + }; - if let serde_json::Value::Object(levels) = security_levels { - for (level_str, kind_str) in levels { - let level = level_str.parse::().map_err(|_| { - JsError::new(&format!("Invalid security level: {}", level_str)) - })?; + let response = request + .execute(self.as_ref(), RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to fetch specific identity keys: {}", + e + )) + })?; - let kind = match kind_str.as_str().unwrap_or("") { - "current" | "0" => { - GrpcKeyKindRequestType::CurrentKeyOfKindRequest as i32 - } - "all" | "1" => GrpcKeyKindRequestType::AllKeysOfKindRequest as i32, - _ => { - return Err(JsError::new(&format!( - "Invalid key kind: {}", - kind_str - ))) + // Process the response to extract keys + use dash_sdk::platform::proto::{ + get_identity_keys_response::Version as ResponseVersion, GetIdentityKeysResponse, + }; + use rs_dapi_client::IntoInner; + + let response: GetIdentityKeysResponse = response.into_inner(); + match response.version { + Some(ResponseVersion::V0(response_v0)) => { + if let Some(result) = response_v0.result { + match result { + dash_sdk::platform::proto::get_identity_keys_response::get_identity_keys_response_v0::Result::Keys(keys_response) => { + // Convert keys to the expected format + let mut key_map: IdentityPublicKeys = IndexMap::new(); + for key_bytes in keys_response.keys_bytes { + use dash_sdk::dpp::serialization::PlatformDeserializable; + let key = dash_sdk::dpp::identity::identity_public_key::IdentityPublicKey::deserialize_from_bytes(key_bytes.as_slice()) + .map_err(|e| WasmSdkError::serialization(format!("Failed to deserialize identity public key: {}", e)))?; + key_map.insert(key.id(), Some(key)); + } + key_map } - }; - - security_level_map.insert(level, kind); + _ => return Err(WasmSdkError::generic("Unexpected response format")), + } + } else { + IndexMap::new() // Return empty map if no keys found } } - - purpose_map.insert(purpose, SecurityLevelMap { security_level_map }); + _ => return Err(WasmSdkError::generic("Unexpected response version")), } - } else { - return Err(JsError::new("search_purpose_map must be a JSON object")); } + "search" => { + // Use direct gRPC request for search keys + use dash_sdk::platform::proto::{ + get_identity_keys_request::{GetIdentityKeysRequestV0, Version}, + key_request_type::Request, + security_level_map::KeyKindRequestType as GrpcKeyKindRequestType, + GetIdentityKeysRequest, KeyRequestType, SearchKey, SecurityLevelMap, + }; + use rs_dapi_client::{DapiRequest, RequestSettings}; + use std::collections::HashMap; - let request = GetIdentityKeysRequest { - version: Some(Version::V0(GetIdentityKeysRequestV0 { - identity_id: id.to_vec(), - prove: false, - limit: Some(limit.unwrap_or(100).into()), // Always provide a limit when prove=false - offset: None, // Offsets not supported when prove=false - request_type: Some(KeyRequestType { - request: Some(Request::SearchKey(SearchKey { purpose_map })), - }), - })), - }; - - let response = request - .execute(sdk.as_ref(), RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch search identity keys: {}", e)) + let purpose_map_str = search_purpose_map.ok_or_else(|| { + WasmSdkError::invalid_argument( + "search_purpose_map is required for 'search' key request type", + ) })?; - // Process the response to extract keys - use dash_sdk::platform::proto::{ - get_identity_keys_response::Version as ResponseVersion, GetIdentityKeysResponse, - }; - use rs_dapi_client::IntoInner; - - let response: GetIdentityKeysResponse = response.into_inner(); - match response.version { - Some(ResponseVersion::V0(response_v0)) => { - if let Some(result) = response_v0.result { - match result { - dash_sdk::platform::proto::get_identity_keys_response::get_identity_keys_response_v0::Result::Keys(keys_response) => { - // Convert keys to the expected format - let mut key_map: IdentityPublicKeys = IndexMap::new(); - for key_bytes in keys_response.keys_bytes { - use dash_sdk::dpp::serialization::PlatformDeserializable; - let key = dash_sdk::dpp::identity::identity_public_key::IdentityPublicKey::deserialize_from_bytes(key_bytes.as_slice())?; - key_map.insert(key.id(), Some(key)); - } - key_map + // Parse the JSON purpose map + let purpose_map_json: serde_json::Value = serde_json::from_str(&purpose_map_str) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid JSON in search_purpose_map: {}", + e + )) + })?; + + // Convert JSON to gRPC structure + let mut purpose_map = HashMap::new(); + + if let serde_json::Value::Object(map) = purpose_map_json { + for (purpose_str, security_levels) in map { + let purpose = purpose_str.parse::().map_err(|_| { + WasmSdkError::invalid_argument(format!( + "Invalid purpose value: {}", + purpose_str + )) + })?; + + let mut security_level_map = HashMap::new(); + + if let serde_json::Value::Object(levels) = security_levels { + for (level_str, kind_str) in levels { + let level = level_str.parse::().map_err(|_| { + WasmSdkError::invalid_argument(format!( + "Invalid security level: {}", + level_str + )) + })?; + + let kind = match kind_str.as_str().unwrap_or("") { + "current" | "0" => { + GrpcKeyKindRequestType::CurrentKeyOfKindRequest as i32 + } + "all" | "1" => { + GrpcKeyKindRequestType::AllKeysOfKindRequest as i32 + } + _ => { + return Err(WasmSdkError::invalid_argument(format!( + "Invalid key kind: {}", + kind_str + ))) + } + }; + + security_level_map.insert(level, kind); } - _ => return Err(JsError::new("Unexpected response format")), } - } else { - return Err(JsError::new("No keys found in response")); + + purpose_map.insert(purpose, SecurityLevelMap { security_level_map }); } + } else { + return Err(WasmSdkError::invalid_argument( + "search_purpose_map must be a JSON object", + )); } - _ => return Err(JsError::new("Unexpected response version")), - } - } - _ => { - return Err(JsError::new( - "Invalid key_request_type. Use 'all', 'specific', or 'search'", - )); - } - }; - - // Convert keys to response format - let mut keys: Vec = Vec::new(); - - // Apply offset and limit if provided - let start = offset.unwrap_or(0) as usize; - let end = if let Some(lim) = limit { - start + lim as usize - } else { - usize::MAX - }; - - for (idx, (key_id, key_opt)) in keys_result.into_iter().enumerate() { - if idx < start { - continue; - } - if idx >= end { - break; - } - if let Some(key) = key_opt { - keys.push(IdentityKeyResponse { - key_id: key_id, - key_type: format!("{:?}", key.key_type()), - public_key_data: hex::encode(key.data().as_slice()), - purpose: format!("{:?}", key.purpose()), - security_level: format!("{:?}", key.security_level()), - read_only: key.read_only(), - disabled: key.disabled_at().is_some(), - }); - } - } + let request = GetIdentityKeysRequest { + version: Some(Version::V0(GetIdentityKeysRequestV0 { + identity_id: id.to_vec(), + prove: false, + limit: Some(limit.unwrap_or(100)), // Always provide a limit when prove=false + offset: None, // Offsets not supported when prove=false + request_type: Some(KeyRequestType { + request: Some(Request::SearchKey(SearchKey { purpose_map })), + }), + })), + }; - serde_wasm_bindgen::to_value(&keys) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let response = request + .execute(self.as_ref(), RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to fetch search identity keys: {}", + e + )) + })?; -#[wasm_bindgen] -pub async fn get_identity_nonce(sdk: &WasmSdk, identity_id: &str) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityNonceFetcher; + // Process the response to extract keys + use dash_sdk::platform::proto::{ + get_identity_keys_response::Version as ResponseVersion, GetIdentityKeysResponse, + }; + use rs_dapi_client::IntoInner; + + let response: GetIdentityKeysResponse = response.into_inner(); + match response.version { + Some(ResponseVersion::V0(response_v0)) => { + if let Some(result) = response_v0.result { + match result { + dash_sdk::platform::proto::get_identity_keys_response::get_identity_keys_response_v0::Result::Keys(keys_response) => { + // Convert keys to the expected format + let mut key_map: IdentityPublicKeys = IndexMap::new(); + for key_bytes in keys_response.keys_bytes { + use dash_sdk::dpp::serialization::PlatformDeserializable; + let key = dash_sdk::dpp::identity::identity_public_key::IdentityPublicKey::deserialize_from_bytes(key_bytes.as_slice()) + .map_err(|e| WasmSdkError::serialization(format!("Failed to deserialize identity public key: {}", e)))?; + key_map.insert(key.id(), Some(key)); + } + key_map + } + _ => return Err(WasmSdkError::generic("Unexpected response format")), + } + } else { + return Err(WasmSdkError::not_found("No keys found in response")); + } + } + _ => return Err(WasmSdkError::generic("Unexpected response version")), + } + } + _ => { + return Err(WasmSdkError::invalid_argument( + "Invalid key_request_type. Use 'all', 'specific', or 'search'", + )); + } + }; - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + // Convert keys to response format + let mut keys: Vec = Vec::new(); - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + // Apply offset and limit if provided + let start = offset.unwrap_or(0) as usize; + let end = if let Some(lim) = limit { + start + lim as usize + } else { + usize::MAX + }; - let nonce_result = IdentityNonceFetcher::fetch(sdk.as_ref(), id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identity nonce: {}", e)))?; + for (idx, (key_id, key_opt)) in keys_result.into_iter().enumerate() { + if idx < start { + continue; + } + if idx >= end { + break; + } - let nonce = nonce_result - .map(|fetcher| fetcher.0) - .ok_or_else(|| JsError::new("Identity nonce not found"))?; + if let Some(key) = key_opt { + keys.push(IdentityKeyResponse { + key_id, + key_type: format!("{:?}", key.key_type()), + public_key_data: hex::encode(key.data().as_slice()), + purpose: format!("{:?}", key.purpose()), + security_level: format!("{:?}", key.security_level()), + read_only: key.read_only(), + disabled: key.disabled_at().is_some(), + }); + } + } - // Return as a JSON object with nonce as string to avoid BigInt serialization issues - #[derive(Serialize)] - struct NonceResponse { - nonce: String, + serde_wasm_bindgen::to_value(&keys).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let response = NonceResponse { - nonce: nonce.to_string(), - }; + #[wasm_bindgen(js_name = "getIdentityNonce")] + pub async fn get_identity_nonce(&self, identity_id: &str) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityNonceFetcher; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } -#[wasm_bindgen] -pub async fn get_identity_nonce_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, -) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityNonceFetcher; - - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + let nonce_result = IdentityNonceFetcher::fetch(self.as_ref(), id).await?; - let (nonce_result, metadata, proof) = - IdentityNonceFetcher::fetch_with_metadata_and_proof(sdk.as_ref(), id, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch identity nonce with proof: {}", e)) - })?; + let nonce = nonce_result + .map(|fetcher| fetcher.0) + .ok_or_else(|| WasmSdkError::not_found("Identity nonce not found"))?; - let nonce = nonce_result - .map(|fetcher| fetcher.0) - .ok_or_else(|| JsError::new("Identity nonce not found"))?; - - let data = serde_json::json!({ - "nonce": nonce.to_string() - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Return as a JSON object with nonce as string to avoid BigInt serialization issues + #[derive(Serialize)] + struct NonceResponse { + nonce: String, + } -#[wasm_bindgen] -pub async fn get_identity_contract_nonce( - sdk: &WasmSdk, - identity_id: &str, - contract_id: &str, -) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityContractNonceFetcher; - - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + let response = NonceResponse { + nonce: nonce.to_string(), + }; - if contract_id.is_empty() { - return Err(JsError::new("Contract ID is required")); + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let identity_id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + #[wasm_bindgen(js_name = "getIdentityNonceWithProofInfo")] + pub async fn get_identity_nonce_with_proof_info( + &self, + identity_id: &str, + ) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityNonceFetcher; - let nonce_result = - IdentityContractNonceFetcher::fetch(sdk.as_ref(), (identity_id, contract_id)) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch identity contract nonce: {}", e)) - })?; + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } - let nonce = nonce_result - .map(|fetcher| fetcher.0) - .ok_or_else(|| JsError::new("Identity contract nonce not found"))?; + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - // Return as a JSON object with nonce as string to avoid BigInt serialization issues - #[derive(Serialize)] - struct NonceResponse { - nonce: String, - } + let (nonce_result, metadata, proof) = + IdentityNonceFetcher::fetch_with_metadata_and_proof(self.as_ref(), id, None).await?; - let response = NonceResponse { - nonce: nonce.to_string(), - }; + let nonce = nonce_result + .map(|fetcher| fetcher.0) + .ok_or_else(|| WasmSdkError::not_found("Identity nonce not found"))?; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let data = serde_json::json!({ + "nonce": nonce.to_string() + }); -#[wasm_bindgen] -pub async fn get_identity_contract_nonce_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - contract_id: &str, -) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityContractNonceFetcher; - - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; - if contract_id.is_empty() { - return Err(JsError::new("Contract ID is required")); + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let identity_id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + #[wasm_bindgen(js_name = "getIdentityContractNonce")] + pub async fn get_identity_contract_nonce( + &self, + identity_id: &str, + contract_id: &str, + ) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityContractNonceFetcher; + + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + if contract_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Contract ID is required")); + } - let (nonce_result, metadata, proof) = - IdentityContractNonceFetcher::fetch_with_metadata_and_proof( - sdk.as_ref(), - (identity_id, contract_id), - None, + let identity_id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, ) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity contract nonce with proof: {}", - e - )) - })?; - - let nonce = nonce_result - .map(|fetcher| fetcher.0) - .ok_or_else(|| JsError::new("Identity contract nonce not found"))?; - - let data = serde_json::json!({ - "nonce": nonce.to_string() - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_identity_balance(sdk: &WasmSdk, id: &str) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityBalance; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - if id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; - let identity_id = Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + let nonce_result = + IdentityContractNonceFetcher::fetch(self.as_ref(), (identity_id, contract_id)).await?; - let balance_result = IdentityBalance::fetch(sdk.as_ref(), identity_id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identity balance: {}", e)))?; + let nonce = nonce_result + .map(|fetcher| fetcher.0) + .ok_or_else(|| WasmSdkError::not_found("Identity contract nonce not found"))?; - if let Some(balance) = balance_result { - // Return as object with balance as string to handle large numbers + // Return as a JSON object with nonce as string to avoid BigInt serialization issues #[derive(Serialize)] - struct BalanceResponse { - balance: String, + struct NonceResponse { + nonce: String, } - let response = BalanceResponse { - balance: balance.to_string(), + let response = NonceResponse { + nonce: nonce.to_string(), }; // Use json_compatible serializer let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new("Identity balance not found")) + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } -} -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub(crate) struct IdentityBalanceResponse { - identity_id: String, - balance: String, // String to handle large numbers -} + #[wasm_bindgen(js_name = "getIdentityContractNonceWithProofInfo")] + pub async fn get_identity_contract_nonce_with_proof_info( + &self, + identity_id: &str, + contract_id: &str, + ) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityContractNonceFetcher; + + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } -#[wasm_bindgen] -pub async fn get_identities_balances( - sdk: &WasmSdk, - identity_ids: Vec, -) -> Result { - use drive_proof_verifier::types::IdentityBalance; - - // Convert string IDs to Identifiers - let identifiers: Vec = identity_ids - .into_iter() - .map(|id| { - Identifier::from_string( - &id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect::, _>>()?; + if contract_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Contract ID is required")); + } - let balances_result: drive_proof_verifier::types::IdentityBalances = - IdentityBalance::fetch_many(sdk.as_ref(), identifiers.clone()) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identities balances: {}", e)))?; - - // Convert to response format - let responses: Vec = identifiers - .into_iter() - .filter_map(|id| { - balances_result.get(&id).and_then(|balance_opt| { - balance_opt.map(|balance| IdentityBalanceResponse { - identity_id: id.to_string( - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ), - balance: balance.to_string(), - }) - }) - }) - .collect(); + let identity_id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub(crate) struct IdentityBalanceAndRevisionResponse { - balance: String, // String to handle large numbers - revision: u64, -} + let (nonce_result, metadata, proof) = + IdentityContractNonceFetcher::fetch_with_metadata_and_proof( + self.as_ref(), + (identity_id, contract_id), + None, + ) + .await?; -#[wasm_bindgen] -pub async fn get_identity_balance_and_revision( - sdk: &WasmSdk, - identity_id: &str, -) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityBalanceAndRevision; - - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + let nonce = nonce_result + .map(|fetcher| fetcher.0) + .ok_or_else(|| WasmSdkError::not_found("Identity contract nonce not found"))?; - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let result = IdentityBalanceAndRevision::fetch(sdk.as_ref(), id) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity balance and revision: {}", - e - )) - })?; + let data = serde_json::json!({ + "nonce": nonce.to_string() + }); - if let Some(balance_and_revision) = result { - let response = IdentityBalanceAndRevisionResponse { - balance: balance_and_revision.0.to_string(), - revision: balance_and_revision.1, + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), }; // Use json_compatible serializer let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new("Identity balance and revision not found")) + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } -} -#[wasm_bindgen] -pub async fn get_identity_by_public_key_hash( - sdk: &WasmSdk, - public_key_hash: &str, -) -> Result { - use dash_sdk::platform::types::identity::PublicKeyHash; - - // Parse the hex-encoded public key hash - let hash_bytes = hex::decode(public_key_hash) - .map_err(|e| JsError::new(&format!("Invalid public key hash hex: {}", e)))?; - - if hash_bytes.len() != 20 { - return Err(JsError::new( - "Public key hash must be 20 bytes (40 hex characters)", - )); - } + #[wasm_bindgen(js_name = "getIdentityBalance")] + pub async fn get_identity_balance(&self, id: &str) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityBalance; - let mut hash_array = [0u8; 20]; - hash_array.copy_from_slice(&hash_bytes); + if id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } - let result = Identity::fetch(sdk.as_ref(), PublicKeyHash(hash_array)) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity by public key hash: {}", - e - )) - })?; + let identity_id = Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - result - .ok_or_else(|| JsError::new("Identity not found for public key hash")) - .map(Into::into) -} + let balance_result = IdentityBalance::fetch(self.as_ref(), identity_id).await?; -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub(crate) struct IdentityContractKeyResponse { - identity_id: String, - purpose: u32, - key_id: u32, - key_type: String, - public_key_data: String, - security_level: String, - read_only: bool, - disabled: bool, -} + if let Some(balance) = balance_result { + // Return as object with balance as string to handle large numbers + #[derive(Serialize)] + struct BalanceResponse { + balance: String, + } -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub(crate) struct IdentityContractKeysResponse { - identity_id: String, - keys: Vec, -} + let response = BalanceResponse { + balance: balance.to_string(), + }; -#[wasm_bindgen] -pub async fn get_identities_contract_keys( - sdk: &WasmSdk, - identities_ids: Vec, - contract_id: &str, - purposes: Option>, -) -> Result { - use dash_sdk::dpp::identity::Purpose; - - // Convert string IDs to Identifiers - let _identity_ids: Vec = identities_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Err(WasmSdkError::not_found("Identity balance not found")) + } + } + + #[wasm_bindgen(js_name = "getIdentitiesBalances")] + pub async fn get_identities_balances( + &self, + identity_ids: Vec, + ) -> Result { + use drive_proof_verifier::types::IdentityBalance; + + // Convert string IDs to Identifiers + let identifiers: Vec = identity_ids + .into_iter() + .map(|id| { + Identifier::from_string( + &id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect::, _>>() + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let balances_result: drive_proof_verifier::types::IdentityBalances = + IdentityBalance::fetch_many(self.as_ref(), identifiers.clone()).await?; + + // Convert to response format + let responses: Vec = identifiers + .into_iter() + .filter_map(|id| { + balances_result.get(&id).and_then(|balance_opt| { + balance_opt.map(|balance| IdentityBalanceResponse { + identity_id: id.to_string( + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ), + balance: balance.to_string(), + }) + }) + }) + .collect(); + + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect::, _>>()?; - - // Contract ID is not used in the individual key queries, but we validate it - let _contract_identifier = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Convert purposes if provided - let purposes_opt = purposes.map(|p| { - p.into_iter() - .filter_map(|purpose_int| match purpose_int { - 0 => Some(Purpose::AUTHENTICATION as u32), - 1 => Some(Purpose::ENCRYPTION as u32), - 2 => Some(Purpose::DECRYPTION as u32), - 3 => Some(Purpose::TRANSFER as u32), - 4 => Some(Purpose::SYSTEM as u32), - 5 => Some(Purpose::VOTING as u32), - _ => None, + } + + #[wasm_bindgen(js_name = "getIdentityBalanceAndRevision")] + pub async fn get_identity_balance_and_revision( + &self, + identity_id: &str, + ) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityBalanceAndRevision; + + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } + + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let result = IdentityBalanceAndRevision::fetch(self.as_ref(), id).await?; + + if let Some(balance_and_revision) = result { + let response = IdentityBalanceAndRevisionResponse { + balance: balance_and_revision.0.to_string(), + revision: balance_and_revision.1, + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect::>() - }); + } else { + Err(WasmSdkError::not_found( + "Identity balance and revision not found", + )) + } + } - // For now, we'll implement this by fetching keys for each identity individually - // The SDK doesn't fully expose the batch query yet - let mut responses: Vec = Vec::new(); + #[wasm_bindgen(js_name = "getIdentityByPublicKeyHash")] + pub async fn get_identity_by_public_key_hash( + &self, + public_key_hash: &str, + ) -> Result { + use dash_sdk::platform::types::identity::PublicKeyHash; - for identity_id_str in identities_ids { - let identity_id = Identifier::from_string( - &identity_id_str, + // Parse the hex-encoded public key hash + let hash_bytes = hex::decode(public_key_hash).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid public key hash hex: {}", e)) + })?; + + if hash_bytes.len() != 20 { + return Err(WasmSdkError::invalid_argument( + "Public key hash must be 20 bytes (40 hex characters)", + )); + } + + let mut hash_array = [0u8; 20]; + hash_array.copy_from_slice(&hash_bytes); + + let result = Identity::fetch(self.as_ref(), PublicKeyHash(hash_array)).await?; + + result + .ok_or_else(|| WasmSdkError::not_found("Identity not found for public key hash")) + .map(Into::into) + } + + #[wasm_bindgen(js_name = "getIdentitiesContractKeys")] + pub async fn get_identities_contract_keys( + &self, + identities_ids: Vec, + contract_id: &str, + purposes: Option>, + ) -> Result { + use dash_sdk::dpp::identity::Purpose; + + // Convert string IDs to Identifiers + let _identity_ids: Vec = identities_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect::, _>>() + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Contract ID is not used in the individual key queries, but we validate it + let _contract_identifier = Identifier::from_string( + contract_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Convert purposes if provided + let purposes_opt = purposes.map(|p| { + p.into_iter() + .filter_map(|purpose_int| match purpose_int { + 0 => Some(Purpose::AUTHENTICATION as u32), + 1 => Some(Purpose::ENCRYPTION as u32), + 2 => Some(Purpose::DECRYPTION as u32), + 3 => Some(Purpose::TRANSFER as u32), + 4 => Some(Purpose::SYSTEM as u32), + 5 => Some(Purpose::VOTING as u32), + _ => None, + }) + .collect::>() + }); - // Get keys for this identity using the regular identity keys query - let keys_result = IdentityPublicKey::fetch_many(sdk.as_ref(), identity_id) - .await + // For now, we'll implement this by fetching keys for each identity individually + // The SDK doesn't fully expose the batch query yet + let mut responses: Vec = Vec::new(); + + for identity_id_str in identities_ids { + let identity_id = Identifier::from_string( + &identity_id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) .map_err(|e| { - JsError::new(&format!( - "Failed to fetch keys for identity {}: {}", + WasmSdkError::invalid_argument(format!( + "Invalid identity ID '{}': {}", identity_id_str, e )) })?; - let mut identity_keys = Vec::new(); + // Get keys for this identity using the regular identity keys query + let keys_result = IdentityPublicKey::fetch_many(self.as_ref(), identity_id).await?; - // Filter keys by purpose if specified - for (key_id, key_opt) in keys_result { - if let Some(key) = key_opt { - // Check if this key matches the requested purposes - if let Some(ref purposes) = purposes_opt { - if !purposes.contains(&(key.purpose() as u32)) { - continue; + let mut identity_keys = Vec::new(); + + // Filter keys by purpose if specified + for (key_id, key_opt) in keys_result { + if let Some(key) = key_opt { + // Check if this key matches the requested purposes + if let Some(ref purposes) = purposes_opt { + if !purposes.contains(&(key.purpose() as u32)) { + continue; + } } + + let key_response = IdentityKeyResponse { + key_id, + key_type: format!("{:?}", key.key_type()), + public_key_data: hex::encode(key.data().as_slice()), + purpose: format!("{:?}", key.purpose()), + security_level: format!("{:?}", key.security_level()), + read_only: key.read_only(), + disabled: key.disabled_at().is_some(), + }; + identity_keys.push(key_response); } + } - let key_response = IdentityKeyResponse { - key_id: key_id, - key_type: format!("{:?}", key.key_type()), - public_key_data: hex::encode(key.data().as_slice()), - purpose: format!("{:?}", key.purpose()), - security_level: format!("{:?}", key.security_level()), - read_only: key.read_only(), - disabled: key.disabled_at().is_some(), - }; - identity_keys.push(key_response); + if !identity_keys.is_empty() { + responses.push(IdentityContractKeysResponse { + identity_id: identity_id_str, + keys: identity_keys, + }); } } - if !identity_keys.is_empty() { - responses.push(IdentityContractKeysResponse { - identity_id: identity_id_str, - keys: identity_keys, - }); - } + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getIdentityByNonUniquePublicKeyHash")] + pub async fn get_identity_by_non_unique_public_key_hash( + &self, + public_key_hash: &str, + start_after: Option, + ) -> Result { + // Parse the hex-encoded public key hash + let hash_bytes = hex::decode(public_key_hash).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid public key hash hex: {}", e)) + })?; -#[wasm_bindgen] -pub async fn get_identity_by_non_unique_public_key_hash( - sdk: &WasmSdk, - public_key_hash: &str, - start_after: Option, -) -> Result { - // Parse the hex-encoded public key hash - let hash_bytes = hex::decode(public_key_hash) - .map_err(|e| JsError::new(&format!("Invalid public key hash hex: {}", e)))?; - - if hash_bytes.len() != 20 { - return Err(JsError::new( - "Public key hash must be 20 bytes (40 hex characters)", - )); - } + if hash_bytes.len() != 20 { + return Err(WasmSdkError::invalid_argument( + "Public key hash must be 20 bytes (40 hex characters)", + )); + } - let mut hash_array = [0u8; 20]; - hash_array.copy_from_slice(&hash_bytes); + let mut hash_array = [0u8; 20]; + hash_array.copy_from_slice(&hash_bytes); - // Convert start_after if provided - let start_id = if let Some(start) = start_after { - Some(Identifier::from_string( - &start, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?) - } else { - None - }; - - use dash_sdk::platform::types::identity::NonUniquePublicKeyHashQuery; - - let query = NonUniquePublicKeyHashQuery { - key_hash: hash_array, - after: start_id.map(|id| *id.as_bytes()), - }; - - // Fetch identity by non-unique public key hash - let identity = Identity::fetch(sdk.as_ref(), query).await.map_err(|e| { - JsError::new(&format!( - "Failed to fetch identities by non-unique public key hash: {}", - e - )) - })?; - - // Return array with single identity if found - let results = if let Some(id) = identity { - vec![id] - } else { - vec![] - }; - - // Convert results to IdentityWasm - let identities: Vec = results.into_iter().map(Into::into).collect(); - - // Create JS array directly - let js_array = Array::new(); - for identity in identities { - let json = identity - .to_json() - .map_err(|e| JsError::new(&format!("Failed to convert identity to JSON: {:?}", e)))?; - js_array.push(&json); - } - Ok(js_array.into()) -} + // Convert start_after if provided + let start_id = if let Some(start) = start_after { + Some( + Identifier::from_string( + &start, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start_after identity ID: {}", + e + )) + })?, + ) + } else { + None + }; -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub(crate) struct TokenBalanceResponse { - token_id: String, - balance: String, // String to handle large numbers -} + use dash_sdk::platform::types::identity::NonUniquePublicKeyHashQuery; -#[wasm_bindgen] -pub async fn get_identity_token_balances( - sdk: &WasmSdk, - identity_id: &str, - token_ids: Vec, -) -> Result { - use dash_sdk::dpp::balances::credits::TokenAmount; - use dash_sdk::platform::tokens::identity_token_balances::IdentityTokenBalancesQuery; - - let identity_id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Convert token IDs to Identifiers - let token_identifiers: Vec = token_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect::, _>>()?; + let query = NonUniquePublicKeyHashQuery { + key_hash: hash_array, + after: start_id.map(|id| *id.as_bytes()), + }; - let query = IdentityTokenBalancesQuery { - identity_id, - token_ids: token_identifiers.clone(), - }; + // Fetch identity by non-unique public key hash + let identity = Identity::fetch(self.as_ref(), query).await?; - // Use FetchMany trait to fetch token balances - let balances: drive_proof_verifier::types::identity_token_balance::IdentityTokenBalances = - TokenAmount::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch identity token balances: {}", e)) + // Return array with single identity if found + let results = if let Some(id) = identity { + vec![id] + } else { + vec![] + }; + + // Convert results to IdentityWasm + let identities: Vec = results.into_iter().map(Into::into).collect(); + + // Create JS array directly + let js_array = Array::new(); + for identity in identities { + let json = identity.to_json().map_err(|e| { + WasmSdkError::serialization(format!("Failed to convert identity to JSON: {:?}", e)) })?; + js_array.push(&json); + } + Ok(js_array.into()) + } - // Convert to response format - let responses: Vec = token_identifiers - .into_iter() - .zip(token_ids.into_iter()) - .filter_map(|(token_id, token_id_str)| { - balances.get(&token_id).and_then(|balance_opt| { - balance_opt.map(|balance| TokenBalanceResponse { - token_id: token_id_str, - balance: balance.to_string(), - }) + #[wasm_bindgen(js_name = "getIdentityTokenBalances")] + pub async fn get_identity_token_balances( + &self, + identity_id: &str, + token_ids: Vec, + ) -> Result { + use dash_sdk::dpp::balances::credits::TokenAmount; + use dash_sdk::platform::tokens::identity_token_balances::IdentityTokenBalancesQuery; + + let identity_id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Convert token IDs to Identifiers + let token_identifiers: Vec = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) }) - }) - .collect(); + .collect::, _>>() + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let query = IdentityTokenBalancesQuery { + identity_id, + token_ids: token_identifiers.clone(), + }; -// Proof info versions for identity queries + // Use FetchMany trait to fetch token balances + let balances: drive_proof_verifier::types::identity_token_balance::IdentityTokenBalances = + TokenAmount::fetch_many(self.as_ref(), query).await?; + + // Convert to response format + let responses: Vec = token_identifiers + .into_iter() + .zip(token_ids.into_iter()) + .filter_map(|(token_id, token_id_str)| { + balances.get(&token_id).and_then(|balance_opt| { + balance_opt.map(|balance| TokenBalanceResponse { + token_id: token_id_str, + balance: balance.to_string(), + }) + }) + }) + .collect(); -#[wasm_bindgen] -pub async fn get_identity_keys_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - key_request_type: &str, - specific_key_ids: Option>, - limit: Option, - offset: Option, -) -> Result { - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Handle different key request types - let (keys_result, metadata, proof) = match key_request_type { - "all" => { - // Use existing all keys implementation with proof - IdentityPublicKey::fetch_many_with_metadata_and_proof(sdk.as_ref(), id, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch identity keys with proof: {}", e)) - })? + // Proof info versions for identity queries + + #[wasm_bindgen(js_name = "getIdentityKeysWithProofInfo")] + pub async fn get_identity_keys_with_proof_info( + &self, + identity_id: &str, + key_request_type: &str, + specific_key_ids: Option>, + limit: Option, + offset: Option, + ) -> Result { + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); } - "specific" => { - // For now, specific keys with proof is not implemented - // Fall back to the non-proof version temporarily - let key_ids = specific_key_ids.ok_or_else(|| { - JsError::new("specific_key_ids is required for 'specific' key request type") - })?; - // Use direct gRPC request for specific keys - use dash_sdk::platform::proto::{ - get_identity_keys_request::{GetIdentityKeysRequestV0, Version}, - key_request_type::Request, - GetIdentityKeysRequest, KeyRequestType, SpecificKeys, - }; - use rs_dapi_client::{DapiRequest, RequestSettings}; - - let request = GetIdentityKeysRequest { - version: Some(Version::V0(GetIdentityKeysRequestV0 { - identity_id: id.to_vec(), - prove: true, - limit: limit.map(|l| l.into()), - offset: offset.map(|o| o.into()), - request_type: Some(KeyRequestType { - request: Some(Request::SpecificKeys(SpecificKeys { key_ids })), - }), - })), - }; - - let response = request - .execute(sdk.as_ref(), RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch specific identity keys: {}", e)) + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Handle different key request types + let (keys_result, metadata, proof) = match key_request_type { + "all" => { + // Use existing all keys implementation with proof + IdentityPublicKey::fetch_many_with_metadata_and_proof(self.as_ref(), id, None) + .await? + } + "specific" => { + // For now, specific keys with proof is not implemented + // Fall back to the non-proof version temporarily + let key_ids = specific_key_ids.ok_or_else(|| { + WasmSdkError::invalid_argument( + "specific_key_ids is required for 'specific' key request type", + ) })?; - // Process the response to extract keys - use dash_sdk::platform::proto::{ - get_identity_keys_response::Version as ResponseVersion, GetIdentityKeysResponse, - }; - use rs_dapi_client::IntoInner; - - let response: GetIdentityKeysResponse = response.into_inner(); - match response.version { - Some(ResponseVersion::V0(response_v0)) => { - if let Some(result) = response_v0.result { - match result { - dash_sdk::platform::proto::get_identity_keys_response::get_identity_keys_response_v0::Result::Keys(keys_response) => { - // Convert keys to the expected format - let mut key_map = IndexMap::new(); - for key_bytes in keys_response.keys_bytes { - use dash_sdk::dpp::serialization::PlatformDeserializable; - let key = dash_sdk::dpp::identity::identity_public_key::IdentityPublicKey::deserialize_from_bytes(key_bytes.as_slice())?; - key_map.insert(key.id(), Some(key)); + // Use direct gRPC request for specific keys + use dash_sdk::platform::proto::{ + get_identity_keys_request::{GetIdentityKeysRequestV0, Version}, + key_request_type::Request, + GetIdentityKeysRequest, KeyRequestType, SpecificKeys, + }; + use rs_dapi_client::{DapiRequest, RequestSettings}; + + let request = GetIdentityKeysRequest { + version: Some(Version::V0(GetIdentityKeysRequestV0 { + identity_id: id.to_vec(), + prove: true, + limit, + offset, + request_type: Some(KeyRequestType { + request: Some(Request::SpecificKeys(SpecificKeys { key_ids })), + }), + })), + }; + + let response = request + .execute(self.as_ref(), RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to fetch specific identity keys: {}", + e + )) + })?; + + // Process the response to extract keys + use dash_sdk::platform::proto::{ + get_identity_keys_response::Version as ResponseVersion, GetIdentityKeysResponse, + }; + use rs_dapi_client::IntoInner; + + let response: GetIdentityKeysResponse = response.into_inner(); + match response.version { + Some(ResponseVersion::V0(response_v0)) => { + if let Some(result) = response_v0.result { + match result { + dash_sdk::platform::proto::get_identity_keys_response::get_identity_keys_response_v0::Result::Keys(keys_response) => { + // Convert keys to the expected format + let mut key_map = IndexMap::new(); + for key_bytes in keys_response.keys_bytes { + use dash_sdk::dpp::serialization::PlatformDeserializable; + let key = dash_sdk::dpp::identity::identity_public_key::IdentityPublicKey::deserialize_from_bytes(key_bytes.as_slice()) + .map_err(|e| WasmSdkError::serialization(format!("Failed to deserialize identity public key: {}", e)))?; + key_map.insert(key.id(), Some(key)); + } + // Create dummy metadata and proof for consistency + let metadata = dash_sdk::platform::proto::ResponseMetadata { + height: 0, + core_chain_locked_height: 0, + epoch: 0, + time_ms: 0, + protocol_version: 0, + chain_id: "".to_string(), + }; + let proof = dash_sdk::platform::proto::Proof { + grovedb_proof: vec![], + quorum_hash: vec![], + signature: vec![], + round: 0, + block_id_hash: vec![], + quorum_type: 0, + }; + (key_map, metadata, proof) } - // Create dummy metadata and proof for consistency - let metadata = dash_sdk::platform::proto::ResponseMetadata { - height: 0, - core_chain_locked_height: 0, - epoch: 0, - time_ms: 0, - protocol_version: 0, - chain_id: "".to_string(), - }; - let proof = dash_sdk::platform::proto::Proof { - grovedb_proof: vec![], - quorum_hash: vec![], - signature: vec![], - round: 0, - block_id_hash: vec![], - quorum_type: 0, - }; - (key_map, metadata, proof) + _ => return Err(WasmSdkError::generic("Unexpected response format")), } - _ => return Err(JsError::new("Unexpected response format")), + } else { + return Err(WasmSdkError::not_found("No keys found in response")); } - } else { - return Err(JsError::new("No keys found in response")); } + _ => return Err(WasmSdkError::generic("Unexpected response version")), } - _ => return Err(JsError::new("Unexpected response version")), } - } - _ => { - return Err(JsError::new( - "Invalid key_request_type. Use 'all', 'specific', or 'search'", - )); - } - }; - - // Convert keys to response format - let mut keys: Vec = Vec::new(); - - // Apply offset and limit if provided - let start = offset.unwrap_or(0) as usize; - let end = if let Some(lim) = limit { - start + lim as usize - } else { - usize::MAX - }; - - for (idx, (key_id, key_opt)) in keys_result.into_iter().enumerate() { - if idx < start { - continue; - } - if idx >= end { - break; - } - - if let Some(key) = key_opt { - keys.push(IdentityKeyResponse { - key_id: key_id, - key_type: format!("{:?}", key.key_type()), - public_key_data: hex::encode(key.data().as_slice()), - purpose: format!("{:?}", key.purpose()), - security_level: format!("{:?}", key.security_level()), - read_only: key.read_only(), - disabled: key.disabled_at().is_some(), - }); - } - } - - let response = ProofMetadataResponse { - data: keys, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + _ => { + return Err(WasmSdkError::invalid_argument( + "Invalid key_request_type. Use 'all', 'specific', or 'search'", + )); + } + }; -#[wasm_bindgen] -pub async fn get_identity_balance_with_proof_info( - sdk: &WasmSdk, - id: &str, -) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityBalance; - - if id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + // Convert keys to response format + let mut keys: Vec = Vec::new(); - let identity_id = Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + // Apply offset and limit if provided + let start = offset.unwrap_or(0) as usize; + let end = if let Some(lim) = limit { + start + lim as usize + } else { + usize::MAX + }; - let (balance_result, metadata, proof) = - IdentityBalance::fetch_with_metadata_and_proof(sdk.as_ref(), identity_id, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity balance with proof: {}", - e - )) - })?; + for (idx, (key_id, key_opt)) in keys_result.into_iter().enumerate() { + if idx < start { + continue; + } + if idx >= end { + break; + } - if let Some(balance) = balance_result { - #[derive(Serialize)] - struct BalanceResponse { - balance: String, + if let Some(key) = key_opt { + keys.push(IdentityKeyResponse { + key_id, + key_type: format!("{:?}", key.key_type()), + public_key_data: hex::encode(key.data().as_slice()), + purpose: format!("{:?}", key.purpose()), + security_level: format!("{:?}", key.security_level()), + read_only: key.read_only(), + disabled: key.disabled_at().is_some(), + }); + } } - let data = BalanceResponse { - balance: balance.to_string(), - }; - let response = ProofMetadataResponse { - data, + data: keys, metadata: metadata.into(), proof: proof.into(), }; // Use json_compatible serializer let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new("Identity balance not found")) + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } -} -#[wasm_bindgen] -pub async fn get_identities_balances_with_proof_info( - sdk: &WasmSdk, - identity_ids: Vec, -) -> Result { - use drive_proof_verifier::types::IdentityBalance; - - // Convert string IDs to Identifiers - let identifiers: Vec = identity_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect::, _>>()?; + #[wasm_bindgen(js_name = "getIdentityBalanceWithProofInfo")] + pub async fn get_identity_balance_with_proof_info( + &self, + id: &str, + ) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityBalance; - let (balances_result, metadata, proof): (drive_proof_verifier::types::IdentityBalances, _, _) = - IdentityBalance::fetch_many_with_metadata_and_proof(sdk.as_ref(), identifiers.clone(), None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identities balances with proof: {}", - e - )) - })?; + if id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } - // Convert to response format - let responses: Vec = identifiers - .into_iter() - .filter_map(|id| { - balances_result.get(&id).and_then(|balance_opt| { - balance_opt.map(|balance| IdentityBalanceResponse { - identity_id: id.to_string( - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ), - balance: balance.to_string(), - }) - }) - }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let identity_id = Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; -#[wasm_bindgen] -pub async fn get_identity_balance_and_revision_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, -) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::IdentityBalanceAndRevision; - - if identity_id.is_empty() { - return Err(JsError::new("Identity ID is required")); - } + let (balance_result, metadata, proof) = + IdentityBalance::fetch_with_metadata_and_proof(self.as_ref(), identity_id, None) + .await?; - let id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; + if let Some(balance) = balance_result { + #[derive(Serialize)] + struct BalanceResponse { + balance: String, + } - let (result, metadata, proof) = - IdentityBalanceAndRevision::fetch_with_metadata_and_proof(sdk.as_ref(), id, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity balance and revision with proof: {}", - e - )) - })?; + let data = BalanceResponse { + balance: balance.to_string(), + }; - if let Some(balance_and_revision) = result { - let data = IdentityBalanceAndRevisionResponse { - balance: balance_and_revision.0.to_string(), - revision: balance_and_revision.1, - }; + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Err(WasmSdkError::not_found("Identity balance not found")) + } + } + + #[wasm_bindgen(js_name = "getIdentitiesBalancesWithProofInfo")] + pub async fn get_identities_balances_with_proof_info( + &self, + identity_ids: Vec, + ) -> Result { + use drive_proof_verifier::types::IdentityBalance; + + // Convert string IDs to Identifiers + let identifiers: Vec = identity_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect::, _>>() + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let (balances_result, metadata, proof): ( + drive_proof_verifier::types::IdentityBalances, + _, + _, + ) = IdentityBalance::fetch_many_with_metadata_and_proof( + self.as_ref(), + identifiers.clone(), + None, + ) + .await?; + + // Convert to response format + let responses: Vec = identifiers + .into_iter() + .filter_map(|id| { + balances_result.get(&id).and_then(|balance_opt| { + balance_opt.map(|balance| IdentityBalanceResponse { + identity_id: id.to_string( + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ), + balance: balance.to_string(), + }) + }) + }) + .collect(); let response = ProofMetadataResponse { - data, + data: responses, metadata: metadata.into(), proof: proof.into(), }; // Use json_compatible serializer let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new("Identity balance and revision not found")) + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } -} -#[wasm_bindgen] -pub async fn get_identity_by_public_key_hash_with_proof_info( - sdk: &WasmSdk, - public_key_hash: &str, -) -> Result { - use dash_sdk::platform::types::identity::PublicKeyHash; - - // Parse the hex-encoded public key hash - let hash_bytes = hex::decode(public_key_hash) - .map_err(|e| JsError::new(&format!("Invalid public key hash hex: {}", e)))?; - - if hash_bytes.len() != 20 { - return Err(JsError::new( - "Public key hash must be 20 bytes (40 hex characters)", - )); - } + #[wasm_bindgen(js_name = "getIdentityBalanceAndRevisionWithProofInfo")] + pub async fn get_identity_balance_and_revision_with_proof_info( + &self, + identity_id: &str, + ) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::IdentityBalanceAndRevision; - let mut hash_array = [0u8; 20]; - hash_array.copy_from_slice(&hash_bytes); + if identity_id.is_empty() { + return Err(WasmSdkError::invalid_argument("Identity ID is required")); + } - let (result, metadata, proof) = - Identity::fetch_with_metadata_and_proof(sdk.as_ref(), PublicKeyHash(hash_array), None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity by public key hash with proof: {}", - e - )) - })?; + let id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - match result { - Some(identity) => { - let identity_json = IdentityWasm::from(identity).to_json().map_err(|e| { - JsError::new(&format!("Failed to convert identity to JSON: {:?}", e)) - })?; - let identity_value: serde_json::Value = serde_wasm_bindgen::from_value(identity_json)?; + let (result, metadata, proof) = + IdentityBalanceAndRevision::fetch_with_metadata_and_proof(self.as_ref(), id, None) + .await?; + + if let Some(balance_and_revision) = result { + let data = IdentityBalanceAndRevisionResponse { + balance: balance_and_revision.0.to_string(), + revision: balance_and_revision.1, + }; let response = ProofMetadataResponse { - data: identity_value, + data, metadata: metadata.into(), proof: proof.into(), }; // Use json_compatible serializer let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Err(WasmSdkError::not_found( + "Identity balance and revision not found", + )) } - None => Err(JsError::new("Identity not found for public key hash")), } -} -#[wasm_bindgen] -pub async fn get_identity_by_non_unique_public_key_hash_with_proof_info( - sdk: &WasmSdk, - public_key_hash: &str, - start_after: Option, -) -> Result { - // Parse the hex-encoded public key hash - let hash_bytes = hex::decode(public_key_hash) - .map_err(|e| JsError::new(&format!("Invalid public key hash hex: {}", e)))?; - - if hash_bytes.len() != 20 { - return Err(JsError::new( - "Public key hash must be 20 bytes (40 hex characters)", - )); + #[wasm_bindgen(js_name = "getIdentityByPublicKeyHashWithProofInfo")] + pub async fn get_identity_by_public_key_hash_with_proof_info( + &self, + public_key_hash: &str, + ) -> Result { + use dash_sdk::platform::types::identity::PublicKeyHash; + + // Parse the hex-encoded public key hash + let hash_bytes = hex::decode(public_key_hash).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid public key hash hex: {}", e)) + })?; + + if hash_bytes.len() != 20 { + return Err(WasmSdkError::invalid_argument( + "Public key hash must be 20 bytes (40 hex characters)", + )); + } + + let mut hash_array = [0u8; 20]; + hash_array.copy_from_slice(&hash_bytes); + + let (result, metadata, proof) = + Identity::fetch_with_metadata_and_proof(self.as_ref(), PublicKeyHash(hash_array), None) + .await?; + + match result { + Some(identity) => { + let identity_json = IdentityWasm::from(identity).to_json().map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert identity to JSON: {:?}", + e + )) + })?; + let identity_value: serde_json::Value = + serde_wasm_bindgen::from_value(identity_json).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to convert to JSON value: {}", + e + )) + })?; + + let response = ProofMetadataResponse { + data: identity_value, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + None => Err(WasmSdkError::not_found( + "Identity not found for public key hash", + )), + } } - let mut hash_array = [0u8; 20]; - hash_array.copy_from_slice(&hash_bytes); + #[wasm_bindgen(js_name = "getIdentityByNonUniquePublicKeyHashWithProofInfo")] + pub async fn get_identity_by_non_unique_public_key_hash_with_proof_info( + &self, + public_key_hash: &str, + start_after: Option, + ) -> Result { + // Parse the hex-encoded public key hash + let hash_bytes = hex::decode(public_key_hash).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid public key hash hex: {}", e)) + })?; - // Convert start_after if provided - let start_id = if let Some(start) = start_after { - Some(Identifier::from_string( - &start, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?) - } else { - None - }; + if hash_bytes.len() != 20 { + return Err(WasmSdkError::invalid_argument( + "Public key hash must be 20 bytes (40 hex characters)", + )); + } - use dash_sdk::platform::types::identity::NonUniquePublicKeyHashQuery; + let mut hash_array = [0u8; 20]; + hash_array.copy_from_slice(&hash_bytes); - let query = NonUniquePublicKeyHashQuery { - key_hash: hash_array, - after: start_id.map(|id| *id.as_bytes()), - }; + // Convert start_after if provided + let start_id = if let Some(start) = start_after { + Some( + Identifier::from_string( + &start, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start_after identity ID: {}", + e + )) + })?, + ) + } else { + None + }; - // Fetch identity by non-unique public key hash with proof - let (identity, metadata, proof) = - Identity::fetch_with_metadata_and_proof(sdk.as_ref(), query, None) - .await + use dash_sdk::platform::types::identity::NonUniquePublicKeyHashQuery; + + let query = NonUniquePublicKeyHashQuery { + key_hash: hash_array, + after: start_id.map(|id| *id.as_bytes()), + }; + + // Fetch identity by non-unique public key hash with proof + let (identity, metadata, proof) = + Identity::fetch_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Return array with single identity if found + let results = if let Some(id) = identity { + vec![id] + } else { + vec![] + }; + + // Convert results to JSON + let identities_json: Vec = results + .into_iter() + .map(|identity| { + let identity_wasm: IdentityWasm = identity.into(); + let json = identity_wasm.to_json().map_err(|_| { + serde_wasm_bindgen::Error::new("Failed to convert identity to JSON") + })?; + serde_wasm_bindgen::from_value(json) + }) + .collect::, _>>() .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identities by non-unique public key hash with proof: {}", - e - )) + WasmSdkError::serialization(format!("Failed to convert identity to JSON: {}", e)) })?; - // Return array with single identity if found - let results = if let Some(id) = identity { - vec![id] - } else { - vec![] - }; - - // Convert results to JSON - let identities_json: Vec = results - .into_iter() - .map(|identity| { - let identity_wasm: IdentityWasm = identity.into(); - let json = identity_wasm.to_json().map_err(|_| { - serde_wasm_bindgen::Error::new("Failed to convert identity to JSON") - })?; - serde_wasm_bindgen::from_value(json) - }) - .collect::, _>>()?; - - let response = ProofMetadataResponse { - data: identities_json, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let response = ProofMetadataResponse { + data: identities_json, + metadata: metadata.into(), + proof: proof.into(), + }; -#[wasm_bindgen] -pub async fn get_identities_contract_keys_with_proof_info( - sdk: &WasmSdk, - identities_ids: Vec, - contract_id: &str, - purposes: Option>, -) -> Result { - use dash_sdk::dpp::identity::Purpose; - - // Convert string IDs to Identifiers - let _identity_ids: Vec = identities_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect::, _>>()?; - - // Contract ID is not used in the individual key queries, but we validate it - let _contract_identifier = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Convert purposes if provided - let purposes_opt = purposes.map(|p| { - p.into_iter() - .filter_map(|purpose_int| match purpose_int { - 0 => Some(Purpose::AUTHENTICATION as u32), - 1 => Some(Purpose::ENCRYPTION as u32), - 2 => Some(Purpose::DECRYPTION as u32), - 3 => Some(Purpose::TRANSFER as u32), - 4 => Some(Purpose::SYSTEM as u32), - 5 => Some(Purpose::VOTING as u32), - _ => None, - }) - .collect::>() - }); + } - // For now, we'll implement this by fetching keys for each identity individually with proof - // The SDK doesn't fully expose the batch query with proof yet - let mut all_responses: Vec = Vec::new(); - let mut combined_metadata: Option = None; - let mut combined_proof: Option = None; + #[wasm_bindgen(js_name = "getIdentitiesContractKeysWithProofInfo")] + pub async fn get_identities_contract_keys_with_proof_info( + &self, + identities_ids: Vec, + contract_id: &str, + purposes: Option>, + ) -> Result { + use dash_sdk::dpp::identity::Purpose; + + // Convert string IDs to Identifiers + let _identity_ids: Vec = identities_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect::, _>>() + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; - for identity_id_str in identities_ids { - let identity_id = Identifier::from_string( - &identity_id_str, + // Contract ID is not used in the individual key queries, but we validate it + let _contract_identifier = Identifier::from_string( + contract_id, dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Get keys for this identity using the regular identity keys query with proof - let (keys_result, metadata, proof) = - IdentityPublicKey::fetch_many_with_metadata_and_proof(sdk.as_ref(), identity_id, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch keys for identity {} with proof: {}", - identity_id_str, e - )) - })?; + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Convert purposes if provided + let purposes_opt = purposes.map(|p| { + p.into_iter() + .filter_map(|purpose_int| match purpose_int { + 0 => Some(Purpose::AUTHENTICATION as u32), + 1 => Some(Purpose::ENCRYPTION as u32), + 2 => Some(Purpose::DECRYPTION as u32), + 3 => Some(Purpose::TRANSFER as u32), + 4 => Some(Purpose::SYSTEM as u32), + 5 => Some(Purpose::VOTING as u32), + _ => None, + }) + .collect::>() + }); + + // For now, we'll implement this by fetching keys for each identity individually with proof + // The SDK doesn't fully expose the batch query with proof yet + let mut all_responses: Vec = Vec::new(); + let mut combined_metadata: Option = None; + let mut combined_proof: Option = None; + + for identity_id_str in identities_ids { + let identity_id = Identifier::from_string( + &identity_id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid identity ID '{}': {}", + identity_id_str, e + )) + })?; - // Store first metadata and proof - if combined_metadata.is_none() { - combined_metadata = Some(metadata.into()); - combined_proof = Some(proof.into()); - } + // Get keys for this identity using the regular identity keys query with proof + let (keys_result, metadata, proof) = + IdentityPublicKey::fetch_many_with_metadata_and_proof( + self.as_ref(), + identity_id, + None, + ) + .await?; + + // Store first metadata and proof + if combined_metadata.is_none() { + combined_metadata = Some(metadata.into()); + combined_proof = Some(proof.into()); + } - let mut identity_keys = Vec::new(); + let mut identity_keys = Vec::new(); - // Filter keys by purpose if specified - for (key_id, key_opt) in keys_result { - if let Some(key) = key_opt { - // Check if this key matches the requested purposes - if let Some(ref purposes) = purposes_opt { - if !purposes.contains(&(key.purpose() as u32)) { - continue; + // Filter keys by purpose if specified + for (key_id, key_opt) in keys_result { + if let Some(key) = key_opt { + // Check if this key matches the requested purposes + if let Some(ref purposes) = purposes_opt { + if !purposes.contains(&(key.purpose() as u32)) { + continue; + } } + + let key_response = IdentityKeyResponse { + key_id, + key_type: format!("{:?}", key.key_type()), + public_key_data: hex::encode(key.data().as_slice()), + purpose: format!("{:?}", key.purpose()), + security_level: format!("{:?}", key.security_level()), + read_only: key.read_only(), + disabled: key.disabled_at().is_some(), + }; + identity_keys.push(key_response); } + } - let key_response = IdentityKeyResponse { - key_id: key_id, - key_type: format!("{:?}", key.key_type()), - public_key_data: hex::encode(key.data().as_slice()), - purpose: format!("{:?}", key.purpose()), - security_level: format!("{:?}", key.security_level()), - read_only: key.read_only(), - disabled: key.disabled_at().is_some(), - }; - identity_keys.push(key_response); + if !identity_keys.is_empty() { + all_responses.push(IdentityContractKeysResponse { + identity_id: identity_id_str, + keys: identity_keys, + }); } } - if !identity_keys.is_empty() { - all_responses.push(IdentityContractKeysResponse { - identity_id: identity_id_str, - keys: identity_keys, - }); - } + let response = ProofMetadataResponse { + data: all_responses, + metadata: combined_metadata.unwrap_or_else(|| ResponseMetadata { + height: 0, + core_chain_locked_height: 0, + epoch: 0, + time_ms: 0, + protocol_version: 0, + chain_id: String::new(), + }), + proof: combined_proof.unwrap_or_else(|| ProofInfo { + grovedb_proof: String::new(), + quorum_hash: String::new(), + signature: String::new(), + round: 0, + block_id_hash: String::new(), + quorum_type: 0, + }), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - let response = ProofMetadataResponse { - data: all_responses, - metadata: combined_metadata.unwrap_or_else(|| ResponseMetadata { - height: 0, - core_chain_locked_height: 0, - epoch: 0, - time_ms: 0, - protocol_version: 0, - chain_id: String::new(), - }), - proof: combined_proof.unwrap_or_else(|| ProofInfo { - grovedb_proof: String::new(), - quorum_hash: String::new(), - signature: String::new(), - round: 0, - block_id_hash: String::new(), - quorum_type: 0, - }), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getIdentityTokenBalancesWithProofInfo")] + pub async fn get_identity_token_balances_with_proof_info( + &self, + identity_id: &str, + token_ids: Vec, + ) -> Result { + use dash_sdk::dpp::balances::credits::TokenAmount; + use dash_sdk::platform::tokens::identity_token_balances::IdentityTokenBalancesQuery; -#[wasm_bindgen] -pub async fn get_identity_token_balances_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - token_ids: Vec, -) -> Result { - use dash_sdk::dpp::balances::credits::TokenAmount; - use dash_sdk::platform::tokens::identity_token_balances::IdentityTokenBalancesQuery; - - let identity_id = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Convert token IDs to Identifiers - let token_identifiers: Vec = token_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect::, _>>()?; - - let query = IdentityTokenBalancesQuery { - identity_id, - token_ids: token_identifiers.clone(), - }; - - // Use FetchMany trait to fetch token balances with proof - let (balances, metadata, proof): ( - dash_sdk::query_types::identity_token_balance::IdentityTokenBalances, - _, - _, - ) = TokenAmount::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity token balances with proof: {}", - e - )) - })?; + let identity_id = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Convert token IDs to Identifiers + let token_identifiers: Vec = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect::, _>>() + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; - // Convert to response format - let responses: Vec = token_identifiers - .into_iter() - .zip(token_ids.into_iter()) - .filter_map(|(token_id, token_id_str)| { - balances.get(&token_id).and_then(|balance_opt| { - balance_opt.map(|balance| TokenBalanceResponse { - token_id: token_id_str, - balance: balance.to_string(), + let query = IdentityTokenBalancesQuery { + identity_id, + token_ids: token_identifiers.clone(), + }; + + // Use FetchMany trait to fetch token balances with proof + let (balances, metadata, proof): ( + dash_sdk::query_types::identity_token_balance::IdentityTokenBalances, + _, + _, + ) = TokenAmount::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Convert to response format + let responses: Vec = token_identifiers + .into_iter() + .zip(token_ids.into_iter()) + .filter_map(|(token_id, token_id_str)| { + balances.get(&token_id).and_then(|balance_opt| { + balance_opt.map(|balance| TokenBalanceResponse { + token_id: token_id_str, + balance: balance.to_string(), + }) }) }) + .collect(); + + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + } } diff --git a/packages/wasm-sdk/src/queries/mod.rs b/packages/wasm-sdk/src/queries/mod.rs index 5b75050d0be..e1c7b3141a1 100644 --- a/packages/wasm-sdk/src/queries/mod.rs +++ b/packages/wasm-sdk/src/queries/mod.rs @@ -10,16 +10,7 @@ pub mod token; pub mod voting; // Re-export all query functions for easy access -pub use data_contract::*; -pub use document::*; -pub use dpns::*; -pub use epoch::*; pub use group::*; -pub use identity::*; -pub use protocol::*; -pub use system::*; -pub use token::*; -pub use voting::*; use serde::{Deserialize, Serialize}; diff --git a/packages/wasm-sdk/src/queries/protocol.rs b/packages/wasm-sdk/src/queries/protocol.rs index 0cfbd6eb988..9c91ac6c4b7 100644 --- a/packages/wasm-sdk/src/queries/protocol.rs +++ b/packages/wasm-sdk/src/queries/protocol.rs @@ -1,7 +1,8 @@ +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -21,167 +22,158 @@ struct ProtocolVersionUpgradeVoteStatus { } #[wasm_bindgen] -pub async fn get_protocol_version_upgrade_state(sdk: &WasmSdk) -> Result { - use dash_sdk::platform::FetchMany; - use drive_proof_verifier::types::ProtocolVersionVoteCount; - - let upgrade_result: drive_proof_verifier::types::ProtocolVersionUpgrades = - ProtocolVersionVoteCount::fetch_many(sdk.as_ref(), ()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch protocol version upgrade state: {}", - e - )) - })?; - - // Get the current protocol version from the SDK - let current_version = sdk.version(); - - // Find the next version with votes - let mut next_version = None; - let mut activation_height = None; - let mut vote_count = None; - let mut threshold_reached = false; - - // The result is an IndexMap> where u32 is version and Option is activation height - for (version, height_opt) in upgrade_result.iter() { - if *version > current_version { - next_version = Some(*version); - activation_height = *height_opt; - // TODO: Get actual vote count and threshold from platform - vote_count = None; - threshold_reached = height_opt.is_some(); - break; +impl WasmSdk { + #[wasm_bindgen(js_name = "getProtocolVersionUpgradeState")] + pub async fn get_protocol_version_upgrade_state(&self) -> Result { + use dash_sdk::platform::FetchMany; + use drive_proof_verifier::types::ProtocolVersionVoteCount; + + let upgrade_result: drive_proof_verifier::types::ProtocolVersionUpgrades = + ProtocolVersionVoteCount::fetch_many(self.as_ref(), ()).await?; + + // Get the current protocol version from the SDK + let current_version = self.version(); + + // Find the next version with votes + let mut next_version = None; + let mut activation_height = None; + let mut vote_count = None; + let mut threshold_reached = false; + + // The result is an IndexMap> where u32 is version and Option is activation height + for (version, height_opt) in upgrade_result.iter() { + if *version > current_version { + next_version = Some(*version); + activation_height = *height_opt; + // TODO: Get actual vote count and threshold from platform + vote_count = None; + threshold_reached = height_opt.is_some(); + break; + } } - } - - let state = ProtocolVersionUpgradeState { - current_protocol_version: current_version, - next_protocol_version: next_version, - activation_height, - vote_count, - threshold_reached, - }; - serde_wasm_bindgen::to_value(&state) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let state = ProtocolVersionUpgradeState { + current_protocol_version: current_version, + next_protocol_version: next_version, + activation_height, + vote_count, + threshold_reached, + }; -#[wasm_bindgen] -pub async fn get_protocol_version_upgrade_vote_status( - sdk: &WasmSdk, - start_pro_tx_hash: &str, - count: u32, -) -> Result { - use dash_sdk::dpp::dashcore::ProTxHash; - use dash_sdk::platform::types::version_votes::MasternodeProtocolVoteEx; - use drive_proof_verifier::types::MasternodeProtocolVote; - use std::str::FromStr; - - // Parse the ProTxHash - let start_hash = if start_pro_tx_hash.is_empty() { - None - } else { - Some( - ProTxHash::from_str(start_pro_tx_hash) - .map_err(|e| JsError::new(&format!("Invalid ProTxHash: {}", e)))?, - ) - }; - - let votes_result = MasternodeProtocolVote::fetch_votes(sdk.as_ref(), start_hash, Some(count)) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch protocol version votes: {}", e)))?; - - // Convert to our response format - let votes: Vec = votes_result - .into_iter() - .filter_map(|(pro_tx_hash, vote_opt)| { - // vote_opt is Option - vote_opt.map(|vote| ProtocolVersionUpgradeVoteStatus { - pro_tx_hash: pro_tx_hash.to_string(), - version: vote.voted_version, - }) + serde_wasm_bindgen::to_value(&state).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); + } - serde_wasm_bindgen::to_value(&votes) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getProtocolVersionUpgradeVoteStatus")] + pub async fn get_protocol_version_upgrade_vote_status( + &self, + start_pro_tx_hash: &str, + count: u32, + ) -> Result { + use dash_sdk::dpp::dashcore::ProTxHash; + use dash_sdk::platform::types::version_votes::MasternodeProtocolVoteEx; + use drive_proof_verifier::types::MasternodeProtocolVote; + use std::str::FromStr; + + // Parse the ProTxHash + let start_hash = + if start_pro_tx_hash.is_empty() { + None + } else { + Some(ProTxHash::from_str(start_pro_tx_hash).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid ProTxHash: {}", e)) + })?) + }; + + let votes_result = + MasternodeProtocolVote::fetch_votes(self.as_ref(), start_hash, Some(count)).await?; + + // Convert to our response format + let votes: Vec = votes_result + .into_iter() + .filter_map(|(pro_tx_hash, vote_opt)| { + // vote_opt is Option + vote_opt.map(|vote| ProtocolVersionUpgradeVoteStatus { + pro_tx_hash: pro_tx_hash.to_string(), + version: vote.voted_version, + }) + }) + .collect(); -// Proof versions for protocol queries + serde_wasm_bindgen::to_value(&votes).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_protocol_version_upgrade_state_with_proof_info( - sdk: &WasmSdk, -) -> Result { - use crate::queries::ProofMetadataResponse; - use dash_sdk::platform::FetchMany; - use drive_proof_verifier::types::ProtocolVersionVoteCount; - - let (upgrade_result, metadata, proof): ( - drive_proof_verifier::types::ProtocolVersionUpgrades, - _, - _, - ) = ProtocolVersionVoteCount::fetch_many_with_metadata_and_proof(sdk.as_ref(), (), None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch protocol version upgrade state with proof: {}", - e - )) - })?; - - // Get the current protocol version from the SDK - let current_version = sdk.version(); - - // Find the next version with votes - let mut next_version = None; - let mut activation_height = None; - let mut vote_count = None; - let mut threshold_reached = false; - - for (version, height_opt) in upgrade_result.iter() { - if *version > current_version { - next_version = Some(*version); - activation_height = *height_opt; - vote_count = None; - threshold_reached = height_opt.is_some(); - break; + // Proof versions for protocol queries + + #[wasm_bindgen(js_name = "getProtocolVersionUpgradeStateWithProofInfo")] + pub async fn get_protocol_version_upgrade_state_with_proof_info( + &self, + ) -> Result { + use crate::queries::ProofMetadataResponse; + use dash_sdk::platform::FetchMany; + use drive_proof_verifier::types::ProtocolVersionVoteCount; + + let (upgrade_result, metadata, proof): ( + drive_proof_verifier::types::ProtocolVersionUpgrades, + _, + _, + ) = ProtocolVersionVoteCount::fetch_many_with_metadata_and_proof(self.as_ref(), (), None) + .await?; + + // Get the current protocol version from the SDK + let current_version = self.version(); + + // Find the next version with votes + let mut next_version = None; + let mut activation_height = None; + let mut vote_count = None; + let mut threshold_reached = false; + + for (version, height_opt) in upgrade_result.iter() { + if *version > current_version { + next_version = Some(*version); + activation_height = *height_opt; + vote_count = None; + threshold_reached = height_opt.is_some(); + break; + } } - } - let state = ProtocolVersionUpgradeState { - current_protocol_version: current_version, - next_protocol_version: next_version, - activation_height, - vote_count, - threshold_reached, - }; - - let response = ProofMetadataResponse { - data: state, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let state = ProtocolVersionUpgradeState { + current_protocol_version: current_version, + next_protocol_version: next_version, + activation_height, + vote_count, + threshold_reached, + }; + + let response = ProofMetadataResponse { + data: state, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_protocol_version_upgrade_vote_status_with_proof_info( - sdk: &WasmSdk, - start_pro_tx_hash: &str, - count: u32, -) -> Result { - // TODO: Implement once a proper fetch_many_with_metadata_and_proof method is available for MasternodeProtocolVote - // The fetch_votes method has different parameters than fetch_many - let _ = (sdk, start_pro_tx_hash, count); // Parameters will be used when implemented - Err(JsError::new( - "get_protocol_version_upgrade_vote_status_with_proof_info is not yet implemented", - )) + #[wasm_bindgen(js_name = "getProtocolVersionUpgradeVoteStatusWithProofInfo")] + pub async fn get_protocol_version_upgrade_vote_status_with_proof_info( + &self, + start_pro_tx_hash: &str, + count: u32, + ) -> Result { + // TODO: Implement once a proper fetch_many_with_metadata_and_proof method is available for MasternodeProtocolVote + // The fetch_votes method has different parameters than fetch_many + let _ = (self, start_pro_tx_hash, count); // Parameters will be used when implemented + Err(WasmSdkError::generic( + "get_protocol_version_upgrade_vote_status_with_proof_info is not yet implemented", + )) + } } diff --git a/packages/wasm-sdk/src/queries/system.rs b/packages/wasm-sdk/src/queries/system.rs index 88effc6fbb5..be78fdd0621 100644 --- a/packages/wasm-sdk/src/queries/system.rs +++ b/packages/wasm-sdk/src/queries/system.rs @@ -1,8 +1,9 @@ +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use dash_sdk::dpp::core_types::validator_set::v0::ValidatorSetV0Getters; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; // Response structures for the gRPC getStatus endpoint #[derive(Serialize, Deserialize, Debug)] @@ -149,686 +150,677 @@ struct PathElement { } #[wasm_bindgen] -pub async fn get_status(sdk: &WasmSdk) -> Result { - use dapi_grpc::platform::v0::get_status_request::{GetStatusRequestV0, Version}; - use dapi_grpc::platform::v0::GetStatusRequest; - use dash_sdk::RequestSettings; - use rs_dapi_client::DapiRequestExecutor; - - // Create the gRPC request - let request = GetStatusRequest { - version: Some(Version::V0(GetStatusRequestV0 {})), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| JsError::new(&format!("Failed to get status: {}", e)))?; - - // Parse the response - use dapi_grpc::platform::v0::get_status_response::Version as ResponseVersion; - - let v0_response = match response.inner.version { - Some(ResponseVersion::V0(v0)) => v0, - None => return Err(JsError::new("No version in GetStatus response")), - }; - - // Map the response to our StatusResponse structure - let status = StatusResponse { - version: StatusVersion { - software: StatusSoftware { - dapi: v0_response - .version - .as_ref() - .map(|v| v.software.as_ref()) - .flatten() - .map(|s| s.dapi.clone()) - .unwrap_or_else(|| "unknown".to_string()), - drive: v0_response - .version - .as_ref() - .and_then(|v| v.software.as_ref()) - .and_then(|s| s.drive.clone()), - tenderdash: v0_response - .version - .as_ref() - .and_then(|v| v.software.as_ref()) - .and_then(|s| s.tenderdash.clone()), - }, - protocol: StatusProtocol { - tenderdash: StatusTenderdashProtocol { - p2p: v0_response - .version - .as_ref() - .and_then(|v| v.protocol.as_ref()) - .and_then(|p| p.tenderdash.as_ref()) - .map(|t| t.p2p) - .unwrap_or(0), - block: v0_response +impl WasmSdk { + #[wasm_bindgen(js_name = "getStatus")] + pub async fn get_status(&self) -> Result { + use dapi_grpc::platform::v0::get_status_request::{GetStatusRequestV0, Version}; + use dapi_grpc::platform::v0::GetStatusRequest; + use dash_sdk::RequestSettings; + use rs_dapi_client::DapiRequestExecutor; + + // Create the gRPC request + let request = GetStatusRequest { + version: Some(Version::V0(GetStatusRequestV0 {})), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| WasmSdkError::generic(format!("Failed to get status: {}", e)))?; + + // Parse the response + use dapi_grpc::platform::v0::get_status_response::Version as ResponseVersion; + + let v0_response = match response.inner.version { + Some(ResponseVersion::V0(v0)) => v0, + None => return Err(WasmSdkError::generic("No version in GetStatus response")), + }; + + // Map the response to our StatusResponse structure + let status = StatusResponse { + version: StatusVersion { + software: StatusSoftware { + dapi: v0_response .version .as_ref() - .and_then(|v| v.protocol.as_ref()) - .and_then(|p| p.tenderdash.as_ref()) - .map(|t| t.block) - .unwrap_or(0), - }, - drive: StatusDriveProtocol { - latest: v0_response + .and_then(|v| v.software.as_ref()) + .map(|s| s.dapi.clone()) + .unwrap_or_else(|| "unknown".to_string()), + drive: v0_response .version .as_ref() - .and_then(|v| v.protocol.as_ref()) - .and_then(|p| p.drive.as_ref()) - .map(|d| d.latest) - .unwrap_or(0), - current: v0_response + .and_then(|v| v.software.as_ref()) + .and_then(|s| s.drive.clone()), + tenderdash: v0_response .version .as_ref() - .and_then(|v| v.protocol.as_ref()) - .and_then(|p| p.drive.as_ref()) - .map(|d| d.current) - .unwrap_or(0), + .and_then(|v| v.software.as_ref()) + .and_then(|s| s.tenderdash.clone()), + }, + protocol: StatusProtocol { + tenderdash: StatusTenderdashProtocol { + p2p: v0_response + .version + .as_ref() + .and_then(|v| v.protocol.as_ref()) + .and_then(|p| p.tenderdash.as_ref()) + .map(|t| t.p2p) + .unwrap_or(0), + block: v0_response + .version + .as_ref() + .and_then(|v| v.protocol.as_ref()) + .and_then(|p| p.tenderdash.as_ref()) + .map(|t| t.block) + .unwrap_or(0), + }, + drive: StatusDriveProtocol { + latest: v0_response + .version + .as_ref() + .and_then(|v| v.protocol.as_ref()) + .and_then(|p| p.drive.as_ref()) + .map(|d| d.latest) + .unwrap_or(0), + current: v0_response + .version + .as_ref() + .and_then(|v| v.protocol.as_ref()) + .and_then(|p| p.drive.as_ref()) + .map(|d| d.current) + .unwrap_or(0), + }, }, }, - }, - node: StatusNode { - id: v0_response - .node - .as_ref() - .map(|n| hex::encode(&n.id)) - .unwrap_or_else(|| "unknown".to_string()), - pro_tx_hash: v0_response - .node - .as_ref() - .and_then(|n| n.pro_tx_hash.as_ref()) - .map(|hash| hex::encode(hash)), - }, - chain: StatusChain { - catching_up: v0_response - .chain - .as_ref() - .map(|c| c.catching_up) - .unwrap_or(false), - latest_block_hash: v0_response - .chain - .as_ref() - .map(|c| hex::encode(&c.latest_block_hash)) - .unwrap_or_else(|| "unknown".to_string()), - latest_app_hash: v0_response - .chain - .as_ref() - .map(|c| hex::encode(&c.latest_app_hash)) - .unwrap_or_else(|| "unknown".to_string()), - latest_block_height: v0_response - .chain - .as_ref() - .map(|c| c.latest_block_height.to_string()) - .unwrap_or_else(|| "0".to_string()), - earliest_block_hash: v0_response - .chain - .as_ref() - .map(|c| hex::encode(&c.earliest_block_hash)) - .unwrap_or_else(|| "unknown".to_string()), - earliest_app_hash: v0_response - .chain - .as_ref() - .map(|c| hex::encode(&c.earliest_app_hash)) - .unwrap_or_else(|| "unknown".to_string()), - earliest_block_height: v0_response - .chain - .as_ref() - .map(|c| c.earliest_block_height.to_string()) - .unwrap_or_else(|| "0".to_string()), - max_peer_block_height: v0_response - .chain - .as_ref() - .map(|c| c.max_peer_block_height.to_string()) - .unwrap_or_else(|| "0".to_string()), - core_chain_locked_height: v0_response - .chain - .as_ref() - .and_then(|c| c.core_chain_locked_height), - }, - network: StatusNetwork { - chain_id: v0_response - .network - .as_ref() - .map(|n| n.chain_id.clone()) - .unwrap_or_else(|| "unknown".to_string()), - peers_count: v0_response - .network - .as_ref() - .map(|n| n.peers_count) - .unwrap_or(0), - listening: v0_response - .network - .as_ref() - .map(|n| n.listening) - .unwrap_or(false), - }, - state_sync: StatusStateSync { - total_synced_time: v0_response - .state_sync - .as_ref() - .map(|s| s.total_synced_time.to_string()) - .unwrap_or_else(|| "0".to_string()), - remaining_time: v0_response - .state_sync - .as_ref() - .map(|s| s.remaining_time.to_string()) - .unwrap_or_else(|| "0".to_string()), - total_snapshots: v0_response - .state_sync - .as_ref() - .map(|s| s.total_snapshots) - .unwrap_or(0), - chunk_process_avg_time: v0_response - .state_sync - .as_ref() - .map(|s| s.chunk_process_avg_time.to_string()) - .unwrap_or_else(|| "0".to_string()), - snapshot_height: v0_response - .state_sync - .as_ref() - .map(|s| s.snapshot_height.to_string()) - .unwrap_or_else(|| "0".to_string()), - snapshot_chunks_count: v0_response - .state_sync - .as_ref() - .map(|s| s.snapshot_chunks_count.to_string()) - .unwrap_or_else(|| "0".to_string()), - backfilled_blocks: v0_response - .state_sync - .as_ref() - .map(|s| s.backfilled_blocks.to_string()) - .unwrap_or_else(|| "0".to_string()), - backfill_blocks_total: v0_response - .state_sync - .as_ref() - .map(|s| s.backfill_blocks_total.to_string()) - .unwrap_or_else(|| "0".to_string()), - }, - time: StatusTime { - local: v0_response - .time - .as_ref() - .map(|t| t.local.to_string()) - .unwrap_or_else(|| "0".to_string()), - block: v0_response - .time - .as_ref() - .and_then(|t| t.block) - .map(|b| b.to_string()), - genesis: v0_response - .time - .as_ref() - .and_then(|t| t.genesis) - .map(|g| g.to_string()), - epoch: v0_response.time.as_ref().and_then(|t| t.epoch), - }, - }; - - serde_wasm_bindgen::to_value(&status) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + node: StatusNode { + id: v0_response + .node + .as_ref() + .map(|n| hex::encode(&n.id)) + .unwrap_or_else(|| "unknown".to_string()), + pro_tx_hash: v0_response + .node + .as_ref() + .and_then(|n| n.pro_tx_hash.as_ref()) + .map(hex::encode), + }, + chain: StatusChain { + catching_up: v0_response + .chain + .as_ref() + .map(|c| c.catching_up) + .unwrap_or(false), + latest_block_hash: v0_response + .chain + .as_ref() + .map(|c| hex::encode(&c.latest_block_hash)) + .unwrap_or_else(|| "unknown".to_string()), + latest_app_hash: v0_response + .chain + .as_ref() + .map(|c| hex::encode(&c.latest_app_hash)) + .unwrap_or_else(|| "unknown".to_string()), + latest_block_height: v0_response + .chain + .as_ref() + .map(|c| c.latest_block_height.to_string()) + .unwrap_or_else(|| "0".to_string()), + earliest_block_hash: v0_response + .chain + .as_ref() + .map(|c| hex::encode(&c.earliest_block_hash)) + .unwrap_or_else(|| "unknown".to_string()), + earliest_app_hash: v0_response + .chain + .as_ref() + .map(|c| hex::encode(&c.earliest_app_hash)) + .unwrap_or_else(|| "unknown".to_string()), + earliest_block_height: v0_response + .chain + .as_ref() + .map(|c| c.earliest_block_height.to_string()) + .unwrap_or_else(|| "0".to_string()), + max_peer_block_height: v0_response + .chain + .as_ref() + .map(|c| c.max_peer_block_height.to_string()) + .unwrap_or_else(|| "0".to_string()), + core_chain_locked_height: v0_response + .chain + .as_ref() + .and_then(|c| c.core_chain_locked_height), + }, + network: StatusNetwork { + chain_id: v0_response + .network + .as_ref() + .map(|n| n.chain_id.clone()) + .unwrap_or_else(|| "unknown".to_string()), + peers_count: v0_response + .network + .as_ref() + .map(|n| n.peers_count) + .unwrap_or(0), + listening: v0_response + .network + .as_ref() + .map(|n| n.listening) + .unwrap_or(false), + }, + state_sync: StatusStateSync { + total_synced_time: v0_response + .state_sync + .as_ref() + .map(|s| s.total_synced_time.to_string()) + .unwrap_or_else(|| "0".to_string()), + remaining_time: v0_response + .state_sync + .as_ref() + .map(|s| s.remaining_time.to_string()) + .unwrap_or_else(|| "0".to_string()), + total_snapshots: v0_response + .state_sync + .as_ref() + .map(|s| s.total_snapshots) + .unwrap_or(0), + chunk_process_avg_time: v0_response + .state_sync + .as_ref() + .map(|s| s.chunk_process_avg_time.to_string()) + .unwrap_or_else(|| "0".to_string()), + snapshot_height: v0_response + .state_sync + .as_ref() + .map(|s| s.snapshot_height.to_string()) + .unwrap_or_else(|| "0".to_string()), + snapshot_chunks_count: v0_response + .state_sync + .as_ref() + .map(|s| s.snapshot_chunks_count.to_string()) + .unwrap_or_else(|| "0".to_string()), + backfilled_blocks: v0_response + .state_sync + .as_ref() + .map(|s| s.backfilled_blocks.to_string()) + .unwrap_or_else(|| "0".to_string()), + backfill_blocks_total: v0_response + .state_sync + .as_ref() + .map(|s| s.backfill_blocks_total.to_string()) + .unwrap_or_else(|| "0".to_string()), + }, + time: StatusTime { + local: v0_response + .time + .as_ref() + .map(|t| t.local.to_string()) + .unwrap_or_else(|| "0".to_string()), + block: v0_response + .time + .as_ref() + .and_then(|t| t.block) + .map(|b| b.to_string()), + genesis: v0_response + .time + .as_ref() + .and_then(|t| t.genesis) + .map(|g| g.to_string()), + epoch: v0_response.time.as_ref().and_then(|t| t.epoch), + }, + }; -#[wasm_bindgen] -pub async fn get_current_quorums_info(sdk: &WasmSdk) -> Result { - use dash_sdk::platform::FetchUnproved; - use drive_proof_verifier::types::{CurrentQuorumsInfo as SdkCurrentQuorumsInfo, NoParamQuery}; - - let quorums_result = SdkCurrentQuorumsInfo::fetch_unproved(sdk.as_ref(), NoParamQuery {}) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch quorums info: {}", e)))?; - - // The result is Option - if let Some(quorum_info) = quorums_result { - // Convert the SDK response to our structure - // Match quorum hashes with validator sets to get detailed information - let quorums: Vec = quorum_info - .quorum_hashes - .into_iter() - .map(|quorum_hash| { - // Try to find the corresponding validator set - let validator_set = quorum_info.validator_sets.iter().find(|vs| { - // Compare the quorum hash bytes directly - - let vs_hash_bytes: &[u8] = vs.quorum_hash().as_ref(); - vs_hash_bytes == &quorum_hash[..] - }); - - if let Some(vs) = validator_set { - let member_count = vs.members().len() as u32; - - // Determine quorum type based on member count and quorum index - // This is an approximation based on common quorum sizes - // TODO: Get actual quorum type from the platform when available - let (quorum_type, threshold) = match member_count { - 50..=70 => ("LLMQ_60_75".to_string(), (member_count * 75 / 100).max(1)), - 90..=110 => ("LLMQ_100_67".to_string(), (member_count * 67 / 100).max(1)), - 350..=450 => ("LLMQ_400_60".to_string(), (member_count * 60 / 100).max(1)), - _ => ( - "LLMQ_TYPE_UNKNOWN".to_string(), - (member_count * 2 / 3).max(1), - ), - }; - - QuorumInfo { - quorum_hash: hex::encode(&quorum_hash), - quorum_type, - member_count, - threshold, - is_verified: true, // We have the validator set, so it's verified - } - } else { - // No validator set found for this quorum hash - // TODO: This should not happen in normal circumstances. When the SDK - // provides complete quorum information, this fallback can be removed. - QuorumInfo { - quorum_hash: hex::encode(&quorum_hash), - quorum_type: "LLMQ_TYPE_UNKNOWN".to_string(), - member_count: 0, - threshold: 0, - is_verified: false, - } - } - }) - .collect(); + serde_wasm_bindgen::to_value(&status).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } - let info = CurrentQuorumsInfo { - quorums, - height: quorum_info.last_platform_block_height, + #[wasm_bindgen(js_name = "getCurrentQuorumsInfo")] + pub async fn get_current_quorums_info(&self) -> Result { + use dash_sdk::platform::FetchUnproved; + use drive_proof_verifier::types::{ + CurrentQuorumsInfo as SdkCurrentQuorumsInfo, NoParamQuery, }; - serde_wasm_bindgen::to_value(&info) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - // No quorum info available - let info = CurrentQuorumsInfo { - quorums: vec![], - height: 0, - }; + let quorums_result = + SdkCurrentQuorumsInfo::fetch_unproved(self.as_ref(), NoParamQuery {}).await?; + + // The result is Option + if let Some(quorum_info) = quorums_result { + // Convert the SDK response to our structure + // Match quorum hashes with validator sets to get detailed information + let quorums: Vec = quorum_info + .quorum_hashes + .into_iter() + .map(|quorum_hash| { + // Try to find the corresponding validator set + let validator_set = quorum_info.validator_sets.iter().find(|vs| { + // Compare the quorum hash bytes directly + + let vs_hash_bytes: &[u8] = vs.quorum_hash().as_ref(); + vs_hash_bytes == &quorum_hash[..] + }); + + if let Some(vs) = validator_set { + let member_count = vs.members().len() as u32; + + // Determine quorum type based on member count and quorum index + // This is an approximation based on common quorum sizes + // TODO: Get actual quorum type from the platform when available + let (quorum_type, threshold) = match member_count { + 50..=70 => ("LLMQ_60_75".to_string(), (member_count * 75 / 100).max(1)), + 90..=110 => { + ("LLMQ_100_67".to_string(), (member_count * 67 / 100).max(1)) + } + 350..=450 => { + ("LLMQ_400_60".to_string(), (member_count * 60 / 100).max(1)) + } + _ => ( + "LLMQ_TYPE_UNKNOWN".to_string(), + (member_count * 2 / 3).max(1), + ), + }; + + QuorumInfo { + quorum_hash: hex::encode(quorum_hash), + quorum_type, + member_count, + threshold, + is_verified: true, // We have the validator set, so it's verified + } + } else { + // No validator set found for this quorum hash + // TODO: This should not happen in normal circumstances. When the SDK + // provides complete quorum information, this fallback can be removed. + QuorumInfo { + quorum_hash: hex::encode(quorum_hash), + quorum_type: "LLMQ_TYPE_UNKNOWN".to_string(), + member_count: 0, + threshold: 0, + is_verified: false, + } + } + }) + .collect(); - serde_wasm_bindgen::to_value(&info) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + let info = CurrentQuorumsInfo { + quorums, + height: quorum_info.last_platform_block_height, + }; + + serde_wasm_bindgen::to_value(&info).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + // No quorum info available + let info = CurrentQuorumsInfo { + quorums: vec![], + height: 0, + }; + + serde_wasm_bindgen::to_value(&info).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } } -} -#[wasm_bindgen] -pub async fn get_total_credits_in_platform(sdk: &WasmSdk) -> Result { - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::{NoParamQuery, TotalCreditsInPlatform as TotalCreditsQuery}; - - let total_credits_result = TotalCreditsQuery::fetch(sdk.as_ref(), NoParamQuery {}) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch total credits: {}", e)))?; - - // TotalCreditsInPlatform is likely a newtype wrapper around u64 - let credits_value = if let Some(credits) = total_credits_result { - // Extract the inner value - assuming it has a field or can be dereferenced - // We'll try to access it as a tuple struct - credits.0 - } else { - 0 - }; - - let response = TotalCreditsResponse { - total_credits_in_platform: credits_value.to_string(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getTotalCreditsInPlatform")] + pub async fn get_total_credits_in_platform(&self) -> Result { + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::{ + NoParamQuery, TotalCreditsInPlatform as TotalCreditsQuery, + }; -#[wasm_bindgen] -pub async fn get_prefunded_specialized_balance( - sdk: &WasmSdk, - identity_id: &str, -) -> Result { - use dash_sdk::platform::{Fetch, Identifier}; - use drive_proof_verifier::types::PrefundedSpecializedBalance as PrefundedBalance; - - // Parse identity ID - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch prefunded specialized balance - let balance_result = PrefundedBalance::fetch(sdk.as_ref(), identity_identifier) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch prefunded specialized balance: {}", - e - )) - })?; + let total_credits_result = TotalCreditsQuery::fetch(self.as_ref(), NoParamQuery {}).await?; - if let Some(balance) = balance_result { - let response = PrefundedSpecializedBalance { - identity_id: identity_id.to_string(), - balance: balance.0, // PrefundedSpecializedBalance is a newtype wrapper around u64 + // TotalCreditsInPlatform is likely a newtype wrapper around u64 + let credits_value = if let Some(credits) = total_credits_result { + // Extract the inner value - assuming it has a field or can be dereferenced + // We'll try to access it as a tuple struct + credits.0 + } else { + 0 }; - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - // Return zero balance if not found - let response = PrefundedSpecializedBalance { - identity_id: identity_id.to_string(), - balance: 0, + let response = TotalCreditsResponse { + total_credits_in_platform: credits_value.to_string(), }; // Use json_compatible serializer let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } -} -#[wasm_bindgen] -pub async fn wait_for_state_transition_result( - sdk: &WasmSdk, - state_transition_hash: &str, -) -> Result { - use dapi_grpc::platform::v0::wait_for_state_transition_result_request::{ - Version, WaitForStateTransitionResultRequestV0, - }; - use dapi_grpc::platform::v0::WaitForStateTransitionResultRequest; - - use dash_sdk::RequestSettings; - use rs_dapi_client::DapiRequestExecutor; - - // Parse the hash from hex string to bytes - let hash_bytes = hex::decode(state_transition_hash) - .map_err(|e| JsError::new(&format!("Invalid state transition hash: {}", e)))?; - - // Create the gRPC request - let request = WaitForStateTransitionResultRequest { - version: Some(Version::V0(WaitForStateTransitionResultRequestV0 { - state_transition_hash: hash_bytes, - prove: sdk.prove(), - })), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to wait for state transition result: {}", - e - )) + #[wasm_bindgen(js_name = "getPrefundedSpecializedBalance")] + pub async fn get_prefunded_specialized_balance( + &self, + identity_id: &str, + ) -> Result { + use dash_sdk::platform::{Fetch, Identifier}; + use drive_proof_verifier::types::PrefundedSpecializedBalance as PrefundedBalance; + + // Parse identity ID + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Fetch prefunded specialized balance + let balance_result = PrefundedBalance::fetch(self.as_ref(), identity_identifier).await?; + + if let Some(balance) = balance_result { + let response = PrefundedSpecializedBalance { + identity_id: identity_id.to_string(), + balance: balance.0, // PrefundedSpecializedBalance is a newtype wrapper around u64 + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + // Return zero balance if not found + let response = PrefundedSpecializedBalance { + identity_id: identity_id.to_string(), + balance: 0, + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + } + + #[wasm_bindgen(js_name = "waitForStateTransitionResult")] + pub async fn wait_for_state_transition_result( + &self, + state_transition_hash: &str, + ) -> Result { + use dapi_grpc::platform::v0::wait_for_state_transition_result_request::{ + Version, WaitForStateTransitionResultRequestV0, + }; + use dapi_grpc::platform::v0::WaitForStateTransitionResultRequest; + + use dash_sdk::RequestSettings; + use rs_dapi_client::DapiRequestExecutor; + + // Parse the hash from hex string to bytes + let hash_bytes = hex::decode(state_transition_hash).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid state transition hash: {}", e)) })?; - // Parse the response - use dapi_grpc::platform::v0::wait_for_state_transition_result_response::{ - wait_for_state_transition_result_response_v0::Result as V0Result, - Version as ResponseVersion, - }; - - let (status, error) = match response.inner.version { - Some(ResponseVersion::V0(v0)) => match v0.result { - Some(V0Result::Error(e)) => { - let error_message = format!("Code: {}, Message: {}", e.code, e.message); - ("ERROR".to_string(), Some(error_message)) - } - Some(V0Result::Proof(_)) => { - // State transition was successful - ("SUCCESS".to_string(), None) - } + // Create the gRPC request + let request = WaitForStateTransitionResultRequest { + version: Some(Version::V0(WaitForStateTransitionResultRequestV0 { + state_transition_hash: hash_bytes, + prove: self.prove(), + })), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!("Failed to wait for state transition result: {}", e)) + })?; + + // Parse the response + use dapi_grpc::platform::v0::wait_for_state_transition_result_response::{ + wait_for_state_transition_result_response_v0::Result as V0Result, + Version as ResponseVersion, + }; + + let (status, error) = match response.inner.version { + Some(ResponseVersion::V0(v0)) => match v0.result { + Some(V0Result::Error(e)) => { + let error_message = format!("Code: {}, Message: {}", e.code, e.message); + ("ERROR".to_string(), Some(error_message)) + } + Some(V0Result::Proof(_)) => { + // State transition was successful + ("SUCCESS".to_string(), None) + } + None => ( + "UNKNOWN".to_string(), + Some("No result returned".to_string()), + ), + }, None => ( "UNKNOWN".to_string(), - Some("No result returned".to_string()), + Some("No version in response".to_string()), ), - }, - None => ( - "UNKNOWN".to_string(), - Some("No version in response".to_string()), - ), - }; - - let result = StateTransitionResult { - state_transition_hash: state_transition_hash.to_string(), - status, - error, - }; - - serde_wasm_bindgen::to_value(&result) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + }; -#[wasm_bindgen] -pub async fn get_path_elements( - sdk: &WasmSdk, - path: Vec, - keys: Vec, -) -> Result { - use dash_sdk::drive::grovedb::Element; - use dash_sdk::platform::FetchMany; - use drive_proof_verifier::types::{Elements, KeysInPath}; - - // Convert string path to byte vectors - // Path elements can be either numeric values (like "96" for Balances) or string keys - let path_bytes: Vec> = path - .iter() - .map(|p| { - // Try to parse as a u8 number first (for root tree paths) - if let Ok(num) = p.parse::() { - vec![num] - } else { - // Otherwise treat as a string key - p.as_bytes().to_vec() - } + let result = StateTransitionResult { + state_transition_hash: state_transition_hash.to_string(), + status, + error, + }; + + serde_wasm_bindgen::to_value(&result).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - - // Convert string keys to byte vectors - let key_bytes: Vec> = keys.iter().map(|k| k.as_bytes().to_vec()).collect(); - - // Create the query - let query = KeysInPath { - path: path_bytes, - keys: key_bytes, - }; - - // Fetch path elements - let path_elements_result: Elements = Element::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch path elements: {}", e)))?; - - // Convert the result to our response format - let elements: Vec = keys - .into_iter() - .map(|key| { - // Check if this key exists in the result - let value = path_elements_result - .get(key.as_bytes()) - .and_then(|element_opt| element_opt.as_ref()) - .and_then(|element| { - // Element can contain different types, we'll serialize it as base64 - element.as_item_bytes().ok().map(|bytes| { - use base64::Engine; - base64::engine::general_purpose::STANDARD.encode(bytes) - }) - }); - - PathElement { - path: vec![key], - value, - } + } + + #[wasm_bindgen(js_name = "getPathElements")] + pub async fn get_path_elements( + &self, + path: Vec, + keys: Vec, + ) -> Result { + use dash_sdk::drive::grovedb::Element; + use dash_sdk::platform::FetchMany; + use drive_proof_verifier::types::{Elements, KeysInPath}; + + // Convert string path to byte vectors + // Path elements can be either numeric values (like "96" for Balances) or string keys + let path_bytes: Vec> = path + .iter() + .map(|p| { + // Try to parse as a u8 number first (for root tree paths) + if let Ok(num) = p.parse::() { + vec![num] + } else { + // Otherwise treat as a string key + p.as_bytes().to_vec() + } + }) + .collect(); + + // Convert string keys to byte vectors + let key_bytes: Vec> = keys.iter().map(|k| k.as_bytes().to_vec()).collect(); + + // Create the query + let query = KeysInPath { + path: path_bytes, + keys: key_bytes, + }; + + // Fetch path elements + let path_elements_result: Elements = Element::fetch_many(self.as_ref(), query).await?; + + // Convert the result to our response format + let elements: Vec = keys + .into_iter() + .map(|key| { + // Check if this key exists in the result + let value = path_elements_result + .get(key.as_bytes()) + .and_then(|element_opt| element_opt.as_ref()) + .and_then(|element| { + // Element can contain different types, we'll serialize it as base64 + element.as_item_bytes().ok().map(|bytes| { + use base64::Engine; + base64::engine::general_purpose::STANDARD.encode(bytes) + }) + }); + + PathElement { + path: vec![key], + value, + } + }) + .collect(); + + serde_wasm_bindgen::to_value(&elements).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); + } - serde_wasm_bindgen::to_value(&elements) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + // Proof versions for system queries -// Proof versions for system queries + #[wasm_bindgen(js_name = "getTotalCreditsInPlatformWithProofInfo")] + pub async fn get_total_credits_in_platform_with_proof_info( + &self, + ) -> Result { + use crate::queries::ProofMetadataResponse; + use dash_sdk::platform::Fetch; + use drive_proof_verifier::types::{ + NoParamQuery, TotalCreditsInPlatform as TotalCreditsQuery, + }; -#[wasm_bindgen] -pub async fn get_total_credits_in_platform_with_proof_info( - sdk: &WasmSdk, -) -> Result { - use crate::queries::ProofMetadataResponse; - use dash_sdk::platform::Fetch; - use drive_proof_verifier::types::{NoParamQuery, TotalCreditsInPlatform as TotalCreditsQuery}; - - let (total_credits_result, metadata, proof) = - TotalCreditsQuery::fetch_with_metadata_and_proof(sdk.as_ref(), NoParamQuery {}, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch total credits with proof: {}", e)) - })?; + let (total_credits_result, metadata, proof) = + TotalCreditsQuery::fetch_with_metadata_and_proof(self.as_ref(), NoParamQuery {}, None) + .await?; - let data = if let Some(credits) = total_credits_result { - Some(TotalCreditsResponse { + let data = total_credits_result.map(|credits| TotalCreditsResponse { total_credits_in_platform: credits.0.to_string(), + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - } else { - None - }; - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + } -#[wasm_bindgen] -pub async fn get_prefunded_specialized_balance_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, -) -> Result { - use crate::queries::ProofMetadataResponse; - use dash_sdk::platform::{Fetch, Identifier}; - use drive_proof_verifier::types::PrefundedSpecializedBalance as PrefundedBalance; - - // Parse identity ID - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch prefunded specialized balance with proof - let (balance_result, metadata, proof) = - PrefundedBalance::fetch_with_metadata_and_proof(sdk.as_ref(), identity_identifier, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch prefunded specialized balance with proof: {}", - e - )) - })?; + #[wasm_bindgen(js_name = "getPrefundedSpecializedBalanceWithProofInfo")] + pub async fn get_prefunded_specialized_balance_with_proof_info( + &self, + identity_id: &str, + ) -> Result { + use crate::queries::ProofMetadataResponse; + use dash_sdk::platform::{Fetch, Identifier}; + use drive_proof_verifier::types::PrefundedSpecializedBalance as PrefundedBalance; + + // Parse identity ID + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Fetch prefunded specialized balance with proof + let (balance_result, metadata, proof) = PrefundedBalance::fetch_with_metadata_and_proof( + self.as_ref(), + identity_identifier, + None, + ) + .await?; + + let data = PrefundedSpecializedBalance { + identity_id: identity_id.to_string(), + balance: balance_result.map(|b| b.0).unwrap_or(0), + }; - let data = PrefundedSpecializedBalance { - identity_id: identity_id.to_string(), - balance: balance_result.map(|b| b.0).unwrap_or(0), - }; - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; -#[wasm_bindgen] -pub async fn get_path_elements_with_proof_info( - sdk: &WasmSdk, - path: Vec, - keys: Vec, -) -> Result { - use crate::queries::ProofMetadataResponse; - use dash_sdk::drive::grovedb::Element; - use dash_sdk::platform::FetchMany; - use drive_proof_verifier::types::KeysInPath; - - // Convert string path to byte vectors - // Path elements can be either numeric values (like "96" for Balances) or string keys - let path_bytes: Vec> = path - .iter() - .map(|p| { - // Try to parse as a u8 number first (for root tree paths) - if let Ok(num) = p.parse::() { - vec![num] - } else { - // Otherwise treat as a string key - p.as_bytes().to_vec() - } + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); + } - // Convert string keys to byte vectors - let key_bytes: Vec> = keys.iter().map(|k| k.as_bytes().to_vec()).collect(); + #[wasm_bindgen(js_name = "getPathElementsWithProofInfo")] + pub async fn get_path_elements_with_proof_info( + &self, + path: Vec, + keys: Vec, + ) -> Result { + use crate::queries::ProofMetadataResponse; + use dash_sdk::drive::grovedb::Element; + use dash_sdk::platform::FetchMany; + use drive_proof_verifier::types::KeysInPath; + + // Convert string path to byte vectors + // Path elements can be either numeric values (like "96" for Balances) or string keys + let path_bytes: Vec> = path + .iter() + .map(|p| { + // Try to parse as a u8 number first (for root tree paths) + if let Ok(num) = p.parse::() { + vec![num] + } else { + // Otherwise treat as a string key + p.as_bytes().to_vec() + } + }) + .collect(); - // Create the query - let query = KeysInPath { - path: path_bytes, - keys: key_bytes, - }; + // Convert string keys to byte vectors + let key_bytes: Vec> = keys.iter().map(|k| k.as_bytes().to_vec()).collect(); - // Fetch path elements with proof - let (path_elements_result, metadata, proof) = - Element::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch path elements with proof: {}", e)) - })?; + // Create the query + let query = KeysInPath { + path: path_bytes, + keys: key_bytes, + }; + + // Fetch path elements with proof + let (path_elements_result, metadata, proof) = + Element::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; - // Convert the result to our response format - let elements: Vec = keys - .into_iter() - .map(|key| { - let value = path_elements_result - .get(key.as_bytes()) - .and_then(|element_opt| element_opt.as_ref()) - .and_then(|element| { - element.as_item_bytes().ok().map(|bytes| { - use base64::Engine; - base64::engine::general_purpose::STANDARD.encode(bytes) - }) - }); - - PathElement { - path: vec![key], - value, - } + // Convert the result to our response format + let elements: Vec = keys + .into_iter() + .map(|key| { + let value = path_elements_result + .get(key.as_bytes()) + .and_then(|element_opt| element_opt.as_ref()) + .and_then(|element| { + element.as_item_bytes().ok().map(|bytes| { + use base64::Engine; + base64::engine::general_purpose::STANDARD.encode(bytes) + }) + }); + + PathElement { + path: vec![key], + value, + } + }) + .collect(); + + let response = ProofMetadataResponse { + data: elements, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - - let response = ProofMetadataResponse { - data: elements, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + } } diff --git a/packages/wasm-sdk/src/queries/token.rs b/packages/wasm-sdk/src/queries/token.rs index b2f18ae10c8..eda077f9c88 100644 --- a/packages/wasm-sdk/src/queries/token.rs +++ b/packages/wasm-sdk/src/queries/token.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::queries::ProofMetadataResponse; use crate::sdk::WasmSdk; use dash_sdk::dpp::balances::credits::TokenAmount; @@ -9,131 +10,7 @@ use dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule; use dash_sdk::platform::{FetchMany, Identifier}; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; - -/// Calculate token ID from contract ID and token position -/// -/// This function calculates the unique token ID based on a data contract ID -/// and the position of the token within that contract. -/// -/// # Arguments -/// * `contract_id` - The data contract ID in base58 format -/// * `token_position` - The position of the token in the contract (0-indexed) -/// -/// # Returns -/// The calculated token ID in base58 format -/// -/// # Example -/// ```javascript -/// const tokenId = await sdk.calculateTokenId("Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv", 0); -/// ``` -#[wasm_bindgen] -pub fn calculate_token_id_from_contract( - contract_id: &str, - token_position: u16, -) -> Result { - // Parse contract ID - let contract_identifier = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Calculate token ID - let token_id = Identifier::from(calculate_token_id( - contract_identifier.as_bytes(), - token_position, - )); - - // Return as base58 string - Ok(token_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58)) -} - -/// Get the current price of a token by contract ID and position -/// -/// This is a convenience function that calculates the token ID from the contract ID -/// and position, then fetches the current pricing schedule for that token. -/// -/// # Arguments -/// * `sdk` - The WasmSdk instance -/// * `contract_id` - The data contract ID in base58 format -/// * `token_position` - The position of the token in the contract (0-indexed) -/// -/// # Returns -/// An object containing: -/// - `tokenId`: The calculated token ID -/// - `currentPrice`: The current price of the token -/// - `basePrice`: The base price of the token (may be same as current for single price) -/// -/// # Example -/// ```javascript -/// const priceInfo = await sdk.getTokenPriceByContract( -/// sdk, -/// "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv", -/// 0 -/// ); -/// console.log(`Token ${priceInfo.tokenId} current price: ${priceInfo.currentPrice}`); -/// ``` -#[wasm_bindgen] -pub async fn get_token_price_by_contract( - sdk: &WasmSdk, - contract_id: &str, - token_position: u16, -) -> Result { - // Calculate token ID - let token_id_string = calculate_token_id_from_contract(contract_id, token_position)?; - - // Parse token ID for the query - let token_identifier = Identifier::from_string( - &token_id_string, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch token prices - let prices_result: drive_proof_verifier::types::TokenDirectPurchasePrices = - TokenPricingSchedule::fetch_many(sdk.as_ref(), &[token_identifier][..]) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch token price: {}", e)))?; - - // Extract price information - if let Some(price_opt) = prices_result.get(&token_identifier) { - if let Some(schedule) = price_opt.as_ref() { - let (base_price, current_price) = match &schedule { - dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SinglePrice(price) => { - (price.to_string(), price.to_string()) - }, - dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SetPrices(prices) => { - // Use first price as base, last as current - let base = prices.first_key_value() - .map(|(_, p)| p.to_string()) - .unwrap_or_else(|| "0".to_string()); - let current = prices.last_key_value() - .map(|(_, p)| p.to_string()) - .unwrap_or_else(|| "0".to_string()); - (base, current) - }, - }; - - let response = TokenPriceResponse { - token_id: token_id_string, - current_price, - base_price, - }; - - serde_wasm_bindgen::to_value(&response) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Err(JsError::new(&format!( - "No pricing schedule found for token at contract {} position {}", - contract_id, token_position - ))) - } - } else { - Err(JsError::new(&format!( - "Token not found at contract {} position {}", - contract_id, token_position - ))) - } -} +use wasm_bindgen::JsValue; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -142,67 +19,6 @@ struct IdentityTokenBalanceResponse { balance: String, // String to handle large numbers } -#[wasm_bindgen] -pub async fn get_identities_token_balances( - sdk: &WasmSdk, - identity_ids: Vec, - token_id: &str, -) -> Result { - use dash_sdk::platform::tokens::identity_token_balances::IdentitiesTokenBalancesQuery; - use drive_proof_verifier::types::identity_token_balance::IdentitiesTokenBalances; - - // Parse token ID - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse identity IDs - let identities: Result, _> = identity_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let identities = identities?; - - // Create query - let query = IdentitiesTokenBalancesQuery { - identity_ids: identities.clone(), - token_id: token_identifier, - }; - - // Fetch balances - let balances_result: IdentitiesTokenBalances = TokenAmount::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identities token balances: {}", e)))?; - - // Convert to response format - let responses: Vec = identity_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; - - balances_result.get(&id).and_then(|balance_opt| { - balance_opt.map(|balance| IdentityTokenBalanceResponse { - identity_id: id_str, - balance: balance.to_string(), - }) - }) - }) - .collect(); - - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct TokenInfoResponse { @@ -210,82 +26,6 @@ struct TokenInfoResponse { is_frozen: bool, } -#[wasm_bindgen] -pub async fn get_identity_token_infos( - sdk: &WasmSdk, - identity_id: &str, - token_ids: Option>, - _limit: Option, - _offset: Option, -) -> Result { - use dash_sdk::platform::tokens::token_info::IdentityTokenInfosQuery; - use drive_proof_verifier::types::token_info::IdentityTokenInfos; - - // Parse identity ID - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // If no token IDs specified, we can't query (SDK requires specific token IDs) - let token_id_strings = - token_ids.ok_or_else(|| JsError::new("token_ids are required for this query"))?; - - // Parse token IDs - let tokens: Result, _> = token_id_strings - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let tokens = tokens?; - - // Create query - let query = IdentityTokenInfosQuery { - identity_id: identity_identifier, - token_ids: tokens.clone(), - }; - - // Fetch token infos - let infos_result: IdentityTokenInfos = IdentityTokenInfo::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identity token infos: {}", e)))?; - - // Convert to response format - let responses: Vec = token_id_strings - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; - - infos_result.get(&id).and_then(|info_opt| { - info_opt.as_ref().map(|info| { - use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; - - // IdentityTokenInfo only contains frozen status - let is_frozen = match &info { - dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), - }; - - TokenInfoResponse { - token_id: id_str, - is_frozen, - } - }) - }) - }) - .collect(); - - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct IdentityTokenInfoResponse { @@ -293,76 +33,6 @@ struct IdentityTokenInfoResponse { is_frozen: bool, } -#[wasm_bindgen] -pub async fn get_identities_token_infos( - sdk: &WasmSdk, - identity_ids: Vec, - token_id: &str, -) -> Result { - use dash_sdk::platform::tokens::token_info::IdentitiesTokenInfosQuery; - use drive_proof_verifier::types::token_info::IdentitiesTokenInfos; - - // Parse token ID - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse identity IDs - let identities: Result, _> = identity_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let identities = identities?; - - // Create query - let query = IdentitiesTokenInfosQuery { - identity_ids: identities.clone(), - token_id: token_identifier, - }; - - // Fetch token infos - let infos_result: IdentitiesTokenInfos = IdentityTokenInfo::fetch_many(sdk.as_ref(), query) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch identities token infos: {}", e)))?; - - // Convert to response format - let responses: Vec = identity_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; - - infos_result.get(&id).and_then(|info_opt| { - info_opt.as_ref().map(|info| { - use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; - - // IdentityTokenInfo only contains frozen status - let is_frozen = match &info { - dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), - }; - - IdentityTokenInfoResponse { - identity_id: id_str, - is_frozen, - } - }) - }) - }) - .collect(); - - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct TokenStatusResponse { @@ -370,50 +40,6 @@ struct TokenStatusResponse { is_paused: bool, } -#[wasm_bindgen] -pub async fn get_token_statuses(sdk: &WasmSdk, token_ids: Vec) -> Result { - use drive_proof_verifier::types::token_status::TokenStatuses; - - // Parse token IDs - let tokens: Result, _> = token_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let tokens = tokens?; - - // Fetch token statuses - let statuses_result: TokenStatuses = TokenStatus::fetch_many(sdk.as_ref(), tokens.clone()) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch token statuses: {}", e)))?; - - // Convert to response format - let responses: Vec = token_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; - - statuses_result.get(&id).and_then(|status_opt| { - status_opt.as_ref().map(|status| TokenStatusResponse { - token_id: id_str, - is_paused: status.paused(), - }) - }) - }) - .collect(); - - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct TokenPriceResponse { @@ -422,78 +48,6 @@ struct TokenPriceResponse { base_price: String, } -#[wasm_bindgen] -pub async fn get_token_direct_purchase_prices( - sdk: &WasmSdk, - token_ids: Vec, -) -> Result { - use drive_proof_verifier::types::TokenDirectPurchasePrices; - - // Parse token IDs - let tokens: Result, _> = token_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - }) - .collect(); - let tokens = tokens?; - - // Fetch token prices - use slice reference - let prices_result: TokenDirectPurchasePrices = - TokenPricingSchedule::fetch_many(sdk.as_ref(), &tokens[..]) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch token direct purchase prices: {}", - e - )) - })?; - - // Convert to response format - let responses: Vec = token_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ).ok()?; - - prices_result.get(&id).and_then(|price_opt| { - price_opt.as_ref().map(|schedule| { - // Get prices based on the schedule type - let (base_price, current_price) = match &schedule { - dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SinglePrice(price) => { - (price.to_string(), price.to_string()) - }, - dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SetPrices(prices) => { - // Use first price as base, last as current - let base = prices.first_key_value() - .map(|(_, p)| p.to_string()) - .unwrap_or_else(|| "0".to_string()); - let current = prices.last_key_value() - .map(|(_, p)| p.to_string()) - .unwrap_or_else(|| "0".to_string()); - (base, current) - }, - }; - - TokenPriceResponse { - token_id: id_str, - current_price, - base_price, - } - }) - }) - }) - .collect(); - - serde_wasm_bindgen::to_value(&responses) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct TokenContractInfoResponse { @@ -501,51 +55,6 @@ struct TokenContractInfoResponse { token_contract_position: u16, } -#[wasm_bindgen] -pub async fn get_token_contract_info( - sdk: &WasmSdk, - data_contract_id: &str, -) -> Result { - use dash_sdk::dpp::tokens::contract_info::TokenContractInfo; - use dash_sdk::platform::Fetch; - - // Parse contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch token contract info - let info_result = TokenContractInfo::fetch(sdk.as_ref(), contract_id) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch token contract info: {}", e)))?; - - if let Some(info) = info_result { - use dash_sdk::dpp::tokens::contract_info::v0::TokenContractInfoV0Accessors; - - // Extract fields based on the enum variant - let (contract_id, position) = match &info { - dash_sdk::dpp::tokens::contract_info::TokenContractInfo::V0(v0) => { - (v0.contract_id(), v0.token_contract_position()) - } - }; - - let response = TokenContractInfoResponse { - contract_id: contract_id - .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - token_contract_position: position, - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Ok(JsValue::NULL) - } -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct LastClaimResponse { @@ -553,155 +62,6 @@ struct LastClaimResponse { last_claim_block_height: u64, } -#[wasm_bindgen] -pub async fn get_token_perpetual_distribution_last_claim( - sdk: &WasmSdk, - identity_id: &str, - token_id: &str, -) -> Result { - // Parse IDs - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Use direct gRPC request instead of high-level SDK fetch to avoid proof verification issues - use dapi_grpc::platform::v0::{ - get_token_perpetual_distribution_last_claim_request::{ - GetTokenPerpetualDistributionLastClaimRequestV0, Version, - }, - GetTokenPerpetualDistributionLastClaimRequest, - }; - use rs_dapi_client::DapiRequestExecutor; - - // Create direct gRPC Request without proofs to avoid context provider issues - let request = GetTokenPerpetualDistributionLastClaimRequest { - version: Some(Version::V0( - GetTokenPerpetualDistributionLastClaimRequestV0 { - token_id: token_identifier.to_vec(), - identity_id: identity_identifier.to_vec(), - contract_info: None, // Not needed for this query - prove: false, // Use prove: false to avoid proof verification and context provider dependency - }, - )), - }; - - // Execute the gRPC request - let response = sdk - .inner_sdk() - .execute(request, rs_dapi_client::RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch token perpetual distribution last claim: {}", - e - )) - })?; - - // Extract result from response and convert to our expected format - let claim_result = match response.inner.version { - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::Version::V0(v0)) => { - match v0.result { - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::Result::LastClaim(claim)) => { - // Convert gRPC response to RewardDistributionMoment equivalent - match claim.paid_at { - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::TimestampMs(timestamp)) => { - Some((timestamp, 0)) // (timestamp_ms, block_height) - }, - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::BlockHeight(height)) => { - Some((0, height)) // (timestamp_ms, block_height) - }, - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::Epoch(epoch)) => { - Some((0, epoch as u64)) // (timestamp_ms, block_height) - }, - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::RawBytes(bytes)) => { - // Raw bytes format specification (confirmed via server trace logs): - // - Total length: 8 bytes (big-endian encoding) - // - Bytes 0-3: Timestamp as u32 (seconds since Unix epoch, 0 = no timestamp recorded) - // - Bytes 4-7: Block height as u32 (Dash blockchain block number) - // - // Validation ranges: - // - Timestamp: 0 (unset) or >= 1609459200 (Jan 1, 2021 00:00:00 UTC, before Dash Platform mainnet) - // - Block height: 0 (invalid) or >= 1 (valid blockchain height) - if bytes.len() >= 8 { - let timestamp = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64; - let block_height = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as u64; - - // Validate timestamp: must be 0 (unset) or a reasonable Unix timestamp - let validated_timestamp = if timestamp != 0 && timestamp < 1609459200 { - web_sys::console::warn_1(&format!("Invalid timestamp in raw bytes: {} (too early)", timestamp).into()); - 0 // Use 0 for invalid timestamps - } else { - timestamp - }; - - // Validate block height: must be a positive value - let validated_block_height = if block_height == 0 { - web_sys::console::warn_1(&"Invalid block height in raw bytes: 0 (genesis block not expected)".into()); - 1 // Use minimum valid block height - } else { - block_height - }; - - Some((validated_timestamp * 1000, validated_block_height)) // Convert timestamp to milliseconds - } else if bytes.len() >= 4 { - // Fallback: decode only the last 4 bytes as block height - let block_height = u32::from_be_bytes([ - bytes[bytes.len()-4], bytes[bytes.len()-3], - bytes[bytes.len()-2], bytes[bytes.len()-1] - ]) as u64; - - // Validate block height - let validated_block_height = if block_height == 0 { - web_sys::console::warn_1(&"Invalid block height in fallback parsing: 0".into()); - 1 // Use minimum valid block height - } else { - block_height - }; - - Some((0, validated_block_height)) - } else { - web_sys::console::warn_1(&format!("Insufficient raw bytes length: {} (expected 8 or 4)", bytes.len()).into()); - Some((0, 0)) - } - }, - None => { - None // No paid_at info - } - } - }, - Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::Result::Proof(_)) => { - return Err(JsError::new("Received proof instead of data - this should not happen with prove: false")) - }, - None => None, // No claim found - } - }, - None => { - return Err(JsError::new("Invalid response version")) - } - }; - - if let Some((timestamp_ms, block_height)) = claim_result { - let response = LastClaimResponse { - last_claim_timestamp_ms: timestamp_ms, - last_claim_block_height: block_height, - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Ok(JsValue::NULL) - } -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct TokenTotalSupplyResponse { @@ -709,609 +69,1214 @@ struct TokenTotalSupplyResponse { } #[wasm_bindgen] -pub async fn get_token_total_supply(sdk: &WasmSdk, token_id: &str) -> Result { - use dash_sdk::dpp::balances::total_single_token_balance::TotalSingleTokenBalance; - use dash_sdk::platform::Fetch; - - // Parse token ID - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch total supply - let supply_result = TotalSingleTokenBalance::fetch(sdk.as_ref(), token_identifier) - .await - .map_err(|e| JsError::new(&format!("Failed to fetch token total supply: {}", e)))?; - - if let Some(supply) = supply_result { - let response = TokenTotalSupplyResponse { - total_supply: supply.token_supply.to_string(), - }; +impl WasmSdk { + /// Calculate token ID from contract ID and token position + /// + /// This function calculates the unique token ID based on a data contract ID + /// and the position of the token within that contract. + /// + /// # Arguments + /// * `contract_id` - The data contract ID in base58 format + /// * `token_position` - The position of the token in the contract (0-indexed) + /// + /// # Returns + /// The calculated token ID in base58 format + /// + /// # Example + /// ```javascript + /// const tokenId = await sdk.calculateTokenId("Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv", 0); + /// ``` + #[wasm_bindgen(js_name = "calculateTokenIdFromContract")] + pub fn calculate_token_id_from_contract( + contract_id: &str, + token_position: u16, + ) -> Result { + // Parse contract ID + let contract_identifier = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Calculate token ID + let token_id = Identifier::from(calculate_token_id( + contract_identifier.as_bytes(), + token_position, + )); + + // Return as base58 string + Ok(token_id.to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58)) + } - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) - } else { - Ok(JsValue::NULL) + /// Get the current price of a token by contract ID and position + /// + /// This is a convenience function that calculates the token ID from the contract ID + /// and position, then fetches the current pricing schedule for that token. + /// + /// # Arguments + /// * `sdk` - The WasmSdk instance + /// * `contract_id` - The data contract ID in base58 format + /// * `token_position` - The position of the token in the contract (0-indexed) + /// + /// # Returns + /// An object containing: + /// - `tokenId`: The calculated token ID + /// - `currentPrice`: The current price of the token + /// - `basePrice`: The base price of the token (may be same as current for single price) + /// + /// # Example + /// ```javascript + /// const priceInfo = await sdk.getTokenPriceByContract( + /// sdk, + /// "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv", + /// 0 + /// ); + /// console.log(`Token ${priceInfo.tokenId} current price: ${priceInfo.currentPrice}`); + /// ``` + #[wasm_bindgen(js_name = "getTokenPriceByContract")] + pub async fn get_token_price_by_contract( + &self, + contract_id: &str, + token_position: u16, + ) -> Result { + // Calculate token ID + let token_id_string = Self::calculate_token_id_from_contract(contract_id, token_position)?; + + // Parse token ID for the query + let token_identifier = Identifier::from_string( + &token_id_string, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Fetch token prices + let prices_result: drive_proof_verifier::types::TokenDirectPurchasePrices = + TokenPricingSchedule::fetch_many(self.as_ref(), &[token_identifier][..]).await?; + + // Extract price information + if let Some(price_opt) = prices_result.get(&token_identifier) { + if let Some(schedule) = price_opt.as_ref() { + let (base_price, current_price) = match &schedule { + dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SinglePrice(price) => { + (price.to_string(), price.to_string()) + }, + dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SetPrices(prices) => { + // Use first price as base, last as current + let base = prices.first_key_value() + .map(|(_, p)| p.to_string()) + .unwrap_or_else(|| "0".to_string()); + let current = prices.last_key_value() + .map(|(_, p)| p.to_string()) + .unwrap_or_else(|| "0".to_string()); + (base, current) + }, + }; + + let response = TokenPriceResponse { + token_id: token_id_string, + current_price, + base_price, + }; + + serde_wasm_bindgen::to_value(&response).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Err(WasmSdkError::not_found(format!( + "No pricing schedule found for token at contract {} position {}", + contract_id, token_position + ))) + } + } else { + Err(WasmSdkError::not_found(format!( + "Token not found at contract {} position {}", + contract_id, token_position + ))) + } } -} -// Proof versions for token queries + #[wasm_bindgen(js_name = "getIdentitiesTokenBalances")] + pub async fn get_identities_token_balances( + &self, + identity_ids: Vec, + token_id: &str, + ) -> Result { + use dash_sdk::platform::tokens::identity_token_balances::IdentitiesTokenBalancesQuery; + use drive_proof_verifier::types::identity_token_balance::IdentitiesTokenBalances; + + // Parse token ID + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Parse identity IDs + let identities: Result, _> = identity_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let identities = identities + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Create query + let query = IdentitiesTokenBalancesQuery { + identity_ids: identities.clone(), + token_id: token_identifier, + }; -#[wasm_bindgen] -pub async fn get_identities_token_balances_with_proof_info( - sdk: &WasmSdk, - identity_ids: Vec, - token_id: &str, -) -> Result { - use dash_sdk::platform::tokens::identity_token_balances::IdentitiesTokenBalancesQuery; - - // Parse token ID - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse identity IDs - let identities: Result, _> = identity_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + // Fetch balances + let balances_result: IdentitiesTokenBalances = + TokenAmount::fetch_many(self.as_ref(), query).await?; + + // Convert to response format + let responses: Vec = identity_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + balances_result.get(&id).and_then(|balance_opt| { + balance_opt.map(|balance| IdentityTokenBalanceResponse { + identity_id: id_str, + balance: balance.to_string(), + }) + }) + }) + .collect(); + + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - let identities = identities?; - - // Create query - let query = IdentitiesTokenBalancesQuery { - identity_ids: identities.clone(), - token_id: token_identifier, - }; - - // Fetch balances with proof - let (balances_result, metadata, proof): ( - drive_proof_verifier::types::identity_token_balance::IdentitiesTokenBalances, - _, - _, - ) = TokenAmount::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identities token balances with proof: {}", - e - )) - })?; - - // Convert to response format - let responses: Vec = identity_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; + } + + #[wasm_bindgen(js_name = "getIdentityTokenInfos")] + pub async fn get_identity_token_infos( + &self, + identity_id: &str, + token_ids: Vec, + ) -> Result { + use dash_sdk::platform::tokens::token_info::IdentityTokenInfosQuery; + use drive_proof_verifier::types::token_info::IdentityTokenInfos; + + // Parse identity ID + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Parse token IDs + let tokens: Result, _> = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let tokens = tokens + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Create query + let query = IdentityTokenInfosQuery { + identity_id: identity_identifier, + token_ids: tokens.clone(), + }; - balances_result.get(&id).and_then(|balance_opt| { - balance_opt.map(|balance| IdentityTokenBalanceResponse { - identity_id: id_str, - balance: balance.to_string(), + // Fetch token infos + let infos_result: IdentityTokenInfos = + IdentityTokenInfo::fetch_many(self.as_ref(), query).await?; + + // Convert to response format + let responses: Vec = token_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + infos_result.get(&id).and_then(|info_opt| { + info_opt.as_ref().map(|info| { + use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + + // IdentityTokenInfo only contains frozen status + let is_frozen = match &info { + dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), + }; + + TokenInfoResponse { + token_id: id_str, + is_frozen, + } + }) }) }) - }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + .collect(); -#[wasm_bindgen] -pub async fn get_token_statuses_with_proof_info( - sdk: &WasmSdk, - token_ids: Vec, -) -> Result { - // Parse token IDs - let tokens: Result, _> = token_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - let tokens = tokens?; - - // Fetch token statuses with proof - let (statuses_result, metadata, proof) = - TokenStatus::fetch_many_with_metadata_and_proof(sdk.as_ref(), tokens.clone(), None) - .await - .map_err(|e| { - JsError::new(&format!("Failed to fetch token statuses with proof: {}", e)) - })?; + } - // Convert to response format - let responses: Vec = token_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; + #[wasm_bindgen(js_name = "getIdentitiesTokenInfos")] + pub async fn get_identities_token_infos( + &self, + identity_ids: Vec, + token_id: &str, + ) -> Result { + use dash_sdk::platform::tokens::token_info::IdentitiesTokenInfosQuery; + use drive_proof_verifier::types::token_info::IdentitiesTokenInfos; + + // Parse token ID + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Parse identity IDs + let identities: Result, _> = identity_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let identities = identities + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Create query + let query = IdentitiesTokenInfosQuery { + identity_ids: identities.clone(), + token_id: token_identifier, + }; - statuses_result.get(&id).and_then(|status_opt| { - status_opt.as_ref().map(|status| TokenStatusResponse { - token_id: id_str, - is_paused: status.paused(), + // Fetch token infos + let infos_result: IdentitiesTokenInfos = + IdentityTokenInfo::fetch_many(self.as_ref(), query).await?; + + // Convert to response format + let responses: Vec = identity_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + infos_result.get(&id).and_then(|info_opt| { + info_opt.as_ref().map(|info| { + use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + + // IdentityTokenInfo only contains frozen status + let is_frozen = match &info { + dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), + }; + + IdentityTokenInfoResponse { + identity_id: id_str, + is_frozen, + } + }) }) }) + .collect(); + + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + } -#[wasm_bindgen] -pub async fn get_token_total_supply_with_proof_info( - sdk: &WasmSdk, - token_id: &str, -) -> Result { - use dash_sdk::dpp::balances::total_single_token_balance::TotalSingleTokenBalance; - use dash_sdk::platform::Fetch; - - // Parse token ID - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch total supply with proof - let (supply_result, metadata, proof) = TotalSingleTokenBalance::fetch_with_metadata_and_proof( - sdk.as_ref(), - token_identifier, - None, - ) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch token total supply with proof: {}", - e - )) - })?; - - let data = if let Some(supply) = supply_result { - Some(TokenTotalSupplyResponse { - total_supply: supply.token_supply.to_string(), + #[wasm_bindgen(js_name = "getTokenStatuses")] + pub async fn get_token_statuses( + &self, + token_ids: Vec, + ) -> Result { + use drive_proof_verifier::types::token_status::TokenStatuses; + + // Parse token IDs + let tokens: Result, _> = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let tokens = tokens + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Fetch token statuses + let statuses_result: TokenStatuses = + TokenStatus::fetch_many(self.as_ref(), tokens.clone()).await?; + + // Convert to response format + let responses: Vec = token_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + statuses_result.get(&id).and_then(|status_opt| { + status_opt.as_ref().map(|status| TokenStatusResponse { + token_id: id_str, + is_paused: status.paused(), + }) + }) + }) + .collect(); + + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - } else { - None - }; - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + } -// Additional proof info versions for remaining token queries + #[wasm_bindgen(js_name = "getTokenDirectPurchasePrices")] + pub async fn get_token_direct_purchase_prices( + &self, + token_ids: Vec, + ) -> Result { + use drive_proof_verifier::types::TokenDirectPurchasePrices; + + // Parse token IDs + let tokens: Result, _> = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let tokens = tokens + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Fetch token prices - use slice reference + let prices_result: TokenDirectPurchasePrices = + TokenPricingSchedule::fetch_many(self.as_ref(), &tokens[..]).await?; + + // Convert to response format + let responses: Vec = token_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ).ok()?; + + prices_result.get(&id).and_then(|price_opt| { + price_opt.as_ref().map(|schedule| { + // Get prices based on the schedule type + let (base_price, current_price) = match &schedule { + dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SinglePrice(price) => { + (price.to_string(), price.to_string()) + }, + dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SetPrices(prices) => { + // Use first price as base, last as current + let base = prices.first_key_value() + .map(|(_, p)| p.to_string()) + .unwrap_or_else(|| "0".to_string()); + let current = prices.last_key_value() + .map(|(_, p)| p.to_string()) + .unwrap_or_else(|| "0".to_string()); + (base, current) + }, + }; + + TokenPriceResponse { + token_id: id_str, + current_price, + base_price, + } + }) + }) + }) + .collect(); -#[wasm_bindgen] -pub async fn get_identity_token_infos_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - token_ids: Option>, - _limit: Option, - _offset: Option, -) -> Result { - use dash_sdk::platform::tokens::token_info::IdentityTokenInfosQuery; - use drive_proof_verifier::types::token_info::IdentityTokenInfos; - - // Parse identity ID - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // If no token IDs specified, we can't query (SDK requires specific token IDs) - let token_id_strings = - token_ids.ok_or_else(|| JsError::new("token_ids are required for this query"))?; - - // Parse token IDs - let tokens: Result, _> = token_id_strings - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + serde_wasm_bindgen::to_value(&responses).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - let tokens = tokens?; - - // Create query - let query = IdentityTokenInfosQuery { - identity_id: identity_identifier, - token_ids: tokens.clone(), - }; - - // Fetch token infos with proof - let (infos_result, metadata, proof): (IdentityTokenInfos, _, _) = - IdentityTokenInfo::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) + } + + #[wasm_bindgen(js_name = "getTokenContractInfo")] + pub async fn get_token_contract_info( + &self, + data_contract_id: &str, + ) -> Result { + use dash_sdk::dpp::tokens::contract_info::TokenContractInfo; + use dash_sdk::platform::Fetch; + + // Parse contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Fetch token contract info + let info_result = TokenContractInfo::fetch(self.as_ref(), contract_id).await?; + + if let Some(info) = info_result { + use dash_sdk::dpp::tokens::contract_info::v0::TokenContractInfoV0Accessors; + + // Extract fields based on the enum variant + let (contract_id, position) = match &info { + dash_sdk::dpp::tokens::contract_info::TokenContractInfo::V0(v0) => { + (v0.contract_id(), v0.token_contract_position()) + } + }; + + let response = TokenContractInfoResponse { + contract_id: contract_id + .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + token_contract_position: position, + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Ok(JsValue::NULL) + } + } + + #[wasm_bindgen(js_name = "getTokenPerpetualDistributionLastClaim")] + pub async fn get_token_perpetual_distribution_last_claim( + &self, + identity_id: &str, + token_id: &str, + ) -> Result { + // Parse IDs + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Use direct gRPC request instead of high-level SDK fetch to avoid proof verification issues + use dapi_grpc::platform::v0::{ + get_token_perpetual_distribution_last_claim_request::{ + GetTokenPerpetualDistributionLastClaimRequestV0, Version, + }, + GetTokenPerpetualDistributionLastClaimRequest, + }; + use rs_dapi_client::DapiRequestExecutor; + + // Create direct gRPC Request without proofs to avoid context provider issues + let request = GetTokenPerpetualDistributionLastClaimRequest { + version: Some(Version::V0( + GetTokenPerpetualDistributionLastClaimRequestV0 { + token_id: token_identifier.to_vec(), + identity_id: identity_identifier.to_vec(), + contract_info: None, // Not needed for this query + prove: false, // Use prove: false to avoid proof verification and context provider dependency + }, + )), + }; + + // Execute the gRPC request + let response = self + .inner_sdk() + .execute(request, rs_dapi_client::RequestSettings::default()) .await .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identity token infos with proof: {}", + WasmSdkError::generic(format!( + "Failed to fetch token perpetual distribution last claim: {}", e )) })?; - // Convert to response format - let responses: Vec = token_id_strings - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) - .ok()?; + // Extract result from response and convert to our expected format + let claim_result = match response.inner.version { + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::Version::V0(v0)) => { + match v0.result { + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::Result::LastClaim(claim)) => { + // Convert gRPC response to RewardDistributionMoment equivalent + match claim.paid_at { + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::TimestampMs(timestamp)) => { + Some((timestamp, 0)) // (timestamp_ms, block_height) + }, + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::BlockHeight(height)) => { + Some((0, height)) // (timestamp_ms, block_height) + }, + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::Epoch(epoch)) => { + Some((0, epoch as u64)) // (timestamp_ms, block_height) + }, + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::last_claim_info::PaidAt::RawBytes(bytes)) => { + // Raw bytes format specification (confirmed via server trace logs): + // - Total length: 8 bytes (big-endian encoding) + // - Bytes 0-3: Timestamp as u32 (seconds since Unix epoch, 0 = no timestamp recorded) + // - Bytes 4-7: Block height as u32 (Dash blockchain block number) + // + // Validation ranges: + // - Timestamp: 0 (unset) or >= 1609459200 (Jan 1, 2021 00:00:00 UTC, before Dash Platform mainnet) + // - Block height: 0 (invalid) or >= 1 (valid blockchain height) + if bytes.len() >= 8 { + let timestamp = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64; + let block_height = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as u64; + + // Validate timestamp: must be 0 (unset) or a reasonable Unix timestamp + let validated_timestamp = if timestamp != 0 && timestamp < 1609459200 { + tracing::warn!(target = "wasm_sdk", timestamp, "Invalid timestamp in raw bytes (too early)"); + 0 // Use 0 for invalid timestamps + } else { + timestamp + }; + + // Validate block height: must be a positive value + let validated_block_height = if block_height == 0 { + tracing::warn!(target = "wasm_sdk", "Invalid block height in raw bytes: 0 (genesis block not expected)"); + 1 // Use minimum valid block height + } else { + block_height + }; + + Some((validated_timestamp * 1000, validated_block_height)) // Convert timestamp to milliseconds + } else if bytes.len() >= 4 { + // Fallback: decode only the last 4 bytes as block height + let block_height = u32::from_be_bytes([ + bytes[bytes.len() - 4], bytes[bytes.len() - 3], + bytes[bytes.len() - 2], bytes[bytes.len() - 1] + ]) as u64; + + // Validate block height + let validated_block_height = if block_height == 0 { + tracing::warn!(target = "wasm_sdk", "Invalid block height in fallback parsing: 0"); + 1 // Use minimum valid block height + } else { + block_height + }; + + Some((0, validated_block_height)) + } else { + tracing::warn!(target = "wasm_sdk", len = bytes.len(), "Insufficient raw bytes length (expected 8 or 4)"); + Some((0, 0)) + } + }, + None => { + None // No paid_at info + } + } + }, + Some(dapi_grpc::platform::v0::get_token_perpetual_distribution_last_claim_response::get_token_perpetual_distribution_last_claim_response_v0::Result::Proof(_)) => { + return Err(WasmSdkError::generic("Received proof instead of data - this should not happen with prove: false")) + }, + None => None, // No claim found + } + }, + None => { + return Err(WasmSdkError::generic("Invalid response version")) + } + }; + + if let Some((timestamp_ms, block_height)) = claim_result { + let response = LastClaimResponse { + last_claim_timestamp_ms: timestamp_ms, + last_claim_block_height: block_height, + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Ok(JsValue::NULL) + } + } - infos_result.get(&id).and_then(|info_opt| { - info_opt.as_ref().map(|info| { - use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + #[wasm_bindgen(js_name = "getTokenTotalSupply")] + pub async fn get_token_total_supply(&self, token_id: &str) -> Result { + use dash_sdk::dpp::balances::total_single_token_balance::TotalSingleTokenBalance; + use dash_sdk::platform::Fetch; - // IdentityTokenInfo only contains frozen status - let is_frozen = match &info { - dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), - }; + // Parse token ID + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; - TokenInfoResponse { - token_id: id_str, - is_frozen, - } + // Fetch total supply + let supply_result = TotalSingleTokenBalance::fetch(self.as_ref(), token_identifier).await?; + + if let Some(supply) = supply_result { + let response = TokenTotalSupplyResponse { + total_supply: supply.token_supply.to_string(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } else { + Ok(JsValue::NULL) + } + } + + // Proof versions for token queries + + #[wasm_bindgen(js_name = "getIdentitiesTokenBalancesWithProofInfo")] + pub async fn get_identities_token_balances_with_proof_info( + &self, + identity_ids: Vec, + token_id: &str, + ) -> Result { + use dash_sdk::platform::tokens::identity_token_balances::IdentitiesTokenBalancesQuery; + + // Parse token ID + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Parse identity IDs + let identities: Result, _> = identity_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let identities = identities + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Create query + let query = IdentitiesTokenBalancesQuery { + identity_ids: identities.clone(), + token_id: token_identifier, + }; + + // Fetch balances with proof + let (balances_result, metadata, proof): ( + drive_proof_verifier::types::identity_token_balance::IdentitiesTokenBalances, + _, + _, + ) = TokenAmount::fetch_many_with_metadata_and_proof(self.as_ref(), query, None).await?; + + // Convert to response format + let responses: Vec = identity_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + balances_result.get(&id).and_then(|balance_opt| { + balance_opt.map(|balance| IdentityTokenBalanceResponse { + identity_id: id_str, + balance: balance.to_string(), + }) }) }) + .collect(); + + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + } -#[wasm_bindgen] -pub async fn get_identities_token_infos_with_proof_info( - sdk: &WasmSdk, - identity_ids: Vec, - token_id: &str, -) -> Result { - use dash_sdk::platform::tokens::token_info::IdentitiesTokenInfosQuery; - use drive_proof_verifier::types::token_info::IdentitiesTokenInfos; - - // Parse token ID - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse identity IDs - let identities: Result, _> = identity_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + #[wasm_bindgen(js_name = "getTokenStatusesWithProofInfo")] + pub async fn get_token_statuses_with_proof_info( + &self, + token_ids: Vec, + ) -> Result { + // Parse token IDs + let tokens: Result, _> = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let tokens = tokens + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Fetch token statuses with proof + let (statuses_result, metadata, proof) = + TokenStatus::fetch_many_with_metadata_and_proof(self.as_ref(), tokens.clone(), None) + .await?; + + // Convert to response format + let responses: Vec = token_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + statuses_result.get(&id).and_then(|status_opt| { + status_opt.as_ref().map(|status| TokenStatusResponse { + token_id: id_str, + is_paused: status.paused(), + }) + }) + }) + .collect(); + + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - let identities = identities?; - - // Create query - let query = IdentitiesTokenInfosQuery { - identity_ids: identities.clone(), - token_id: token_identifier, - }; - - // Fetch token infos with proof - let (infos_result, metadata, proof): (IdentitiesTokenInfos, _, _) = - IdentityTokenInfo::fetch_many_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch identities token infos with proof: {}", - e - )) - })?; + } - // Convert to response format - let responses: Vec = identity_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + #[wasm_bindgen(js_name = "getTokenTotalSupplyWithProofInfo")] + pub async fn get_token_total_supply_with_proof_info( + &self, + token_id: &str, + ) -> Result { + use dash_sdk::dpp::balances::total_single_token_balance::TotalSingleTokenBalance; + use dash_sdk::platform::Fetch; + + // Parse token ID + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Fetch total supply with proof + let (supply_result, metadata, proof) = + TotalSingleTokenBalance::fetch_with_metadata_and_proof( + self.as_ref(), + token_identifier, + None, ) - .ok()?; + .await?; - infos_result.get(&id).and_then(|info_opt| { - info_opt.as_ref().map(|info| { - use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + let data = supply_result.map(|supply| TokenTotalSupplyResponse { + total_supply: supply.token_supply.to_string(), + }); - // IdentityTokenInfo only contains frozen status - let is_frozen = match &info { - dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), - }; + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; - IdentityTokenInfoResponse { - identity_id: id_str, - is_frozen, - } + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + // Additional proof info versions for remaining token queries + + #[wasm_bindgen(js_name = "getIdentityTokenInfosWithProofInfo")] + pub async fn get_identity_token_infos_with_proof_info( + &self, + identity_id: &str, + token_ids: Vec, + ) -> Result { + use dash_sdk::platform::tokens::token_info::IdentityTokenInfosQuery; + use drive_proof_verifier::types::token_info::IdentityTokenInfos; + + // Parse identity ID + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Parse token IDs + let tokens: Result, _> = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let tokens = tokens + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Create query + let query = IdentityTokenInfosQuery { + identity_id: identity_identifier, + token_ids: tokens.clone(), + }; + + // Fetch token infos with proof + let (infos_result, metadata, proof): (IdentityTokenInfos, _, _) = + IdentityTokenInfo::fetch_many_with_metadata_and_proof(self.as_ref(), query, None) + .await?; + + // Convert to response format + let responses: Vec = token_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + infos_result.get(&id).and_then(|info_opt| { + info_opt.as_ref().map(|info| { + use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + + // IdentityTokenInfo only contains frozen status + let is_frozen = match &info { + dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), + }; + + TokenInfoResponse { + token_id: id_str, + is_frozen, + } + }) }) }) - }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + .collect(); -#[wasm_bindgen] -pub async fn get_token_direct_purchase_prices_with_proof_info( - sdk: &WasmSdk, - token_ids: Vec, -) -> Result { - use drive_proof_verifier::types::TokenDirectPurchasePrices; - - // Parse token IDs - let tokens: Result, _> = token_ids - .iter() - .map(|id| { - Identifier::from_string( - id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ) + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - let tokens = tokens?; + } - // Fetch token prices with proof - use slice reference - let (prices_result, metadata, proof): (TokenDirectPurchasePrices, _, _) = - TokenPricingSchedule::fetch_many_with_metadata_and_proof(sdk.as_ref(), &tokens[..], None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch token direct purchase prices with proof: {}", - e - )) - })?; + #[wasm_bindgen(js_name = "getIdentitiesTokenInfosWithProofInfo")] + pub async fn get_identities_token_infos_with_proof_info( + &self, + identity_ids: Vec, + token_id: &str, + ) -> Result { + use dash_sdk::platform::tokens::token_info::IdentitiesTokenInfosQuery; + use drive_proof_verifier::types::token_info::IdentitiesTokenInfos; + + // Parse token ID + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Parse identity IDs + let identities: Result, _> = identity_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let identities = identities + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Create query + let query = IdentitiesTokenInfosQuery { + identity_ids: identities.clone(), + token_id: token_identifier, + }; - // Convert to response format - let responses: Vec = token_ids - .into_iter() - .filter_map(|id_str| { - let id = Identifier::from_string( - &id_str, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - ).ok()?; - - prices_result.get(&id).and_then(|price_opt| { - price_opt.as_ref().map(|schedule| { - // Get prices based on the schedule type - let (base_price, current_price) = match &schedule { - dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SinglePrice(price) => { - (price.to_string(), price.to_string()) - }, - dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SetPrices(prices) => { - // Use first price as base, last as current - let base = prices.first_key_value() - .map(|(_, p)| p.to_string()) - .unwrap_or_else(|| "0".to_string()); - let current = prices.last_key_value() - .map(|(_, p)| p.to_string()) - .unwrap_or_else(|| "0".to_string()); - (base, current) - }, - }; - - TokenPriceResponse { - token_id: id_str, - current_price, - base_price, - } + // Fetch token infos with proof + let (infos_result, metadata, proof): (IdentitiesTokenInfos, _, _) = + IdentityTokenInfo::fetch_many_with_metadata_and_proof(self.as_ref(), query, None) + .await?; + + // Convert to response format + let responses: Vec = identity_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .ok()?; + + infos_result.get(&id).and_then(|info_opt| { + info_opt.as_ref().map(|info| { + use dash_sdk::dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; + + // IdentityTokenInfo only contains frozen status + let is_frozen = match &info { + dash_sdk::dpp::tokens::info::IdentityTokenInfo::V0(v0) => v0.frozen(), + }; + + IdentityTokenInfoResponse { + identity_id: id_str, + is_frozen, + } + }) }) }) + .collect(); + + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - .collect(); - - let response = ProofMetadataResponse { - data: responses, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + } -#[wasm_bindgen] -pub async fn get_token_contract_info_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, -) -> Result { - use dash_sdk::dpp::tokens::contract_info::TokenContractInfo; - use dash_sdk::platform::Fetch; - - // Parse contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Fetch token contract info with proof - let (info_result, metadata, proof) = - TokenContractInfo::fetch_with_metadata_and_proof(sdk.as_ref(), contract_id, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch token contract info with proof: {}", - e - )) - })?; + #[wasm_bindgen(js_name = "getTokenDirectPurchasePricesWithProofInfo")] + pub async fn get_token_direct_purchase_prices_with_proof_info( + &self, + token_ids: Vec, + ) -> Result { + use drive_proof_verifier::types::TokenDirectPurchasePrices; + + // Parse token IDs + let tokens: Result, _> = token_ids + .iter() + .map(|id| { + Identifier::from_string( + id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + }) + .collect(); + let tokens = tokens + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Fetch token prices with proof - use slice reference + let (prices_result, metadata, proof): (TokenDirectPurchasePrices, _, _) = + TokenPricingSchedule::fetch_many_with_metadata_and_proof( + self.as_ref(), + &tokens[..], + None, + ) + .await?; + + // Convert to response format + let responses: Vec = token_ids + .into_iter() + .filter_map(|id_str| { + let id = Identifier::from_string( + &id_str, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ).ok()?; + + prices_result.get(&id).and_then(|price_opt| { + price_opt.as_ref().map(|schedule| { + // Get prices based on the schedule type + let (base_price, current_price) = match &schedule { + dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SinglePrice(price) => { + (price.to_string(), price.to_string()) + }, + dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule::SetPrices(prices) => { + // Use first price as base, last as current + let base = prices.first_key_value() + .map(|(_, p)| p.to_string()) + .unwrap_or_else(|| "0".to_string()); + let current = prices.last_key_value() + .map(|(_, p)| p.to_string()) + .unwrap_or_else(|| "0".to_string()); + (base, current) + }, + }; + + TokenPriceResponse { + token_id: id_str, + current_price, + base_price, + } + }) + }) + }) + .collect(); - let data = if let Some(info) = info_result { - use dash_sdk::dpp::tokens::contract_info::v0::TokenContractInfoV0Accessors; + let response = ProofMetadataResponse { + data: responses, + metadata: metadata.into(), + proof: proof.into(), + }; - // Extract fields based on the enum variant - let (contract_id, position) = match &info { - dash_sdk::dpp::tokens::contract_info::TokenContractInfo::V0(v0) => { - (v0.contract_id(), v0.token_contract_position()) - } + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getTokenContractInfoWithProofInfo")] + pub async fn get_token_contract_info_with_proof_info( + &self, + data_contract_id: &str, + ) -> Result { + use dash_sdk::dpp::tokens::contract_info::TokenContractInfo; + use dash_sdk::platform::Fetch; + + // Parse contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Fetch token contract info with proof + let (info_result, metadata, proof) = + TokenContractInfo::fetch_with_metadata_and_proof(self.as_ref(), contract_id, None) + .await?; + + let data = if let Some(info) = info_result { + use dash_sdk::dpp::tokens::contract_info::v0::TokenContractInfoV0Accessors; + + // Extract fields based on the enum variant + let (contract_id, position) = match &info { + dash_sdk::dpp::tokens::contract_info::TokenContractInfo::V0(v0) => { + (v0.contract_id(), v0.token_contract_position()) + } + }; + + Some(TokenContractInfoResponse { + contract_id: contract_id + .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), + token_contract_position: position, + }) + } else { + None }; - Some(TokenContractInfoResponse { - contract_id: contract_id - .to_string(dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58), - token_contract_position: position, + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - } else { - None - }; - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + } -#[wasm_bindgen] -pub async fn get_token_perpetual_distribution_last_claim_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - token_id: &str, -) -> Result { - use dash_sdk::platform::query::TokenLastClaimQuery; + #[wasm_bindgen(js_name = "getTokenPerpetualDistributionLastClaimWithProofInfo")] + pub async fn get_token_perpetual_distribution_last_claim_with_proof_info( + &self, + identity_id: &str, + token_id: &str, + ) -> Result { + use dash_sdk::platform::query::TokenLastClaimQuery; use dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment; - use dash_sdk::platform::Fetch; - - // Parse IDs - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let token_identifier = Identifier::from_string( - token_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create query - let query = TokenLastClaimQuery { - token_id: token_identifier, - identity_id: identity_identifier, - }; - - // Fetch last claim info with proof - let (claim_result, metadata, proof) = - RewardDistributionMoment::fetch_with_metadata_and_proof(sdk.as_ref(), query, None) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to fetch token perpetual distribution last claim with proof: {}", - e - )) - })?; + use dash_sdk::platform::Fetch; + + // Parse IDs + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + let token_identifier = Identifier::from_string( + token_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid token ID: {}", e)))?; + + // Create query + let query = TokenLastClaimQuery { + token_id: token_identifier, + identity_id: identity_identifier, + }; - let data = if let Some(moment) = claim_result { - // Extract timestamp and block height based on the moment type - // Since we need both timestamp and block height in the response, - // we'll return the moment value and type - let (last_claim_timestamp_ms, last_claim_block_height) = match moment { - dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment::BlockBasedMoment(height) => { - (0, height) // No timestamp available for block-based - }, - dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment::TimeBasedMoment(timestamp) => { - (timestamp, 0) // No block height available for time-based - }, - dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment::EpochBasedMoment(epoch) => { - (0, epoch as u64) // Convert epoch to u64, no timestamp available - }, + // Fetch last claim info with proof + let (claim_result, metadata, proof) = + RewardDistributionMoment::fetch_with_metadata_and_proof(self.as_ref(), query, None) + .await?; + + let data = if let Some(moment) = claim_result { + // Extract timestamp and block height based on the moment type + // Since we need both timestamp and block height in the response, + // we'll return the moment value and type + let (last_claim_timestamp_ms, last_claim_block_height) = match moment { + dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment::BlockBasedMoment(height) => { + (0, height) // No timestamp available for block-based + }, + dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment::TimeBasedMoment(timestamp) => { + (timestamp, 0) // No block height available for time-based + }, + dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment::EpochBasedMoment(epoch) => { + (0, epoch as u64) // Convert epoch to u64, no timestamp available + }, + }; + + Some(LastClaimResponse { + last_claim_timestamp_ms, + last_claim_block_height, + }) + } else { + None }; - Some(LastClaimResponse { - last_claim_timestamp_ms, - last_claim_block_height, + let response = ProofMetadataResponse { + data, + metadata: metadata.into(), + proof: proof.into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) }) - } else { - None - }; - - let response = ProofMetadataResponse { - data, - metadata: metadata.into(), - proof: proof.into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + } } diff --git a/packages/wasm-sdk/src/queries/voting.rs b/packages/wasm-sdk/src/queries/voting.rs index 03e25fd1af4..229b147d175 100644 --- a/packages/wasm-sdk/src/queries/voting.rs +++ b/packages/wasm-sdk/src/queries/voting.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use dapi_grpc::platform::v0::{ get_contested_resource_identity_votes_request::{ @@ -20,887 +21,961 @@ use dash_sdk::RequestSettings; use rs_dapi_client::DapiRequestExecutor; use serde::Serialize; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsValue; // Standard bincode configuration used by Platform use dash_sdk::dpp::bincode; const BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard(); #[wasm_bindgen] -pub async fn get_contested_resources( - sdk: &WasmSdk, - document_type_name: &str, - data_contract_id: &str, - index_name: &str, - start_at_value: Option>, - limit: Option, - _offset: Option, - order_ascending: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse result_type to get start_index_values - // The result_type parameter actually specifies what we want to query - // For contested domain names in DPNS, we query at the "dash" parent domain level - let start_index_values = if index_name == "parentNameAndLabel" && document_type_name == "domain" - { - // For DPNS domains, start at the parent domain level (e.g., "dash") - vec![] // Empty to get all contested resources at any parent domain - } else { - // For other types, may need different index values - vec![] - }; - - // Create start_at_value_info if provided - let start_at_value_info = start_at_value.map(|bytes| { - get_contested_resources_request::get_contested_resources_request_v0::StartAtValueInfo { - start_value: bytes, - start_value_included: true, - } - }); - - // Create the gRPC request directly - let request = GetContestedResourcesRequest { - version: Some(get_contested_resources_request::Version::V0( - GetContestedResourcesRequestV0 { - contract_id: contract_id.to_vec(), - document_type_name: document_type_name.to_string(), - index_name: index_name.to_string(), - start_index_values, - end_index_values: vec![], - start_at_value_info, - count: limit, - order_ascending: order_ascending.unwrap_or(true), - prove: sdk.prove(), - }, - )), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| JsError::new(&format!("Failed to get contested resources: {}", e)))?; - - // For now, return a simple response structure - // The actual response parsing would require the ContestedResource type - let result = serde_json::json!({ - "contestedResources": [], - "metadata": { - "height": response.inner.metadata().ok().map(|m| m.height), - "coreChainLockedHeight": response.inner.metadata().ok().map(|m| m.core_chain_locked_height), - "timeMs": response.inner.metadata().ok().map(|m| m.time_ms), - "protocolVersion": response.inner.metadata().ok().map(|m| m.protocol_version), - } - }); - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - result - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_contested_resource_voters_for_identity( - sdk: &WasmSdk, - contract_id: &str, - document_type_name: &str, - index_name: &str, - index_values: Vec, - contestant_id: &str, - start_at_voter_info: Option, - limit: Option, - order_ascending: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse contestant ID - let contestant_id = Identifier::from_string( - contestant_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Convert JsValue index values to Vec> using bincode serialization - let mut index_values_bytes: Vec> = Vec::new(); - for value in index_values { - if let Some(s) = value.as_string() { - // Create a platform Value from the string - let platform_value = Value::Text(s); - // Serialize using bincode - let serialized = bincode::encode_to_vec(&platform_value, BINCODE_CONFIG) - .map_err(|e| JsError::new(&format!("Failed to serialize index value: {}", e)))?; - index_values_bytes.push(serialized); - } else { - return Err(JsError::new("Index values must be strings")); - } - } - - // Parse start_at_voter_info if provided - let start_at_identifier_info = if let Some(info_str) = start_at_voter_info { - let info: serde_json::Value = serde_json::from_str(&info_str) - .map_err(|e| JsError::new(&format!("Invalid start_at_voter_info JSON: {}", e)))?; - - if let (Some(start_id), Some(included)) = ( - info.get("startIdentifier"), - info.get("startIdentifierIncluded"), - ) { - let start_identifier = start_id - .as_str() - .ok_or_else(|| JsError::new("startIdentifier must be a string"))? - .as_bytes() - .to_vec(); - let start_identifier_included = included.as_bool().unwrap_or(true); - - Some(get_contested_resource_voters_for_identity_request::get_contested_resource_voters_for_identity_request_v0::StartAtIdentifierInfo { - start_identifier, - start_identifier_included, - }) - } else { - None - } - } else { - None - }; - - // Create the gRPC request - let request = GetContestedResourceVotersForIdentityRequest { - version: Some( - get_contested_resource_voters_for_identity_request::Version::V0( - GetContestedResourceVotersForIdentityRequestV0 { +impl WasmSdk { + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getContestedResources")] + pub async fn get_contested_resources( + &self, + document_type_name: &str, + data_contract_id: &str, + index_name: &str, + start_at_value: Option>, + limit: Option, + _offset: Option, + order_ascending: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse result_type to get start_index_values + // The result_type parameter actually specifies what we want to query + // For contested domain names in DPNS, we query at the "dash" parent domain level + let start_index_values = + if index_name == "parentNameAndLabel" && document_type_name == "domain" { + // For DPNS domains, start at the parent domain level (e.g., "dash") + vec![] // Empty to get all contested resources at any parent domain + } else { + // For other types, may need different index values + vec![] + }; + + // Create start_at_value_info if provided + let start_at_value_info = start_at_value.map(|bytes| { + get_contested_resources_request::get_contested_resources_request_v0::StartAtValueInfo { + start_value: bytes, + start_value_included: true, + } + }); + + // Create the gRPC request directly + let request = GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { contract_id: contract_id.to_vec(), document_type_name: document_type_name.to_string(), index_name: index_name.to_string(), - index_values: index_values_bytes, - contestant_id: contestant_id.to_vec(), - start_at_identifier_info, + start_index_values, + end_index_values: vec![], + start_at_value_info, count: limit, order_ascending: order_ascending.unwrap_or(true), - prove: sdk.prove(), + prove: self.prove(), }, - ), - ), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| JsError::new(&format!("Failed to get contested resource voters: {}", e)))?; - - // For now, return a simple response structure - let result = serde_json::json!({ - "voters": [], - "metadata": { - "height": response.inner.metadata().ok().map(|m| m.height), - "coreChainLockedHeight": response.inner.metadata().ok().map(|m| m.core_chain_locked_height), - "timeMs": response.inner.metadata().ok().map(|m| m.time_ms), - "protocolVersion": response.inner.metadata().ok().map(|m| m.protocol_version), - } - }); - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - result - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_contested_resource_identity_votes( - sdk: &WasmSdk, - identity_id: &str, - limit: Option, - start_at_vote_poll_id_info: Option, - order_ascending: Option, -) -> Result { - // TODO: Implement get_contested_resource_identity_votes - // This function should return all votes made by a specific identity - let _ = ( - sdk, - identity_id, - limit, - start_at_vote_poll_id_info, - order_ascending, - ); - - // Return empty result for now - let result = serde_json::json!({ - "votes": [], - "metadata": {} - }); - - serde_wasm_bindgen::to_value(&result) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_vote_polls_by_end_date( - sdk: &WasmSdk, - start_time_info: Option, - end_time_info: Option, - limit: Option, - order_ascending: Option, -) -> Result { - // TODO: Implement get_vote_polls_by_end_date - // This function should return vote polls filtered by end date - let _ = (sdk, start_time_info, end_time_info, limit, order_ascending); - - // Return empty result for now - let result = serde_json::json!({ - "votePolls": [], - "metadata": {} - }); - - serde_wasm_bindgen::to_value(&result) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -// Proof info versions for voting queries + )), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!("Failed to get contested resources: {}", e)) + })?; + + // For now, return a simple response structure + // The actual response parsing would require the ContestedResource type + let result = serde_json::json!({ + "contestedResources": [], + "metadata": { + "height": response.inner.metadata().ok().map(|m| m.height), + "coreChainLockedHeight": response.inner.metadata().ok().map(|m| m.core_chain_locked_height), + "timeMs": response.inner.metadata().ok().map(|m| m.time_ms), + "protocolVersion": response.inner.metadata().ok().map(|m| m.protocol_version), + } + }); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + result.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_contested_resources_with_proof_info( - sdk: &WasmSdk, - document_type_name: &str, - data_contract_id: &str, - index_name: &str, - start_at_value: Option>, - limit: Option, - _offset: Option, - order_ascending: Option, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Parse contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse result_type to get start_index_values - // The result_type parameter actually specifies what we want to query - // For contested domain names in DPNS, we query at the "dash" parent domain level - let start_index_values = if index_name == "parentNameAndLabel" && document_type_name == "domain" - { - // For DPNS domains, start at the parent domain level (e.g., "dash") - vec![] // Empty to get all contested resources at any parent domain - } else { - // For other types, may need different index values - vec![] - }; - - // Create start_at_value_info if provided - let start_at_value_info = start_at_value.map(|bytes| { - get_contested_resources_request::get_contested_resources_request_v0::StartAtValueInfo { - start_value: bytes, - start_value_included: true, + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getContestedResourceVotersForIdentity")] + pub async fn get_contested_resource_voters_for_identity( + &self, + contract_id: &str, + document_type_name: &str, + index_name: &str, + index_values: Vec, + contestant_id: &str, + start_at_voter_info: Option, + limit: Option, + order_ascending: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse contestant ID + let contestant_id = Identifier::from_string( + contestant_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contestant ID: {}", e)))?; + + // Convert JsValue index values to Vec> using bincode serialization + let mut index_values_bytes: Vec> = Vec::new(); + for value in index_values { + if let Some(s) = value.as_string() { + // Create a platform Value from the string + let platform_value = Value::Text(s); + // Serialize using bincode + let serialized = + bincode::encode_to_vec(&platform_value, BINCODE_CONFIG).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to serialize index value: {}", + e + )) + })?; + index_values_bytes.push(serialized); + } else { + return Err(WasmSdkError::invalid_argument( + "Index values must be strings", + )); + } } - }); - - // Create the gRPC request directly - force prove=true for proof info - let request = GetContestedResourcesRequest { - version: Some(get_contested_resources_request::Version::V0( - GetContestedResourcesRequestV0 { - contract_id: contract_id.to_vec(), - document_type_name: document_type_name.to_string(), - index_name: index_name.to_string(), - start_index_values, - end_index_values: vec![], - start_at_value_info, - count: limit, - order_ascending: order_ascending.unwrap_or(true), - prove: true, // Always true for proof info version - }, - )), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to get contested resources with proof: {}", - e - )) - })?; - - // Extract metadata and proof from response - let metadata = response - .inner - .metadata() - .map_err(|e| JsError::new(&format!("Failed to get metadata: {:?}", e)))?; - - let proof = response - .inner - .proof() - .map_err(|e| JsError::new(&format!("Failed to get proof: {:?}", e)))?; - - // For now, return a simple response structure - let data = serde_json::json!({ - "contestedResources": [] - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.clone().into(), - proof: proof.clone().into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} -#[wasm_bindgen] -pub async fn get_contested_resource_vote_state_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, - document_type_name: &str, - index_name: &str, - index_values: Vec, - // TODO: Implement result_type parameter properly - // Currently unused - should map to protobuf ResultType enum: - // - "documents" -> 0 (DOCUMENTS) - // - "vote_tally" -> 1 (VOTE_TALLY) - // - "documents_and_vote_tally" -> 2 (DOCUMENTS_AND_VOTE_TALLY) - // See: https://github.com/dashpay/platform/issues/2760 - _result_type: &str, - allow_include_locked_and_abstaining_vote_tally: Option, - start_at_identifier_info: Option, - count: Option, - _order_ascending: Option, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Parse contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse start_at_identifier_info if provided - let start_at_identifier_info = if let Some(info_str) = start_at_identifier_info { - let info: serde_json::Value = serde_json::from_str(&info_str) - .map_err(|e| JsError::new(&format!("Invalid start_at_identifier_info JSON: {}", e)))?; - - if let (Some(start_id), Some(included)) = ( - info.get("startIdentifier"), - info.get("startIdentifierIncluded"), - ) { - let start_identifier = start_id - .as_str() - .ok_or_else(|| JsError::new("startIdentifier must be a string"))? - .as_bytes() - .to_vec(); - let start_identifier_included = included.as_bool().unwrap_or(true); - - Some(get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo { - start_identifier, - start_identifier_included, - }) + // Parse start_at_voter_info if provided + let start_at_identifier_info = if let Some(info_str) = start_at_voter_info { + let info: serde_json::Value = serde_json::from_str(&info_str).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid start_at_voter_info JSON: {}", e)) + })?; + + if let (Some(start_id), Some(included)) = ( + info.get("startIdentifier"), + info.get("startIdentifierIncluded"), + ) { + let start_identifier = start_id + .as_str() + .ok_or_else(|| { + WasmSdkError::invalid_argument("startIdentifier must be a string") + })? + .as_bytes() + .to_vec(); + let start_identifier_included = included.as_bool().unwrap_or(true); + + Some(get_contested_resource_voters_for_identity_request::get_contested_resource_voters_for_identity_request_v0::StartAtIdentifierInfo { + start_identifier, + start_identifier_included, + }) + } else { + None + } } else { None - } - } else { - None - }; - - // Convert JsValue index values to Vec> using bincode serialization - let mut index_values_bytes: Vec> = Vec::new(); - for value in index_values { - if let Some(s) = value.as_string() { - // Create a platform Value from the string - let platform_value = Value::Text(s); - // Serialize using bincode - let serialized = bincode::encode_to_vec(&platform_value, BINCODE_CONFIG) - .map_err(|e| JsError::new(&format!("Failed to serialize index value: {}", e)))?; - index_values_bytes.push(serialized); - } else { - return Err(JsError::new("Index values must be strings")); - } + }; + + // Create the gRPC request + let request = GetContestedResourceVotersForIdentityRequest { + version: Some( + get_contested_resource_voters_for_identity_request::Version::V0( + GetContestedResourceVotersForIdentityRequestV0 { + contract_id: contract_id.to_vec(), + document_type_name: document_type_name.to_string(), + index_name: index_name.to_string(), + index_values: index_values_bytes, + contestant_id: contestant_id.to_vec(), + start_at_identifier_info, + count: limit, + order_ascending: order_ascending.unwrap_or(true), + prove: self.prove(), + }, + ), + ), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!("Failed to get contested resource voters: {}", e)) + })?; + + // For now, return a simple response structure + let result = serde_json::json!({ + "voters": [], + "metadata": { + "height": response.inner.metadata().ok().map(|m| m.height), + "coreChainLockedHeight": response.inner.metadata().ok().map(|m| m.core_chain_locked_height), + "timeMs": response.inner.metadata().ok().map(|m| m.time_ms), + "protocolVersion": response.inner.metadata().ok().map(|m| m.protocol_version), + } + }); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + result.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - // Create the gRPC request directly - force prove=true - let request = GetContestedResourceVoteStateRequest { - version: Some(get_contested_resource_vote_state_request::Version::V0( - GetContestedResourceVoteStateRequestV0 { - contract_id: contract_id.to_vec(), - document_type_name: document_type_name.to_string(), - index_name: index_name.to_string(), - index_values: index_values_bytes, - // TODO: This should use the _result_type parameter instead of allow_include_locked_and_abstaining_vote_tally - // Current logic is incorrect - these are independent concerns - result_type: if allow_include_locked_and_abstaining_vote_tally.unwrap_or(false) { - 0 - } else { - 1 - }, - allow_include_locked_and_abstaining_vote_tally: - allow_include_locked_and_abstaining_vote_tally.unwrap_or(false), - start_at_identifier_info, - count, - prove: true, // Always true for proof info version - }, - )), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to get contested resource vote state with proof: {}", - e - )) - })?; - - // Extract metadata and proof from response - let metadata = response - .inner - .metadata() - .map_err(|e| JsError::new(&format!("Failed to get metadata: {:?}", e)))?; - - let proof = response - .inner - .proof() - .map_err(|e| JsError::new(&format!("Failed to get proof: {:?}", e)))?; - - // Return a simple response structure - let data = serde_json::json!({ - "contenders": [], - "abstainVoteTally": null, - "lockVoteTally": null, - "finishedVoteInfo": null - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.clone().into(), - proof: proof.clone().into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[wasm_bindgen(js_name = "getContestedResourceIdentityVotes")] + pub async fn get_contested_resource_identity_votes( + &self, + identity_id: &str, + limit: Option, + start_at_vote_poll_id_info: Option, + order_ascending: Option, + ) -> Result { + // TODO: Implement get_contested_resource_identity_votes + // This function should return all votes made by a specific identity + let _ = ( + self, + identity_id, + limit, + start_at_vote_poll_id_info, + order_ascending, + ); + + // Return empty result for now + let result = serde_json::json!({ + "votes": [], + "metadata": {} + }); + + serde_wasm_bindgen::to_value(&result).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_contested_resource_voters_for_identity_with_proof_info( - sdk: &WasmSdk, - data_contract_id: &str, - document_type_name: &str, - index_name: &str, - index_values: Vec, - contestant_id: &str, - start_at_identifier_info: Option, - count: Option, - order_ascending: Option, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Parse IDs - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - let contestant_identifier = Identifier::from_string( - contestant_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Convert JsValue index values to Vec> using bincode serialization - let mut index_values_bytes: Vec> = Vec::new(); - for value in index_values { - if let Some(s) = value.as_string() { - // Create a platform Value from the string - let platform_value = Value::Text(s); - // Serialize using bincode - let serialized = bincode::encode_to_vec(&platform_value, BINCODE_CONFIG) - .map_err(|e| JsError::new(&format!("Failed to serialize index value: {}", e)))?; - index_values_bytes.push(serialized); - } else { - return Err(JsError::new("Index values must be strings")); - } + #[wasm_bindgen(js_name = "getVotePollsByEndDate")] + pub async fn get_vote_polls_by_end_date( + &self, + start_time_info: Option, + end_time_info: Option, + limit: Option, + order_ascending: Option, + ) -> Result { + // TODO: Implement get_vote_polls_by_end_date + // This function should return vote polls filtered by end date + let _ = (self, start_time_info, end_time_info, limit, order_ascending); + + // Return empty result for now + let result = serde_json::json!({ + "votePolls": [], + "metadata": {} + }); + + serde_wasm_bindgen::to_value(&result).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - // Parse start_at_identifier_info if provided - let start_at_identifier_info = if let Some(info_str) = start_at_identifier_info { - let info: serde_json::Value = serde_json::from_str(&info_str) - .map_err(|e| JsError::new(&format!("Invalid start_at_identifier_info JSON: {}", e)))?; - - if let (Some(start_id), Some(included)) = ( - info.get("startIdentifier"), - info.get("startIdentifierIncluded"), - ) { - let start_identifier = start_id - .as_str() - .ok_or_else(|| JsError::new("startIdentifier must be a string"))? - .as_bytes() - .to_vec(); - let start_identifier_included = included.as_bool().unwrap_or(true); - - Some(get_contested_resource_voters_for_identity_request::get_contested_resource_voters_for_identity_request_v0::StartAtIdentifierInfo { - start_identifier, - start_identifier_included, - }) + // Proof info versions for voting queries + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getContestedResourcesWithProofInfo")] + pub async fn get_contested_resources_with_proof_info( + &self, + document_type_name: &str, + data_contract_id: &str, + index_name: &str, + start_at_value: Option>, + limit: Option, + _offset: Option, + order_ascending: Option, + ) -> Result { + use crate::queries::ProofMetadataResponse; + + // Parse contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse result_type to get start_index_values + // The result_type parameter actually specifies what we want to query + // For contested domain names in DPNS, we query at the "dash" parent domain level + let start_index_values = + if index_name == "parentNameAndLabel" && document_type_name == "domain" { + // For DPNS domains, start at the parent domain level (e.g., "dash") + vec![] // Empty to get all contested resources at any parent domain + } else { + // For other types, may need different index values + vec![] + }; + + // Create start_at_value_info if provided + let start_at_value_info = start_at_value.map(|bytes| { + get_contested_resources_request::get_contested_resources_request_v0::StartAtValueInfo { + start_value: bytes, + start_value_included: true, + } + }); + + // Create the gRPC request directly - force prove=true for proof info + let request = GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: contract_id.to_vec(), + document_type_name: document_type_name.to_string(), + index_name: index_name.to_string(), + start_index_values, + end_index_values: vec![], + start_at_value_info, + count: limit, + order_ascending: order_ascending.unwrap_or(true), + prove: true, // Always true for proof info version + }, + )), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to get contested resources with proof: {}", + e + )) + })?; + + // Extract metadata and proof from response + let metadata = response + .inner + .metadata() + .map_err(|e| WasmSdkError::generic(format!("Failed to get metadata: {:?}", e)))?; + + let proof = response + .inner + .proof() + .map_err(|e| WasmSdkError::generic(format!("Failed to get proof: {:?}", e)))?; + + // For now, return a simple response structure + let data = serde_json::json!({ + "contestedResources": [] + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.clone().into(), + proof: proof.clone().into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getContestedResourceVoteStateWithProofInfo")] + pub async fn get_contested_resource_vote_state_with_proof_info( + &self, + data_contract_id: &str, + document_type_name: &str, + index_name: &str, + index_values: Vec, + // TODO: Implement result_type parameter properly + // Currently unused - should map to protobuf ResultType enum: + // - "documents" -> 0 (DOCUMENTS) + // - "vote_tally" -> 1 (VOTE_TALLY) + // - "documents_and_vote_tally" -> 2 (DOCUMENTS_AND_VOTE_TALLY) + // See: https://github.com/dashpay/platform/issues/2760 + _result_type: &str, + allow_include_locked_and_abstaining_vote_tally: Option, + start_at_identifier_info: Option, + count: Option, + _order_ascending: Option, + ) -> Result { + use crate::queries::ProofMetadataResponse; + + // Parse contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse start_at_identifier_info if provided + let start_at_identifier_info = if let Some(info_str) = start_at_identifier_info { + let info: serde_json::Value = serde_json::from_str(&info_str).map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start_at_identifier_info JSON: {}", + e + )) + })?; + + if let (Some(start_id), Some(included)) = ( + info.get("startIdentifier"), + info.get("startIdentifierIncluded"), + ) { + let start_identifier = start_id + .as_str() + .ok_or_else(|| { + WasmSdkError::invalid_argument("startIdentifier must be a string") + })? + .as_bytes() + .to_vec(); + let start_identifier_included = included.as_bool().unwrap_or(true); + + Some(get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo { + start_identifier, + start_identifier_included, + }) + } else { + None + } } else { None + }; + + // Convert JsValue index values to Vec> using bincode serialization + let mut index_values_bytes: Vec> = Vec::new(); + for value in index_values { + if let Some(s) = value.as_string() { + // Create a platform Value from the string + let platform_value = Value::Text(s); + // Serialize using bincode + let serialized = + bincode::encode_to_vec(&platform_value, BINCODE_CONFIG).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to serialize index value: {}", + e + )) + })?; + index_values_bytes.push(serialized); + } else { + return Err(WasmSdkError::invalid_argument( + "Index values must be strings", + )); + } } - } else { - None - }; - - // Create the gRPC request directly - force prove=true - let request = GetContestedResourceVotersForIdentityRequest { - version: Some( - get_contested_resource_voters_for_identity_request::Version::V0( - GetContestedResourceVotersForIdentityRequestV0 { + + // Create the gRPC request directly - force prove=true + let request = GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { contract_id: contract_id.to_vec(), document_type_name: document_type_name.to_string(), index_name: index_name.to_string(), index_values: index_values_bytes, - contestant_id: contestant_identifier.to_vec(), + // TODO: This should use the _result_type parameter instead of allow_include_locked_and_abstaining_vote_tally + // Current logic is incorrect - these are independent concerns + result_type: if allow_include_locked_and_abstaining_vote_tally.unwrap_or(false) + { + 0 + } else { + 1 + }, + allow_include_locked_and_abstaining_vote_tally: + allow_include_locked_and_abstaining_vote_tally.unwrap_or(false), start_at_identifier_info, count, - order_ascending: order_ascending.unwrap_or(true), prove: true, // Always true for proof info version }, - ), - ), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to get contested resource voters with proof: {}", - e - )) - })?; - - // Extract metadata and proof from response - let metadata = response - .inner - .metadata() - .map_err(|e| JsError::new(&format!("Failed to get metadata: {:?}", e)))?; - - let proof = response - .inner - .proof() - .map_err(|e| JsError::new(&format!("Failed to get proof: {:?}", e)))?; - - // Return a simple response structure - let data = serde_json::json!({ - "voters": [], - "finishedResults": false - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.clone().into(), - proof: proof.clone().into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} - -#[wasm_bindgen] -pub async fn get_contested_resource_identity_votes_with_proof_info( - sdk: &WasmSdk, - identity_id: &str, - limit: Option, - offset: Option, - order_ascending: Option, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Parse identity ID - let identity_identifier = Identifier::from_string( - identity_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Create the gRPC request directly - force prove=true - let request = GetContestedResourceIdentityVotesRequest { - version: Some(get_contested_resource_identity_votes_request::Version::V0( - GetContestedResourceIdentityVotesRequestV0 { - identity_id: identity_identifier.to_vec(), - limit, - offset, - order_ascending: order_ascending.unwrap_or(true), - start_at_vote_poll_id_info: None, - prove: true, // Always true for proof info version - }, - )), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to get contested resource identity votes with proof: {}", - e - )) - })?; - - // Extract metadata and proof from response - let metadata = response - .inner - .metadata() - .map_err(|e| JsError::new(&format!("Failed to get metadata: {:?}", e)))?; - - let proof = response - .inner - .proof() - .map_err(|e| JsError::new(&format!("Failed to get proof: {:?}", e)))?; - - // Return a simple response structure - let data = serde_json::json!({ - "votes": [], - "finishedResults": false - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.clone().into(), - proof: proof.clone().into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + )), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to get contested resource vote state with proof: {}", + e + )) + })?; + + // Extract metadata and proof from response + let metadata = response + .inner + .metadata() + .map_err(|e| WasmSdkError::generic(format!("Failed to get metadata: {:?}", e)))?; + + let proof = response + .inner + .proof() + .map_err(|e| WasmSdkError::generic(format!("Failed to get proof: {:?}", e)))?; + + // Return a simple response structure + let data = serde_json::json!({ + "contenders": [], + "abstainVoteTally": null, + "lockVoteTally": null, + "finishedVoteInfo": null + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.clone().into(), + proof: proof.clone().into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } -#[wasm_bindgen] -pub async fn get_vote_polls_by_end_date_with_proof_info( - sdk: &WasmSdk, - start_time_ms: Option, - end_time_ms: Option, - limit: Option, - offset: Option, - order_ascending: Option, -) -> Result { - use crate::queries::ProofMetadataResponse; - - // Note: GetVotePollsByEndDateRequestV0 doesn't have start_at_poll_info, only offset - - // Create the gRPC request directly - force prove=true - let request = GetVotePollsByEndDateRequest { - version: Some(get_vote_polls_by_end_date_request::Version::V0( - GetVotePollsByEndDateRequestV0 { - start_time_info: start_time_ms.map(|ms| { - get_vote_polls_by_end_date_request::get_vote_polls_by_end_date_request_v0::StartAtTimeInfo { - start_time_ms: ms, - start_time_included: true, - } - }), - end_time_info: end_time_ms.map(|ms| { - get_vote_polls_by_end_date_request::get_vote_polls_by_end_date_request_v0::EndAtTimeInfo { - end_time_ms: ms, - end_time_included: true, - } - }), - limit, - offset, - ascending: order_ascending.unwrap_or(true), - prove: true, // Always true for proof info version - }, - )), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to get vote polls by end date with proof: {}", - e - )) - })?; - - // Extract metadata and proof from response - let metadata = response - .inner - .metadata() - .map_err(|e| JsError::new(&format!("Failed to get metadata: {:?}", e)))?; - - let proof = response - .inner - .proof() - .map_err(|e| JsError::new(&format!("Failed to get proof: {:?}", e)))?; - - // Return a simple response structure - let data = serde_json::json!({ - "votePollsByTimestamps": {}, - "finishedResults": false - }); - - let response = ProofMetadataResponse { - data, - metadata: metadata.clone().into(), - proof: proof.clone().into(), - }; - - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - response - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) -} + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getContestedResourceVotersForIdentityWithProofInfo")] + pub async fn get_contested_resource_voters_for_identity_with_proof_info( + &self, + data_contract_id: &str, + document_type_name: &str, + index_name: &str, + index_values: Vec, + contestant_id: &str, + start_at_identifier_info: Option, + count: Option, + order_ascending: Option, + ) -> Result { + use crate::queries::ProofMetadataResponse; + + // Parse IDs + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + let contestant_identifier = Identifier::from_string( + contestant_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contestant ID: {}", e)))?; + + // Convert JsValue index values to Vec> using bincode serialization + let mut index_values_bytes: Vec> = Vec::new(); + for value in index_values { + if let Some(s) = value.as_string() { + // Create a platform Value from the string + let platform_value = Value::Text(s); + // Serialize using bincode + let serialized = + bincode::encode_to_vec(&platform_value, BINCODE_CONFIG).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to serialize index value: {}", + e + )) + })?; + index_values_bytes.push(serialized); + } else { + return Err(WasmSdkError::invalid_argument( + "Index values must be strings", + )); + } + } -#[wasm_bindgen] -pub async fn get_contested_resource_vote_state( - sdk: &WasmSdk, - data_contract_id: &str, - document_type_name: &str, - index_name: &str, - index_values: Vec, - // TODO: Implement result_type parameter properly - // Currently unused - should map to protobuf ResultType enum: - // - "documents" -> 0 (DOCUMENTS) - // - "vote_tally" -> 1 (VOTE_TALLY) - // - "documents_and_vote_tally" -> 2 (DOCUMENTS_AND_VOTE_TALLY) - // See: https://github.com/dashpay/platform/issues/2760 - _result_type: &str, - allow_include_locked_and_abstaining_vote_tally: Option, - start_at_identifier_info: Option, - count: Option, - _order_ascending: Option, -) -> Result { - // Parse contract ID - let contract_id = Identifier::from_string( - data_contract_id, - dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, - )?; - - // Parse start_at_identifier_info if provided - let start_at_identifier_info = if let Some(info_str) = start_at_identifier_info { - let info: serde_json::Value = serde_json::from_str(&info_str) - .map_err(|e| JsError::new(&format!("Invalid start_at_identifier_info JSON: {}", e)))?; - - if let (Some(start_id), Some(included)) = ( - info.get("startIdentifier"), - info.get("startIdentifierIncluded"), - ) { - let start_identifier = start_id - .as_str() - .ok_or_else(|| JsError::new("startIdentifier must be a string"))? - .as_bytes() - .to_vec(); - let start_identifier_included = included.as_bool().unwrap_or(true); - - Some(get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo { - start_identifier, - start_identifier_included, - }) + // Parse start_at_identifier_info if provided + let start_at_identifier_info = if let Some(info_str) = start_at_identifier_info { + let info: serde_json::Value = serde_json::from_str(&info_str).map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start_at_identifier_info JSON: {}", + e + )) + })?; + + if let (Some(start_id), Some(included)) = ( + info.get("startIdentifier"), + info.get("startIdentifierIncluded"), + ) { + let start_identifier = start_id + .as_str() + .ok_or_else(|| { + WasmSdkError::invalid_argument("startIdentifier must be a string") + })? + .as_bytes() + .to_vec(); + let start_identifier_included = included.as_bool().unwrap_or(true); + + Some(get_contested_resource_voters_for_identity_request::get_contested_resource_voters_for_identity_request_v0::StartAtIdentifierInfo { + start_identifier, + start_identifier_included, + }) + } else { + None + } } else { None - } - } else { - None - }; - - // Convert JsValue index values to Vec> using bincode serialization - let mut index_values_bytes: Vec> = Vec::new(); - for value in index_values { - if let Some(s) = value.as_string() { - // Create a platform Value from the string - let platform_value = Value::Text(s); - // Serialize using bincode - let serialized = bincode::encode_to_vec(&platform_value, BINCODE_CONFIG) - .map_err(|e| JsError::new(&format!("Failed to serialize index value: {}", e)))?; - index_values_bytes.push(serialized); - } else { - return Err(JsError::new("Index values must be strings")); - } + }; + + // Create the gRPC request directly - force prove=true + let request = GetContestedResourceVotersForIdentityRequest { + version: Some( + get_contested_resource_voters_for_identity_request::Version::V0( + GetContestedResourceVotersForIdentityRequestV0 { + contract_id: contract_id.to_vec(), + document_type_name: document_type_name.to_string(), + index_name: index_name.to_string(), + index_values: index_values_bytes, + contestant_id: contestant_identifier.to_vec(), + start_at_identifier_info, + count, + order_ascending: order_ascending.unwrap_or(true), + prove: true, // Always true for proof info version + }, + ), + ), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to get contested resource voters with proof: {}", + e + )) + })?; + + // Extract metadata and proof from response + let metadata = response + .inner + .metadata() + .map_err(|e| WasmSdkError::generic(format!("Failed to get metadata: {:?}", e)))?; + + let proof = response + .inner + .proof() + .map_err(|e| WasmSdkError::generic(format!("Failed to get proof: {:?}", e)))?; + + // Return a simple response structure + let data = serde_json::json!({ + "voters": [], + "finishedResults": false + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.clone().into(), + proof: proof.clone().into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[wasm_bindgen(js_name = "getContestedResourceIdentityVotesWithProofInfo")] + pub async fn get_contested_resource_identity_votes_with_proof_info( + &self, + identity_id: &str, + limit: Option, + offset: Option, + order_ascending: Option, + ) -> Result { + use crate::queries::ProofMetadataResponse; + + // Parse identity ID + let identity_identifier = Identifier::from_string( + identity_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; + + // Create the gRPC request directly - force prove=true + let request = GetContestedResourceIdentityVotesRequest { + version: Some(get_contested_resource_identity_votes_request::Version::V0( + GetContestedResourceIdentityVotesRequestV0 { + identity_id: identity_identifier.to_vec(), + limit, + offset, + order_ascending: order_ascending.unwrap_or(true), + start_at_vote_poll_id_info: None, + prove: true, // Always true for proof info version + }, + )), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to get contested resource identity votes with proof: {}", + e + )) + })?; + + // Extract metadata and proof from response + let metadata = response + .inner + .metadata() + .map_err(|e| WasmSdkError::generic(format!("Failed to get metadata: {:?}", e)))?; + + let proof = response + .inner + .proof() + .map_err(|e| WasmSdkError::generic(format!("Failed to get proof: {:?}", e)))?; + + // Return a simple response structure + let data = serde_json::json!({ + "votes": [], + "finishedResults": false + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.clone().into(), + proof: proof.clone().into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) } - // Create the gRPC request directly - let request = GetContestedResourceVoteStateRequest { - version: Some(get_contested_resource_vote_state_request::Version::V0( - GetContestedResourceVoteStateRequestV0 { - contract_id: contract_id.to_vec(), - document_type_name: document_type_name.to_string(), - index_name: index_name.to_string(), - index_values: index_values_bytes, - // TODO: This should use the _result_type parameter instead of allow_include_locked_and_abstaining_vote_tally - // Current logic is incorrect - these are independent concerns - result_type: if allow_include_locked_and_abstaining_vote_tally.unwrap_or(false) { - 0 - } else { - 1 + #[wasm_bindgen(js_name = "getVotePollsByEndDateWithProofInfo")] + pub async fn get_vote_polls_by_end_date_with_proof_info( + &self, + start_time_ms: Option, + end_time_ms: Option, + limit: Option, + offset: Option, + order_ascending: Option, + ) -> Result { + use crate::queries::ProofMetadataResponse; + + // Note: GetVotePollsByEndDateRequestV0 doesn't have start_at_poll_info, only offset + + // Create the gRPC request directly - force prove=true + let request = GetVotePollsByEndDateRequest { + version: Some(get_vote_polls_by_end_date_request::Version::V0( + GetVotePollsByEndDateRequestV0 { + start_time_info: start_time_ms.map(|ms| { + get_vote_polls_by_end_date_request::get_vote_polls_by_end_date_request_v0::StartAtTimeInfo { + start_time_ms: ms, + start_time_included: true, + } + }), + end_time_info: end_time_ms.map(|ms| { + get_vote_polls_by_end_date_request::get_vote_polls_by_end_date_request_v0::EndAtTimeInfo { + end_time_ms: ms, + end_time_included: true, + } + }), + limit, + offset, + ascending: order_ascending.unwrap_or(true), + prove: true, // Always true for proof info version }, - allow_include_locked_and_abstaining_vote_tally: - allow_include_locked_and_abstaining_vote_tally.unwrap_or(false), - start_at_identifier_info, - count, - prove: sdk.prove(), - }, - )), - }; - - // Execute the request - let response = sdk - .as_ref() - .execute(request, RequestSettings::default()) - .await - .map_err(|e| { - JsError::new(&format!( - "Failed to get contested resource vote state: {}", - e - )) - })?; - - // Return a simple response structure - let result = serde_json::json!({ - "contenders": [], - "abstainVoteTally": null, - "lockVoteTally": null, - "finishedVoteInfo": null, - "metadata": { - "height": response.inner.metadata().ok().map(|m| m.height), - "coreChainLockedHeight": response.inner.metadata().ok().map(|m| m.core_chain_locked_height), - "timeMs": response.inner.metadata().ok().map(|m| m.time_ms), - "protocolVersion": response.inner.metadata().ok().map(|m| m.protocol_version), + )), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to get vote polls by end date with proof: {}", + e + )) + })?; + + // Extract metadata and proof from response + let metadata = response + .inner + .metadata() + .map_err(|e| WasmSdkError::generic(format!("Failed to get metadata: {:?}", e)))?; + + let proof = response + .inner + .proof() + .map_err(|e| WasmSdkError::generic(format!("Failed to get proof: {:?}", e)))?; + + // Return a simple response structure + let data = serde_json::json!({ + "votePollsByTimestamps": {}, + "finishedResults": false + }); + + let response = ProofMetadataResponse { + data, + metadata: metadata.clone().into(), + proof: proof.clone().into(), + }; + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + response.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } + + #[allow(clippy::too_many_arguments)] + #[wasm_bindgen(js_name = "getContestedResourceVoteState")] + pub async fn get_contested_resource_vote_state( + &self, + data_contract_id: &str, + document_type_name: &str, + index_name: &str, + index_values: Vec, + // TODO: Implement result_type parameter properly + // Currently unused - should map to protobuf ResultType enum: + // - "documents" -> 0 (DOCUMENTS) + // - "vote_tally" -> 1 (VOTE_TALLY) + // - "documents_and_vote_tally" -> 2 (DOCUMENTS_AND_VOTE_TALLY) + // See: https://github.com/dashpay/platform/issues/2760 + _result_type: &str, + allow_include_locked_and_abstaining_vote_tally: Option, + start_at_identifier_info: Option, + count: Option, + _order_ascending: Option, + ) -> Result { + // Parse contract ID + let contract_id = Identifier::from_string( + data_contract_id, + dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; + + // Parse start_at_identifier_info if provided + let start_at_identifier_info = if let Some(info_str) = start_at_identifier_info { + let info: serde_json::Value = serde_json::from_str(&info_str).map_err(|e| { + WasmSdkError::invalid_argument(format!( + "Invalid start_at_identifier_info JSON: {}", + e + )) + })?; + + if let (Some(start_id), Some(included)) = ( + info.get("startIdentifier"), + info.get("startIdentifierIncluded"), + ) { + let start_identifier = start_id + .as_str() + .ok_or_else(|| { + WasmSdkError::invalid_argument("startIdentifier must be a string") + })? + .as_bytes() + .to_vec(); + let start_identifier_included = included.as_bool().unwrap_or(true); + + Some(get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::StartAtIdentifierInfo { + start_identifier, + start_identifier_included, + }) + } else { + None + } + } else { + None + }; + + // Convert JsValue index values to Vec> using bincode serialization + let mut index_values_bytes: Vec> = Vec::new(); + for value in index_values { + if let Some(s) = value.as_string() { + // Create a platform Value from the string + let platform_value = Value::Text(s); + // Serialize using bincode + let serialized = + bincode::encode_to_vec(&platform_value, BINCODE_CONFIG).map_err(|e| { + WasmSdkError::serialization(format!( + "Failed to serialize index value: {}", + e + )) + })?; + index_values_bytes.push(serialized); + } else { + return Err(WasmSdkError::invalid_argument( + "Index values must be strings", + )); + } } - }); - // Use json_compatible serializer - let serializer = serde_wasm_bindgen::Serializer::json_compatible(); - result - .serialize(&serializer) - .map_err(|e| JsError::new(&format!("Failed to serialize response: {}", e))) + // Create the gRPC request directly + let request = GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { + contract_id: contract_id.to_vec(), + document_type_name: document_type_name.to_string(), + index_name: index_name.to_string(), + index_values: index_values_bytes, + // TODO: This should use the _result_type parameter instead of allow_include_locked_and_abstaining_vote_tally + // Current logic is incorrect - these are independent concerns + result_type: if allow_include_locked_and_abstaining_vote_tally.unwrap_or(false) + { + 0 + } else { + 1 + }, + allow_include_locked_and_abstaining_vote_tally: + allow_include_locked_and_abstaining_vote_tally.unwrap_or(false), + start_at_identifier_info, + count, + prove: self.prove(), + }, + )), + }; + + // Execute the request + let response = self + .as_ref() + .execute(request, RequestSettings::default()) + .await + .map_err(|e| { + WasmSdkError::generic(format!( + "Failed to get contested resource vote state: {}", + e + )) + })?; + + // Return a simple response structure + let result = serde_json::json!({ + "contenders": [], + "abstainVoteTally": null, + "lockVoteTally": null, + "finishedVoteInfo": null, + "metadata": { + "height": response.inner.metadata().ok().map(|m| m.height), + "coreChainLockedHeight": response.inner.metadata().ok().map(|m| m.core_chain_locked_height), + "timeMs": response.inner.metadata().ok().map(|m| m.time_ms), + "protocolVersion": response.inner.metadata().ok().map(|m| m.protocol_version), + } + }); + + // Use json_compatible serializer + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + result.serialize(&serializer).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize response: {}", e)) + }) + } } diff --git a/packages/wasm-sdk/src/sdk.rs b/packages/wasm-sdk/src/sdk.rs index 5db5c01192c..0f6a3f56dcc 100644 --- a/packages/wasm-sdk/src/sdk.rs +++ b/packages/wasm-sdk/src/sdk.rs @@ -1,30 +1,22 @@ use crate::context_provider::WasmContext; -use dash_sdk::dpp::block::extended_epoch_info::ExtendedEpochInfo; -use dash_sdk::dpp::dashcore::{Network, PrivateKey}; -use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; -use dash_sdk::dpp::data_contract::DataContractFactory; -use dash_sdk::dpp::document::serialization_traits::DocumentPlatformConversionMethodsV0; -use dash_sdk::dpp::identity::signer::Signer; -use dash_sdk::dpp::identity::IdentityV0; -use dash_sdk::dpp::prelude::AssetLockProof; -use dash_sdk::dpp::serialization::PlatformSerializableWithPlatformVersion; +use crate::error::WasmSdkError; use dash_sdk::dpp::version::PlatformVersion; -use dash_sdk::platform::transition::broadcast::BroadcastStateTransition; -use dash_sdk::platform::transition::put_identity::PutIdentity; -use dash_sdk::platform::{DataContract, Document, DocumentQuery, Fetch, Identifier, Identity}; -use dash_sdk::sdk::AddressList; use dash_sdk::{Sdk, SdkBuilder}; -use platform_value::platform_value; use rs_dapi_client::RequestSettings; -use serde_json; -use std::collections::BTreeMap; -use std::fmt::Debug; use std::ops::{Deref, DerefMut}; -use std::str::FromStr; use std::time::Duration; use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::{JsError, JsValue}; -use web_sys::{console, js_sys}; + +// Store shared trusted contexts +use once_cell::sync::Lazy; +use std::sync::Mutex; + +pub(crate) static MAINNET_TRUSTED_CONTEXT: Lazy< + Mutex>, +> = Lazy::new(|| Mutex::new(None)); +pub(crate) static TESTNET_TRUSTED_CONTEXT: Lazy< + Mutex>, +> = Lazy::new(|| Mutex::new(None)); #[wasm_bindgen] pub struct WasmSdk(Sdk); @@ -63,57 +55,6 @@ impl WasmSdk { pub(crate) fn network(&self) -> dash_sdk::dpp::dashcore::Network { self.0.network } - - /// Test serialization of different object types - #[wasm_bindgen(js_name = testSerialization)] - pub fn test_serialization(&self, test_type: &str) -> Result { - use serde_wasm_bindgen::to_value; - - match test_type { - "simple" => { - let simple = serde_json::json!({ - "type": "simple", - "value": "test" - }); - to_value(&simple) - .map_err(|e| JsValue::from_str(&format!("Simple serialization failed: {}", e))) - } - "complex" => { - let complex = serde_json::json!({ - "type": "complex", - "nested": { - "id": "123", - "number": 42, - "array": [1, 2, 3], - "null_value": null, - "bool_value": true - } - }); - to_value(&complex) - .map_err(|e| JsValue::from_str(&format!("Complex serialization failed: {}", e))) - } - "document" => { - // Simulate the exact structure we're trying to return - let doc = serde_json::json!({ - "type": "DocumentCreated", - "documentId": "8kGVyLBpghr4jBG7nJepKzyo3gyhPLitePxNSSGtbTwj", - "document": { - "id": "8kGVyLBpghr4jBG7nJepKzyo3gyhPLitePxNSSGtbTwj", - "ownerId": "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk", - "dataContractId": "9nzpvjVSStUrhkEs3eNHw2JYpcNoLh1MjmqW45QiyjSa", - "documentType": "post", - "revision": 1, - "createdAt": 1736300191752i64, - "updatedAt": 1736300191752i64, - } - }); - to_value(&doc).map_err(|e| { - JsValue::from_str(&format!("Document serialization failed: {}", e)) - }) - } - _ => Err(JsValue::from_str("Unknown test type")), - } - } } impl WasmSdk { @@ -123,6 +64,45 @@ impl WasmSdk { } } +#[wasm_bindgen] +impl WasmSdk { + #[wasm_bindgen(js_name = "prefetchTrustedQuorumsMainnet")] + pub async fn prefetch_trusted_quorums_mainnet() -> Result<(), WasmSdkError> { + use crate::context_provider::WasmTrustedContext; + + let trusted_context = WasmTrustedContext::new_mainnet() + .map_err(|e| WasmSdkError::from(dash_sdk::Error::from(e)))?; + + trusted_context + .prefetch_quorums() + .await + .map_err(|e| WasmSdkError::from(dash_sdk::Error::from(e)))?; + + // Store the context for later use + *MAINNET_TRUSTED_CONTEXT.lock().unwrap() = Some(trusted_context); + + Ok(()) + } + + #[wasm_bindgen(js_name = "prefetchTrustedQuorumsTestnet")] + pub async fn prefetch_trusted_quorums_testnet() -> Result<(), WasmSdkError> { + use crate::context_provider::WasmTrustedContext; + + let trusted_context = WasmTrustedContext::new_testnet() + .map_err(|e| WasmSdkError::from(dash_sdk::Error::from(e)))?; + + trusted_context + .prefetch_quorums() + .await + .map_err(|e| WasmSdkError::from(dash_sdk::Error::from(e)))?; + + // Store the context for later use + *TESTNET_TRUSTED_CONTEXT.lock().unwrap() = Some(trusted_context); + + Ok(()) + } +} + #[wasm_bindgen] pub struct WasmSdkBuilder(SdkBuilder); @@ -146,6 +126,8 @@ impl WasmSdkBuilder { pub fn get_latest_version_number() -> u32 { PlatformVersion::latest().protocol_version } + + #[wasm_bindgen(js_name = "mainnet")] pub fn new_mainnet() -> Self { // Mainnet addresses from mnowatch.org let mainnet_addresses = vec![ @@ -369,7 +351,8 @@ impl WasmSdkBuilder { Self(sdk_builder) } - pub fn new_mainnet_trusted() -> Result { + #[wasm_bindgen(js_name = "mainnetTrusted")] + pub fn new_mainnet_trusted() -> Result { use crate::context_provider::WasmTrustedContext; // Use the cached context if available, otherwise create a new one @@ -379,9 +362,8 @@ impl WasmSdkBuilder { } .map(Ok) .unwrap_or_else(|| { - WasmTrustedContext::new_mainnet().map_err(|e| { - JsError::new(&format!("Failed to create mainnet trusted context: {}", e)) - }) + WasmTrustedContext::new_mainnet() + .map_err(|e| WasmSdkError::from(dash_sdk::Error::from(e))) })?; // Mainnet addresses from mnowatch.org @@ -606,6 +588,7 @@ impl WasmSdkBuilder { Ok(Self(sdk_builder)) } + #[wasm_bindgen(js_name = "testnet")] pub fn new_testnet() -> Self { // Testnet addresses from https://quorums.testnet.networks.dash.org/masternodes // Using HTTPS endpoints for ENABLED nodes with successful version checks @@ -628,7 +611,8 @@ impl WasmSdkBuilder { Self(sdk_builder) } - pub fn new_testnet_trusted() -> Result { + #[wasm_bindgen(js_name = "testnetTrusted")] + pub fn new_testnet_trusted() -> Result { use crate::context_provider::WasmTrustedContext; // Use the cached context if available, otherwise create a new one @@ -638,9 +622,8 @@ impl WasmSdkBuilder { } .map(Ok) .unwrap_or_else(|| { - WasmTrustedContext::new_testnet().map_err(|e| { - JsError::new(&format!("Failed to create testnet trusted context: {}", e)) - }) + WasmTrustedContext::new_testnet() + .map_err(|e| WasmSdkError::from(dash_sdk::Error::from(e))) })?; // Testnet addresses from https://quorums.testnet.networks.dash.org/masternodes @@ -664,10 +647,11 @@ impl WasmSdkBuilder { Ok(Self(sdk_builder)) } - pub fn build(self) -> Result { - Ok(WasmSdk(self.0.build()?)) + pub fn build(self) -> Result { + self.0.build().map(WasmSdk).map_err(WasmSdkError::from) } + #[wasm_bindgen(js_name = "withContextProvider")] pub fn with_context_provider(self, context_provider: WasmContext) -> Self { WasmSdkBuilder(self.0.with_context_provider(context_provider)) } @@ -680,9 +664,10 @@ impl WasmSdkBuilder { /// - ... up to latest version /// /// Defaults to latest version if not specified. - pub fn with_version(self, version_number: u32) -> Result { + #[wasm_bindgen(js_name = "withVersion")] + pub fn with_version(self, version_number: u32) -> Result { let version = PlatformVersion::get(version_number).map_err(|e| { - JsError::new(&format!( + WasmSdkError::invalid_argument(format!( "Invalid platform version {}: {}", version_number, e )) @@ -698,6 +683,7 @@ impl WasmSdkBuilder { /// - timeout_ms: Timeout for single request (in milliseconds) /// - retries: Number of retries in case of failed requests /// - ban_failed_address: Whether to ban DAPI address if node not responded or responded with error + #[wasm_bindgen(js_name = "withSettings")] pub fn with_settings( self, connect_timeout_ms: Option, @@ -726,153 +712,31 @@ impl WasmSdkBuilder { WasmSdkBuilder(self.0.with_settings(settings)) } - // TODO: Add with_proofs method when it's available in the SDK builder - // pub fn with_proofs(self, enable_proofs: bool) -> Self { - // WasmSdkBuilder(self.0.with_proofs(enable_proofs)) - // } -} - -// Store shared trusted contexts -use once_cell::sync::Lazy; -use std::sync::Mutex; - -pub(crate) static MAINNET_TRUSTED_CONTEXT: Lazy< - Mutex>, -> = Lazy::new(|| Mutex::new(None)); -pub(crate) static TESTNET_TRUSTED_CONTEXT: Lazy< - Mutex>, -> = Lazy::new(|| Mutex::new(None)); - -#[wasm_bindgen] -pub async fn prefetch_trusted_quorums_mainnet() -> Result<(), JsError> { - use crate::context_provider::WasmTrustedContext; - - let trusted_context = WasmTrustedContext::new_mainnet() - .map_err(|e| JsError::new(&format!("Failed to create trusted context: {}", e)))?; - - trusted_context - .prefetch_quorums() - .await - .map_err(|e| JsError::new(&format!("Failed to prefetch quorums: {}", e)))?; - - // Store the context for later use - *MAINNET_TRUSTED_CONTEXT.lock().unwrap() = Some(trusted_context); - - Ok(()) -} - -#[wasm_bindgen] -pub async fn prefetch_trusted_quorums_testnet() -> Result<(), JsError> { - use crate::context_provider::WasmTrustedContext; - - let trusted_context = WasmTrustedContext::new_testnet() - .map_err(|e| JsError::new(&format!("Failed to create trusted context: {}", e)))?; - - trusted_context - .prefetch_quorums() - .await - .map_err(|e| JsError::new(&format!("Failed to prefetch quorums: {}", e)))?; - - // Store the context for later use - *TESTNET_TRUSTED_CONTEXT.lock().unwrap() = Some(trusted_context); - - Ok(()) -} - -// Query functions have been moved to src/queries/ modules - -#[wasm_bindgen] -pub async fn identity_put(sdk: &WasmSdk) { - // This is just a mock implementation to show how to use the SDK and ensure proper linking - // of all required dependencies. This function is not supposed to work. - let id = Identifier::from_bytes(&[0; 32]).expect("create identifier"); - - let identity = Identity::V0(IdentityV0 { - id, - public_keys: BTreeMap::new(), - balance: 0, - revision: 0, - }); - - let asset_lock_proof = AssetLockProof::default(); - let asset_lock_proof_private_key = - PrivateKey::from_byte_array(&[0; 32], Network::Testnet).expect("create private key"); - - let signer = MockSigner; - let _pushed: Identity = identity - .put_to_platform( - sdk, - asset_lock_proof, - &asset_lock_proof_private_key, - &signer, - None, - ) - .await - .expect("put identity") - .broadcast_and_wait(sdk, None) - .await - .unwrap(); + #[wasm_bindgen(js_name = "withProofs")] + pub fn with_proofs(self, enable_proofs: bool) -> Self { + WasmSdkBuilder(self.0.with_proofs(enable_proofs)) + } } #[wasm_bindgen] -pub async fn epoch_testing() { - let sdk = SdkBuilder::new(AddressList::new()) - .build() - .expect("build sdk"); - - let _ei = ExtendedEpochInfo::fetch(&sdk, 0) - .await - .expect("fetch extended epoch info") - .expect("extended epoch info not found"); +impl WasmSdk { + /// Configure tracing/logging level or filter (static, global) + /// + /// Accepts simple levels: "off", "error", "warn", "info", "debug", "trace" + /// or a full EnvFilter string like: "wasm_sdk=debug,rs_dapi_client=warn" + #[wasm_bindgen(js_name = "setLogLevel")] + pub fn set_log_level(level_or_filter: &str) -> Result<(), WasmSdkError> { + crate::logging::set_log_level(level_or_filter) + } } #[wasm_bindgen] -pub async fn docs_testing(sdk: &WasmSdk) { - let id = Identifier::random(); - - let factory = DataContractFactory::new(1).expect("create data contract factory"); - factory - .create(id, 1, platform_value!({}), None, None) - .expect("create data contract"); - - let dc = DataContract::fetch(sdk, id) - .await - .expect("fetch data contract") - .expect("data contract not found"); - - let dcs = dc - .serialize_to_bytes_with_platform_version(sdk.0.version()) - .expect("serialize data contract"); - - let query = DocumentQuery::new(dc.clone(), "asd").expect("create query"); - let doc = Document::fetch(sdk, query) - .await - .expect("fetch document") - .expect("document not found"); - - let document_type = dc - .document_type_for_name("aaa") - .expect("document type for name"); - let doc_serialized = doc - .serialize(document_type, &dc, sdk.0.version()) - .expect("serialize document"); - - let msg = js_sys::JsString::from_str(&format!("{:?} {:?} ", dcs, doc_serialized)) - .expect("create js string"); - console::log_1(&msg); -} - -#[derive(Clone, Debug)] -struct MockSigner; -impl Signer for MockSigner { - fn can_sign_with(&self, _identity_public_key: &dash_sdk::platform::IdentityPublicKey) -> bool { - true - } - fn sign( - &self, - _identity_public_key: &dash_sdk::platform::IdentityPublicKey, - _data: &[u8], - ) -> Result { - todo!("signature creation is not implemented due to lack of dash platform wallet support in wasm") +impl WasmSdkBuilder { + /// Configure tracing/logging via the builder + /// Returns a new builder with logging configured + #[wasm_bindgen(js_name = "withLogs")] + pub fn with_logs(self, level_or_filter: &str) -> Result { + crate::logging::set_log_level(level_or_filter)?; + Ok(self) } } diff --git a/packages/wasm-sdk/src/state_transitions/contracts/mod.rs b/packages/wasm-sdk/src/state_transitions/contracts/mod.rs index 5e1dc8afdb4..e47dc2d6c00 100644 --- a/packages/wasm-sdk/src/state_transitions/contracts/mod.rs +++ b/packages/wasm-sdk/src/state_transitions/contracts/mod.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; use dash_sdk::dpp::data_contract::conversion::json::DataContractJsonConversionMethodsV0; @@ -38,33 +39,35 @@ impl WasmSdk { contract_definition: String, private_key_wif: String, key_id: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse owner identifier let owner_identifier = Identifier::from_string(&owner_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid owner ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid owner ID: {}", e)))?; // Parse contract definition JSON - let contract_json: serde_json::Value = serde_json::from_str(&contract_definition) - .map_err(|e| JsValue::from_str(&format!("Invalid contract definition JSON: {}", e)))?; + let contract_json: serde_json::Value = + serde_json::from_str(&contract_definition).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid contract definition JSON: {}", e)) + })?; // Fetch owner identity let owner_identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch owner identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Owner identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Owner identity not found"))?; // Parse private key and find matching public key let private_key_bytes = dash_sdk::dpp::dashcore::PrivateKey::from_wif(&private_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))? + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))? .inner .secret_bytes(); let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); - let secret_key = - dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice(&private_key_bytes) - .map_err(|e| JsValue::from_str(&format!("Invalid secret key: {}", e)))?; + let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( + &private_key_bytes, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize(); @@ -89,7 +92,7 @@ impl WasmSdk { && key.data().as_slice() == public_key_hash160.as_slice() }) .ok_or_else(|| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Key with ID {} not found or doesn't match private key", requested_key_id )) @@ -107,7 +110,7 @@ impl WasmSdk { }) .map(|(_, key)| key.clone()) .ok_or_else(|| { - JsValue::from_str( + WasmSdkError::not_found( "No matching authentication key found for the provided private key", ) })? @@ -120,18 +123,20 @@ impl WasmSdk { sdk.version(), ) .map_err(|e| { - JsValue::from_str(&format!("Failed to create data contract from JSON: {}", e)) + WasmSdkError::invalid_argument(format!( + "Failed to create data contract from JSON: {}", + e + )) })?; // Create signer let signer = SingleKeySigner::from_string(&private_key_wif, self.network()) - .map_err(|e| JsValue::from_str(&e))?; + .map_err(WasmSdkError::invalid_argument)?; // Create and broadcast the contract let created_contract = data_contract .put_to_platform_and_wait_for_response(&sdk, matching_key, &signer, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to create contract: {}", e)))?; + .await?; // Create JavaScript result object let result_obj = js_sys::Object::new(); @@ -141,7 +146,7 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; // Convert contract ID to base58 let contract_id_base58 = created_contract.id().to_string(Encoding::Base58); @@ -150,21 +155,21 @@ impl WasmSdk { &JsValue::from_str("contractId"), &JsValue::from_str(&contract_id_base58), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set contractId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set contractId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("ownerId"), &JsValue::from_str(&owner_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set ownerId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set ownerId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("version"), &JsValue::from_f64(created_contract.version() as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set version: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set version: {:?}", e)))?; // Add document type names let schema = created_contract.document_types(); @@ -177,14 +182,14 @@ impl WasmSdk { &JsValue::from_str("documentTypes"), &doc_types_array, ) - .map_err(|e| JsValue::from_str(&format!("Failed to set documentTypes: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set documentTypes: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Data contract created successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } @@ -210,47 +215,50 @@ impl WasmSdk { contract_updates: String, private_key_wif: String, key_id: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers let contract_identifier = Identifier::from_string(&contract_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid contract ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; let owner_identifier = Identifier::from_string(&owner_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid owner ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid owner ID: {}", e)))?; // Parse contract updates JSON - let updates_json: serde_json::Value = serde_json::from_str(&contract_updates) - .map_err(|e| JsValue::from_str(&format!("Invalid contract updates JSON: {}", e)))?; + let updates_json: serde_json::Value = + serde_json::from_str(&contract_updates).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid contract updates JSON: {}", e)) + })?; // Fetch the existing contract let existing_contract = DataContract::fetch(&sdk, contract_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch contract: {}", e)))? - .ok_or_else(|| JsValue::from_str("Contract not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Contract not found"))?; // Verify ownership if existing_contract.owner_id() != owner_identifier { - return Err(JsValue::from_str("Identity does not own this contract")); + return Err(WasmSdkError::invalid_argument( + "Identity does not own this contract", + )); } // Fetch owner identity let owner_identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch owner identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Owner identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Owner identity not found"))?; // Parse private key and find matching public key let private_key_bytes = dash_sdk::dpp::dashcore::PrivateKey::from_wif(&private_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))? + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))? .inner .secret_bytes(); let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); - let secret_key = - dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice(&private_key_bytes) - .map_err(|e| JsValue::from_str(&format!("Invalid secret key: {}", e)))?; + let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( + &private_key_bytes, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize(); @@ -275,7 +283,7 @@ impl WasmSdk { && key.data().as_slice() == public_key_hash160.as_slice() }) .ok_or_else(|| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Key with ID {} not found or doesn't match private key", requested_key_id )) @@ -293,7 +301,7 @@ impl WasmSdk { }) .map(|(_, key)| key.clone()) .ok_or_else(|| { - JsValue::from_str( + WasmSdkError::not_found( "No matching authentication key found for the provided private key", ) })? @@ -307,7 +315,7 @@ impl WasmSdk { sdk.version(), ) .map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::invalid_argument(format!( "Failed to create updated contract from JSON: {}", e )) @@ -315,7 +323,7 @@ impl WasmSdk { // Verify the version was incremented if updated_contract.version() <= existing_contract.version() { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Contract version must be incremented. Current: {}, Provided: {}", existing_contract.version(), updated_contract.version() @@ -325,10 +333,7 @@ impl WasmSdk { // Get identity contract nonce (contract updates use per-contract nonces) let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_identifier, contract_identifier, true, None) - .await - .map_err(|e| { - JsValue::from_str(&format!("Failed to get identity contract nonce: {}", e)) - })?; + .await?; // Create partial identity for signing let partial_identity = dash_sdk::dpp::identity::PartialIdentity { @@ -341,7 +346,7 @@ impl WasmSdk { // Create signer let signer = SingleKeySigner::from_string(&private_key_wif, self.network()) - .map_err(|e| JsValue::from_str(&e))?; + .map_err(WasmSdkError::invalid_argument)?; // Create the update transition let state_transition = DataContractUpdateTransition::new_from_data_contract( @@ -354,14 +359,13 @@ impl WasmSdk { sdk.version(), None, ) - .map_err(|e| JsValue::from_str(&format!("Failed to create update transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create update transition: {}", e)))?; // Broadcast the transition use dash_sdk::dpp::state_transition::proof_result::StateTransitionProofResult; let result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast update: {}", e)))?; + .await?; // Extract updated contract from result let updated_version = match result { @@ -377,25 +381,25 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("contractId"), &JsValue::from_str(&contract_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set contractId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set contractId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("version"), &JsValue::from_f64(updated_version as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set version: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set version: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Data contract updated successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } diff --git a/packages/wasm-sdk/src/state_transitions/documents/mod.rs b/packages/wasm-sdk/src/state_transitions/documents/mod.rs index 643fc285752..7711a46ede3 100644 --- a/packages/wasm-sdk/src/state_transitions/documents/mod.rs +++ b/packages/wasm-sdk/src/state_transitions/documents/mod.rs @@ -2,6 +2,7 @@ //! //! This module provides WASM bindings for document operations like create, replace, delete, etc. +use crate::error::WasmSdkError; use crate::sdk::{WasmSdk, MAINNET_TRUSTED_CONTEXT, TESTNET_TRUSTED_CONTEXT}; use dash_sdk::dpp::dashcore::PrivateKey; use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; @@ -27,7 +28,6 @@ use serde_json; use simple_signer::SingleKeySigner; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; -use web_sys; // WasmSigner has been replaced with SingleKeySigner from simple-signer crate @@ -38,17 +38,17 @@ impl WasmSdk { contract_id_str: &str, owner_id_str: &str, doc_id_str: Option<&str>, - ) -> Result<(Identifier, Identifier, Option), JsValue> { + ) -> Result<(Identifier, Identifier, Option), WasmSdkError> { let contract_id = Identifier::from_string(contract_id_str, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid contract ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; let owner_id = Identifier::from_string(owner_id_str, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid owner ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid owner ID: {}", e)))?; let doc_id = doc_id_str .map(|id| Identifier::from_string(id, Encoding::Base58)) .transpose() - .map_err(|e| JsValue::from_str(&format!("Invalid document ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid document ID: {}", e)))?; Ok((contract_id, owner_id, doc_id)) } @@ -57,13 +57,12 @@ impl WasmSdk { async fn fetch_and_cache_contract( &self, contract_id: Identifier, - ) -> Result { + ) -> Result { // Fetch from network let sdk = self.inner_clone(); let contract = dash_sdk::platform::DataContract::fetch(&sdk, contract_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch data contract: {}", e)))? - .ok_or_else(|| JsValue::from_str("Data contract not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found"))?; // Cache the contract in the trusted context if self.network() == dash_sdk::dpp::dashcore::Network::Testnet { @@ -83,16 +82,17 @@ impl WasmSdk { pub(crate) fn find_authentication_key<'a>( identity: &'a dash_sdk::platform::Identity, private_key_wif: &str, - ) -> Result<(u32, &'a IdentityPublicKey), JsValue> { + ) -> Result<(u32, &'a IdentityPublicKey), WasmSdkError> { // Derive public key from private key let private_key = PrivateKey::from_wif(private_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))?; let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); let private_key_bytes = private_key.inner.secret_bytes(); - let secret_key = - dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice(&private_key_bytes) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))?; + let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( + &private_key_bytes, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize().to_vec(); @@ -106,14 +106,12 @@ impl WasmSdk { }; // Log debug information - web_sys::console::log_1(&JsValue::from_str(&format!( - "Looking for authentication key with public key: {}", - hex::encode(&public_key_bytes) - ))); - web_sys::console::log_1(&JsValue::from_str(&format!( - "Public key hash160: {}", - hex::encode(&public_key_hash160) - ))); + tracing::debug!( + target = "wasm_sdk", + pubkey = %hex::encode(&public_key_bytes), + hash160 = %hex::encode(&public_key_hash160), + "Looking for authentication key" + ); // Find matching authentication key let (key_id, matching_key) = identity @@ -135,17 +133,18 @@ impl WasmSdk { }; if matches { - web_sys::console::log_1(&JsValue::from_str(&format!( - "Found matching key: ID={}, Type={:?}", - key.id(), - key.key_type() - ))); + tracing::debug!( + target = "wasm_sdk", + id = key.id(), + key_type = ?key.key_type(), + "Found matching key" + ); } matches }) .ok_or_else(|| { - JsValue::from_str( + WasmSdkError::not_found( "No matching authentication key found for the provided private key", ) })?; @@ -157,8 +156,9 @@ impl WasmSdk { pub(crate) fn create_signer_from_wif( private_key_wif: &str, network: dash_sdk::dpp::dashcore::Network, - ) -> Result { - SingleKeySigner::from_string(private_key_wif, network).map_err(|e| JsValue::from_str(&e)) + ) -> Result { + SingleKeySigner::from_string(private_key_wif, network) + .map_err(WasmSdkError::invalid_argument) } /// Build JavaScript result object for state transition results @@ -166,7 +166,7 @@ impl WasmSdk { transition_type: &str, document_id: &str, additional_fields: Vec<(&str, JsValue)>, - ) -> Result { + ) -> Result { let result_obj = js_sys::Object::new(); // Set type @@ -175,7 +175,7 @@ impl WasmSdk { &JsValue::from_str("type"), &JsValue::from_str(transition_type), ) - .map_err(|_| JsValue::from_str("Failed to set type"))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set type: {:?}", e)))?; // Set document ID js_sys::Reflect::set( @@ -183,26 +183,26 @@ impl WasmSdk { &JsValue::from_str("documentId"), &JsValue::from_str(document_id), ) - .map_err(|_| JsValue::from_str("Failed to set documentId"))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set documentId: {:?}", e)))?; // Set additional fields for (key, value) in additional_fields { js_sys::Reflect::set(&result_obj, &JsValue::from_str(key), &value) - .map_err(|_| JsValue::from_str(&format!("Failed to set {}", key)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set {}: {:?}", key, e)))?; } Ok(result_obj.into()) } /// Get the next revision for a document, handling errors for missing revisions and overflow - fn get_next_revision(document: &dash_sdk::platform::Document) -> Result { + fn get_next_revision(document: &dash_sdk::platform::Document) -> Result { let current_revision = document .revision() - .ok_or_else(|| JsValue::from_str("Document revision is missing"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("Document revision is missing"))?; current_revision .checked_add(1) - .ok_or_else(|| JsValue::from_str("Document revision overflow")) + .ok_or_else(|| WasmSdkError::invalid_argument("Document revision overflow")) } } @@ -231,7 +231,7 @@ impl WasmSdk { document_data: String, entropy: String, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers @@ -240,18 +240,22 @@ impl WasmSdk { // Parse entropy let entropy_bytes = hex::decode(&entropy) - .map_err(|e| JsValue::from_str(&format!("Invalid entropy hex: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid entropy hex: {}", e)))?; if entropy_bytes.len() != 32 { - return Err(JsValue::from_str("Entropy must be exactly 32 bytes")); + return Err(WasmSdkError::invalid_argument( + "Entropy must be exactly 32 bytes", + )); } let mut entropy_array = [0u8; 32]; entropy_array.copy_from_slice(&entropy_bytes); // Parse document data - let document_data_value: serde_json::Value = serde_json::from_str(&document_data) - .map_err(|e| JsValue::from_str(&format!("Invalid JSON document data: {}", e)))?; + let document_data_value: serde_json::Value = + serde_json::from_str(&document_data).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid JSON document data: {}", e)) + })?; // Fetch and cache the data contract let data_contract = self.fetch_and_cache_contract(contract_id).await?; @@ -259,7 +263,7 @@ impl WasmSdk { // Get document type let document_type_result = data_contract.document_type_for_name(&document_type); let document_type_ref = document_type_result.map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Document type '{}' not found: {}", document_type, e )) @@ -279,19 +283,17 @@ impl WasmSdk { entropy_array, platform_version, ) - .map_err(|e| JsValue::from_str(&format!("Failed to create document: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create document: {}", e)))?; // Fetch the identity to get the correct key let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = Self::find_authentication_key(&identity, &private_key_wif)?; @@ -311,34 +313,39 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create document transition: {}", e)))?; + .map_err(|e| { + WasmSdkError::generic(format!("Failed to create document transition: {}", e)) + })?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast transition: {}", e)))?; // Log the result for debugging - web_sys::console::log_1(&JsValue::from_str( - "Processing state transition proof result", - )); + tracing::debug!( + target = "wasm_sdk", + "Processing state transition proof result" + ); // Convert result to JsValue based on the type match proof_result { StateTransitionProofResult::VerifiedDocuments(documents) => { - web_sys::console::log_1(&JsValue::from_str(&format!( - "Documents in result: {}", - documents.len() - ))); + tracing::debug!( + target = "wasm_sdk", + count = documents.len(), + "Documents in result" + ); // Try to find the created document for (doc_id, maybe_doc) in documents.iter() { - web_sys::console::log_1(&JsValue::from_str(&format!( - "Document ID: {}, Document present: {}", - doc_id.to_string(Encoding::Base58), - maybe_doc.is_some() - ))); + tracing::debug!( + target = "wasm_sdk", + id = %doc_id.to_string(Encoding::Base58), + present = maybe_doc.is_some(), + "Document entry" + ); } if let Some((doc_id, maybe_doc)) = documents.into_iter().next() { @@ -446,9 +453,7 @@ impl WasmSdk { ) .unwrap(); - web_sys::console::log_1(&JsValue::from_str( - "Document created successfully, returning JS object", - )); + tracing::debug!(target = "wasm_sdk", "Document created successfully"); Ok(js_result.into()) } else { @@ -551,6 +556,7 @@ impl WasmSdk { /// # Returns /// /// Returns a Promise that resolves to a JsValue containing the replaced document + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = documentReplace)] pub async fn document_replace( &self, @@ -561,7 +567,7 @@ impl WasmSdk { document_data: String, revision: u64, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers @@ -570,8 +576,10 @@ impl WasmSdk { let doc_id = doc_id.unwrap(); // Parse document data - let document_data_value: serde_json::Value = serde_json::from_str(&document_data) - .map_err(|e| JsValue::from_str(&format!("Invalid JSON document data: {}", e)))?; + let document_data_value: serde_json::Value = + serde_json::from_str(&document_data).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid JSON document data: {}", e)) + })?; // Fetch and cache the data contract let data_contract = self.fetch_and_cache_contract(contract_id).await?; @@ -579,7 +587,7 @@ impl WasmSdk { // Get document type let document_type_result = data_contract.document_type_for_name(&document_type); let document_type_ref = document_type_result.map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Document type '{}' not found: {}", document_type, e )) @@ -596,7 +604,7 @@ impl WasmSdk { properties: document_data_platform_value .into_btree_string_map() .map_err(|e| { - JsValue::from_str(&format!("Failed to convert document data: {}", e)) + WasmSdkError::serialization(format!("Failed to convert document data: {}", e)) })?, revision: Some(revision + 1), created_at: None, @@ -612,15 +620,13 @@ impl WasmSdk { // Fetch the identity to get the correct key let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = Self::find_authentication_key(&identity, &private_key_wif)?; @@ -640,7 +646,7 @@ impl WasmSdk { None, // state_transition_creation_options ) .map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::generic(format!( "Failed to create document replace transition: {}", e )) @@ -650,7 +656,7 @@ impl WasmSdk { let proof_result = state_transition .broadcast_and_wait::(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast transition: {}", e)))?; // Convert result to JsValue based on the type match proof_result { @@ -760,9 +766,7 @@ impl WasmSdk { ) .unwrap(); - web_sys::console::log_1(&JsValue::from_str( - "Document replaced successfully", - )); + tracing::debug!(target = "wasm_sdk", "Document replaced successfully"); Ok(js_result.into()) } else { @@ -871,7 +875,7 @@ impl WasmSdk { document_id: String, owner_id: String, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers @@ -885,7 +889,7 @@ impl WasmSdk { // Get document type let document_type_result = data_contract.document_type_for_name(&document_type); let document_type_ref = document_type_result.map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Document type '{}' not found: {}", document_type, e )) @@ -896,29 +900,26 @@ impl WasmSdk { let query = DocumentQuery::new_with_data_contract_id(&sdk, contract_id, &document_type) .await - .map_err(|e| JsValue::from_str(&format!("Failed to create document query: {}", e)))? + .map_err(|e| WasmSdkError::generic(format!("Failed to create document query: {}", e)))? .with_document_id(&doc_id); let existing_doc = dash_sdk::platform::Document::fetch(&sdk, query) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch document: {}", e)))? - .ok_or_else(|| JsValue::from_str("Document not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Document not found"))?; let current_revision = existing_doc .revision() - .ok_or_else(|| JsValue::from_str("Document revision is missing"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("Document revision is missing"))?; // Fetch the identity to get the correct key let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = Self::find_authentication_key(&identity, &private_key_wif)?; @@ -953,16 +954,16 @@ impl WasmSdk { sdk.version(), None, // options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create transition: {}", e)))?; // The transition is already signed, convert to StateTransition - let state_transition: StateTransition = transition.into(); + let state_transition: StateTransition = transition; // Broadcast the state transition state_transition .broadcast(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast: {}", e)))?; // Return the result with document ID Self::build_js_result_object( @@ -995,7 +996,7 @@ impl WasmSdk { owner_id: String, recipient_id: String, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers @@ -1004,7 +1005,7 @@ impl WasmSdk { let doc_id = doc_id.expect("Document ID was provided"); let recipient_identifier = Identifier::from_string(&recipient_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid recipient ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid recipient ID: {}", e)))?; // Fetch and cache the data contract let data_contract = self.fetch_and_cache_contract(contract_id).await?; @@ -1012,7 +1013,7 @@ impl WasmSdk { // Get document type let document_type_result = data_contract.document_type_for_name(&document_type); let document_type_ref = document_type_result.map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Document type '{}' not found: {}", document_type, e )) @@ -1023,13 +1024,12 @@ impl WasmSdk { let query = DocumentQuery::new_with_data_contract_id(&sdk, contract_id, &document_type) .await - .map_err(|e| JsValue::from_str(&format!("Failed to create document query: {}", e)))? + .map_err(|e| WasmSdkError::generic(format!("Failed to create document query: {}", e)))? .with_document_id(&doc_id); let document = dash_sdk::platform::Document::fetch(&sdk, query) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch document: {}", e)))? - .ok_or_else(|| JsValue::from_str("Document not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Document not found"))?; // Get the current revision and increment it let next_revision = Self::get_next_revision(&document)?; @@ -1053,15 +1053,13 @@ impl WasmSdk { // Fetch the identity to get the correct key let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = Self::find_authentication_key(&identity, &private_key_wif)?; @@ -1080,16 +1078,13 @@ impl WasmSdk { sdk.version(), None, // options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create transition: {}", e)))?; // The transition is already signed, convert to StateTransition - let state_transition: StateTransition = transition.into(); + let state_transition: StateTransition = transition; // Broadcast the state transition - state_transition - .broadcast(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast: {}", e)))?; + state_transition.broadcast(&sdk, None).await?; // Return the result with document ID and new owner Self::build_js_result_object( @@ -1125,7 +1120,7 @@ impl WasmSdk { buyer_id: String, price: u64, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers @@ -1139,7 +1134,7 @@ impl WasmSdk { // Get document type from contract let document_type_ref = data_contract .document_type_for_name(&document_type) - .map_err(|e| JsValue::from_str(&format!("Document type not found: {}", e)))?; + .map_err(|e| WasmSdkError::not_found(format!("Document type not found: {}", e)))?; // Fetch the document to purchase let query = dash_sdk::platform::documents::document_query::DocumentQuery::new_with_data_contract_id( @@ -1148,23 +1143,22 @@ impl WasmSdk { &document_type, ) .await - .map_err(|e| JsValue::from_str(&format!("Failed to create document query: {}", e)))? + .map_err(|e| WasmSdkError::generic(format!("Failed to create document query: {}", e)))? .with_document_id(&doc_id); let document = dash_sdk::platform::Document::fetch(&sdk, query) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch document: {}", e)))? - .ok_or_else(|| JsValue::from_str("Document not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Document not found"))?; // Verify the document has a price and it matches let listed_price = document .properties() .get_optional_integer::("$price") - .map_err(|e| JsValue::from_str(&format!("Failed to get document price: {}", e)))? - .ok_or_else(|| JsValue::from_str("Document is not for sale (no price set)"))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to get document price: {}", e)))? + .ok_or_else(|| WasmSdkError::not_found("Document is not for sale (no price set)"))?; if listed_price != price { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Price mismatch: document is listed for {} but purchase attempted with {}", listed_price, price ))); @@ -1192,9 +1186,8 @@ impl WasmSdk { // Fetch buyer identity let buyer_identity = dash_sdk::platform::Identity::fetch(&sdk, buyer_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch buyer identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Buyer identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Buyer identity not found"))?; // Find matching authentication key and create signer let (_, matching_key) = Self::find_authentication_key(&buyer_identity, &private_key_wif)?; @@ -1203,10 +1196,7 @@ impl WasmSdk { // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(buyer_identifier, contract_id, true, None) - .await - .map_err(|e| { - JsValue::from_str(&format!("Failed to get identity contract nonce: {}", e)) - })?; + .await?; // Create document purchase transition let transition = BatchTransition::new_document_purchase_transition_from_document( @@ -1222,13 +1212,15 @@ impl WasmSdk { sdk.version(), None, // Default options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create purchase transition: {}", e)))?; + .map_err(|e| { + WasmSdkError::generic(format!("Failed to create purchase transition: {}", e)) + })?; // Broadcast the transition let proof_result = transition .broadcast_and_wait::(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast purchase: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast purchase: {}", e)))?; // Handle the proof result match proof_result { @@ -1245,14 +1237,12 @@ impl WasmSdk { ]; // If we have the updated document in the response, include basic info - if let Some((_, maybe_doc)) = documents.into_iter().next() { - if let Some(doc) = maybe_doc { - additional_fields.push(("documentUpdated", JsValue::from_bool(true))); - additional_fields.push(( - "revision", - JsValue::from_f64(doc.revision().unwrap_or(0) as f64), - )); - } + if let Some((_, Some(doc))) = documents.into_iter().next() { + additional_fields.push(("documentUpdated", JsValue::from_bool(true))); + additional_fields.push(( + "revision", + JsValue::from_f64(doc.revision().unwrap_or(0) as f64), + )); } Self::build_js_result_object( @@ -1298,7 +1288,7 @@ impl WasmSdk { owner_id: String, price: u64, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers @@ -1312,7 +1302,7 @@ impl WasmSdk { // Get document type from contract let document_type_ref = data_contract .document_type_for_name(&document_type) - .map_err(|e| JsValue::from_str(&format!("Document type not found: {}", e)))?; + .map_err(|e| WasmSdkError::not_found(format!("Document type not found: {}", e)))?; // Fetch the existing document to update its price let query = dash_sdk::platform::documents::document_query::DocumentQuery::new_with_data_contract_id( @@ -1321,17 +1311,16 @@ impl WasmSdk { &document_type, ) .await - .map_err(|e| JsValue::from_str(&format!("Failed to create document query: {}", e)))? + .map_err(|e| WasmSdkError::generic(format!("Failed to create document query: {}", e)))? .with_document_id(&doc_id); let existing_doc = Document::fetch(&sdk, query) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch document: {}", e)))? - .ok_or_else(|| JsValue::from_str("Document not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Document not found"))?; // Verify ownership if existing_doc.owner_id() != owner_identifier { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "Only the document owner can set its price", )); } @@ -1358,9 +1347,8 @@ impl WasmSdk { // Fetch the identity to get the authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Find matching authentication key and create signer let (_, matching_key) = Self::find_authentication_key(&identity, &private_key_wif)?; @@ -1369,15 +1357,14 @@ impl WasmSdk { // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Create the price update transition using the dedicated method let transition = BatchTransition::new_document_update_price_transition_from_document( price_update_document, document_type_ref, price, - &matching_key, + matching_key, identity_contract_nonce, UserFeeIncrease::default(), None, // token_payment_info @@ -1386,17 +1373,14 @@ impl WasmSdk { None, // options ) .map_err(|e| { - JsValue::from_str(&format!("Failed to create price update transition: {}", e)) + WasmSdkError::generic(format!("Failed to create price update transition: {}", e)) })?; // The transition is already signed, convert to StateTransition - let state_transition: StateTransition = transition.into(); + let state_transition: StateTransition = transition; // Broadcast the state transition - state_transition - .broadcast(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast: {}", e)))?; + state_transition.broadcast(&sdk, None).await?; // Return the result with document ID and price Self::build_js_result_object( diff --git a/packages/wasm-sdk/src/state_transitions/identity/mod.rs b/packages/wasm-sdk/src/state_transitions/identity/mod.rs index bf5daec4a0f..05bd71119d4 100644 --- a/packages/wasm-sdk/src/state_transitions/identity/mod.rs +++ b/packages/wasm-sdk/src/state_transitions/identity/mod.rs @@ -1,3 +1,4 @@ +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use dash_sdk::dpp::dashcore::PrivateKey; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; @@ -14,9 +15,9 @@ use dash_sdk::platform::transition::top_up_identity::TopUpIdentity; use dash_sdk::platform::Fetch; use js_sys; use simple_signer::{signer::SimpleSigner, SingleKeySigner}; +use tracing::{debug, error}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; -use web_sys; #[wasm_bindgen] impl WasmSdk { @@ -47,28 +48,18 @@ impl WasmSdk { asset_lock_proof: String, asset_lock_proof_private_key: String, public_keys: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); - // Debug log all parameters - web_sys::console::log_1(&JsValue::from_str(&format!("identityCreate called with:"))); - web_sys::console::log_1(&JsValue::from_str(&format!( - " asset_lock_proof (length {}): {}", - asset_lock_proof.len(), - if asset_lock_proof.len() > 100 { - format!("{}...", &asset_lock_proof[..100]) - } else { - asset_lock_proof.clone() - } - ))); - web_sys::console::log_1(&JsValue::from_str(&format!( - " asset_lock_proof_private_key: [REDACTED] (length: {})", - asset_lock_proof_private_key.len() - ))); - web_sys::console::log_1(&JsValue::from_str(&format!( - " public_keys: {}", - public_keys - ))); + // Debug log parameters (truncated/sanitized) + debug!( + target: "wasm_sdk", + len = asset_lock_proof.len(), + preview = %if asset_lock_proof.len() > 100 { format!("{}...", &asset_lock_proof[..100]) } else { asset_lock_proof.clone() }, + "identityCreate called" + ); + debug!(target: "wasm_sdk", pk_len = asset_lock_proof_private_key.len(), "identityCreate private key length"); + debug!(target: "wasm_sdk", public_keys = %public_keys, "identityCreate public keys JSON"); // Parse asset lock proof - try hex first, then JSON let asset_lock_proof: AssetLockProof = if asset_lock_proof @@ -76,53 +67,58 @@ impl WasmSdk { .all(|c| c.is_ascii_hexdigit()) { // It's hex encoded - decode and parse as JSON from the decoded bytes - let asset_lock_proof_bytes = hex::decode(&asset_lock_proof) - .map_err(|e| JsValue::from_str(&format!("Invalid asset lock proof hex: {}", e)))?; + let asset_lock_proof_bytes = hex::decode(&asset_lock_proof).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid asset lock proof hex: {}", e)) + })?; // Convert bytes to string and parse as JSON let json_str = String::from_utf8(asset_lock_proof_bytes).map_err(|e| { - JsValue::from_str(&format!("Invalid UTF-8 in asset lock proof: {}", e)) + WasmSdkError::invalid_argument(format!("Invalid UTF-8 in asset lock proof: {}", e)) })?; serde_json::from_str(&json_str).map_err(|e| { - JsValue::from_str(&format!("Failed to parse asset lock proof JSON: {}", e)) + WasmSdkError::invalid_argument(format!( + "Failed to parse asset lock proof JSON: {}", + e + )) })? } else { // Try JSON directly - serde_json::from_str(&asset_lock_proof) - .map_err(|e| JsValue::from_str(&format!("Invalid asset lock proof JSON: {}", e)))? + serde_json::from_str(&asset_lock_proof).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid asset lock proof JSON: {}", e)) + })? }; // Parse private key - WIF format - // Log the private key format for debugging - web_sys::console::log_1(&JsValue::from_str(&format!( - "Private key format validation - length: {}", - asset_lock_proof_private_key.len() - ))); + debug!(target: "wasm_sdk", pk_len = asset_lock_proof_private_key.len(), "Private key format validation"); let private_key = PrivateKey::from_wif(&asset_lock_proof_private_key) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))?; // Parse public keys from JSON - let keys_data: serde_json::Value = serde_json::from_str(&public_keys) - .map_err(|e| JsValue::from_str(&format!("Invalid JSON for public_keys: {}", e)))?; + let keys_data: serde_json::Value = serde_json::from_str(&public_keys).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid JSON for public_keys: {}", e)) + })?; let keys_array = keys_data .as_array() - .ok_or_else(|| JsValue::from_str("public_keys must be a JSON array"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("public_keys must be a JSON array"))?; // Create identity public keys and collect private keys for signing let mut identity_public_keys = std::collections::BTreeMap::new(); let mut signer = SimpleSigner::default(); - let mut key_id = 0u32; - for key_data in keys_array { + for (key_id, key_data) in keys_array + .iter() + .enumerate() + .map(|(key, value)| (key as u32, value)) + { let key_type_str = key_data["keyType"] .as_str() - .ok_or_else(|| JsValue::from_str("keyType is required"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("keyType is required"))?; let purpose_str = key_data["purpose"] .as_str() - .ok_or_else(|| JsValue::from_str("purpose is required"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("purpose is required"))?; let security_level_str = key_data["securityLevel"].as_str().unwrap_or("HIGH"); // Parse key type first @@ -133,7 +129,7 @@ impl WasmSdk { "BIP13_SCRIPT_HASH" => KeyType::BIP13_SCRIPT_HASH, "EDDSA_25519_HASH160" => KeyType::EDDSA_25519_HASH160, _ => { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Unknown key type: {}", key_type_str ))) @@ -149,7 +145,7 @@ impl WasmSdk { "SYSTEM" => Purpose::SYSTEM, "VOTING" => Purpose::VOTING, _ => { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Unknown purpose: {}", purpose_str ))) @@ -172,11 +168,14 @@ impl WasmSdk { if let Some(private_key_hex) = key_data["privateKeyHex"].as_str() { // Decode private key from hex let bytes = hex::decode(private_key_hex).map_err(|e| { - JsValue::from_str(&format!("Invalid private key hex: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid private key hex: {}", + e + )) })?; if bytes.len() != 32 { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Private key must be 32 bytes, got {}", bytes.len() ))); @@ -192,7 +191,7 @@ impl WasmSdk { self.network(), ) .map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::generic(format!( "Failed to derive ECDSA_HASH160 public key data: {}", e )) @@ -203,12 +202,15 @@ impl WasmSdk { } else if let Some(data_str) = key_data["data"].as_str() { let key_data_bytes = dash_sdk::dpp::dashcore::base64::decode(data_str) .map_err(|e| { - JsValue::from_str(&format!("Invalid base64 key data: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid base64 key data: {}", + e + )) })?; // Enforce correct HASH160 size (20 bytes). if key_data_bytes.len() != 20 { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "ECDSA_HASH160 key data must be 20 bytes, got {}", key_data_bytes.len() ))); @@ -216,7 +218,7 @@ impl WasmSdk { (key_data_bytes, [0u8; 32]) } else { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "ECDSA_HASH160 requires either 'privateKeyHex' to derive from or 'data' (base64-encoded 20-byte hash)", )); } @@ -228,11 +230,14 @@ impl WasmSdk { { // Decode private key from hex let bytes = hex::decode(private_key_hex).map_err(|e| { - JsValue::from_str(&format!("Invalid private key hex: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid private key hex: {}", + e + )) })?; if bytes.len() != 32 { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Private key must be 32 bytes, got {}", bytes.len() ))); @@ -244,11 +249,14 @@ impl WasmSdk { } else if let Some(private_key_wif) = key_data["privateKeyWif"].as_str() { // Parse WIF format private key let private_key = PrivateKey::from_wif(private_key_wif).map_err(|e| { - JsValue::from_str(&format!("Invalid WIF private key: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid WIF private key: {}", + e + )) })?; private_key.inner.secret_bytes() } else { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "ECDSA_SECP256K1 keys require either privateKeyHex or privateKeyWif", )); }; @@ -257,7 +265,7 @@ impl WasmSdk { let public_key_data = key_type .public_key_data_from_private_key_data(&private_key_bytes, self.network()) .map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::generic(format!( "Failed to derive ECDSA_SECP256K1 public key data: {}", e )) @@ -268,7 +276,7 @@ impl WasmSdk { KeyType::BLS12_381 => { // BLS12_381 keys only support hex format (WIF is not valid for BLS keys) if key_data["privateKeyWif"].is_string() { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "BLS12_381 keys do not support WIF format, use privateKeyHex only", )); } @@ -277,11 +285,14 @@ impl WasmSdk { if let Some(private_key_hex) = key_data["privateKeyHex"].as_str() { // Decode private key from hex let bytes = hex::decode(private_key_hex).map_err(|e| { - JsValue::from_str(&format!("Invalid private key hex: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid private key hex: {}", + e + )) })?; if bytes.len() != 32 { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Private key must be 32 bytes, got {}", bytes.len() ))); @@ -291,14 +302,16 @@ impl WasmSdk { private_key_array.copy_from_slice(&bytes); private_key_array } else { - return Err(JsValue::from_str("BLS12_381 keys require privateKeyHex")); + return Err(WasmSdkError::invalid_argument( + "BLS12_381 keys require privateKeyHex", + )); }; // Derive public key data from private key let public_key_data = key_type .public_key_data_from_private_key_data(&private_key_bytes, self.network()) .map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::generic(format!( "Failed to derive BLS12_381 public key data: {}", e )) @@ -307,7 +320,7 @@ impl WasmSdk { (public_key_data, private_key_bytes) } _ => { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Unsupported key type for identity creation: {}", key_type_str ))); @@ -333,7 +346,6 @@ impl WasmSdk { } identity_public_keys.insert(key_id, public_key); - key_id += 1; } // Create identity @@ -364,11 +376,8 @@ impl WasmSdk { // Extract more detailed error information let error_msg = format!("Failed to create identity: {}", e); - web_sys::console::error_1(&JsValue::from_str(&format!( - "Identity creation failed: {}", - error_msg - ))); - return Err(JsValue::from_str(&error_msg)); + error!(target: "wasm_sdk", msg = %error_msg, "Identity creation failed"); + return Err(WasmSdkError::generic(error_msg)); } }; @@ -380,37 +389,37 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("identityId"), &JsValue::from_str(&created_identity.id().to_string(Encoding::Base58)), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set identityId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set identityId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("balance"), &JsValue::from_f64(created_identity.balance() as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set balance: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set balance: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("revision"), &JsValue::from_f64(created_identity.revision() as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set revision: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set revision: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("publicKeys"), &JsValue::from_f64(created_identity.public_keys().len() as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set publicKeys: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set publicKeys: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Identity created successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } @@ -432,12 +441,12 @@ impl WasmSdk { identity_id: String, asset_lock_proof: String, asset_lock_proof_private_key: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identity identifier let identifier = Identifier::from_string(&identity_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid identity ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; // Parse asset lock proof - try hex first, then JSON let asset_lock_proof: AssetLockProof = if asset_lock_proof @@ -445,45 +454,46 @@ impl WasmSdk { .all(|c| c.is_ascii_hexdigit()) { // It's hex encoded - decode and parse as JSON from the decoded bytes - let asset_lock_proof_bytes = hex::decode(&asset_lock_proof) - .map_err(|e| JsValue::from_str(&format!("Invalid asset lock proof hex: {}", e)))?; + let asset_lock_proof_bytes = hex::decode(&asset_lock_proof).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid asset lock proof hex: {}", e)) + })?; // Convert bytes to string and parse as JSON let json_str = String::from_utf8(asset_lock_proof_bytes).map_err(|e| { - JsValue::from_str(&format!("Invalid UTF-8 in asset lock proof: {}", e)) + WasmSdkError::invalid_argument(format!("Invalid UTF-8 in asset lock proof: {}", e)) })?; serde_json::from_str(&json_str).map_err(|e| { - JsValue::from_str(&format!("Failed to parse asset lock proof JSON: {}", e)) + WasmSdkError::invalid_argument(format!( + "Failed to parse asset lock proof JSON: {}", + e + )) })? } else { // Try JSON directly - serde_json::from_str(&asset_lock_proof) - .map_err(|e| JsValue::from_str(&format!("Invalid asset lock proof JSON: {}", e)))? + serde_json::from_str(&asset_lock_proof).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid asset lock proof JSON: {}", e)) + })? }; // Parse private key - WIF format - // Log the private key format for debugging - web_sys::console::log_1(&JsValue::from_str(&format!( - "Private key format validation - length: {}", - asset_lock_proof_private_key.len() - ))); + debug!(target: "wasm_sdk", pk_len = asset_lock_proof_private_key.len(), "Private key format validation"); let private_key = PrivateKey::from_wif(&asset_lock_proof_private_key) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))?; // Fetch the identity let identity = match dash_sdk::platform::Identity::fetch(&sdk, identifier).await { Ok(Some(identity)) => identity, Ok(None) => { let error_msg = format!("Identity not found: {}", identifier); - web_sys::console::error_1(&JsValue::from_str(&error_msg)); - return Err(JsValue::from_str(&error_msg)); + error!(target: "wasm_sdk", %error_msg); + return Err(WasmSdkError::not_found(error_msg)); } Err(e) => { let error_msg = format!("Failed to fetch identity: {}", e); - web_sys::console::error_1(&JsValue::from_str(&error_msg)); - return Err(JsValue::from_str(&error_msg)); + error!(target: "wasm_sdk", %error_msg); + return Err(WasmSdkError::from(e)); } }; @@ -498,8 +508,8 @@ impl WasmSdk { Ok(balance) => balance, Err(e) => { let error_msg = format!("Failed to top up identity: {}", e); - web_sys::console::error_1(&JsValue::from_str(&error_msg)); - return Err(JsValue::from_str(&error_msg)); + error!(target: "wasm_sdk", %error_msg); + return Err(WasmSdkError::from(e)); } }; @@ -513,31 +523,31 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("identityId"), &JsValue::from_str(&identity_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set identityId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set identityId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("newBalance"), &JsValue::from_f64(new_balance as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set newBalance: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set newBalance: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("toppedUpAmount"), &JsValue::from_f64(topped_up_amount as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set toppedUpAmount: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set toppedUpAmount: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Identity topped up successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } @@ -563,42 +573,46 @@ impl WasmSdk { amount: u64, private_key_wif: String, key_id: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identifiers let sender_identifier = Identifier::from_string(&sender_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid sender ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid sender ID: {}", e)))?; let recipient_identifier = Identifier::from_string(&recipient_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid recipient ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid recipient ID: {}", e)))?; // Validate not sending to self if sender_identifier == recipient_identifier { - return Err(JsValue::from_str("Cannot transfer credits to yourself")); + return Err(WasmSdkError::invalid_argument( + "Cannot transfer credits to yourself", + )); } // Validate amount if amount == 0 { - return Err(JsValue::from_str("Transfer amount must be greater than 0")); + return Err(WasmSdkError::invalid_argument( + "Transfer amount must be greater than 0", + )); } // Fetch sender identity let sender_identity = dash_sdk::platform::Identity::fetch(&sdk, sender_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch sender identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Sender identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Sender identity not found"))?; // Parse private key and find matching public key let private_key_bytes = dash_sdk::dpp::dashcore::PrivateKey::from_wif(&private_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))? + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))? .inner .secret_bytes(); let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); - let secret_key = - dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice(&private_key_bytes) - .map_err(|e| JsValue::from_str(&format!("Invalid secret key: {}", e)))?; + let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( + &private_key_bytes, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize(); @@ -623,7 +637,7 @@ impl WasmSdk { && key.data().as_slice() == public_key_hash160.as_slice() }) .ok_or_else(|| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Key with ID {} not found or doesn't match private key", requested_key_id )) @@ -640,7 +654,9 @@ impl WasmSdk { }) .map(|(_, key)| key) .ok_or_else(|| { - JsValue::from_str("No matching transfer key found for the provided private key") + WasmSdkError::not_found( + "No matching transfer key found for the provided private key", + ) })? }; @@ -648,11 +664,11 @@ impl WasmSdk { let identity_nonce = sdk .get_identity_nonce(sender_identifier, true, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to get identity nonce: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to get identity nonce: {}", e)))?; // Create signer let signer = SingleKeySigner::from_string(&private_key_wif, self.network()) - .map_err(|e| JsValue::from_str(&e))?; + .map_err(WasmSdkError::invalid_argument)?; // Create the credit transfer transition let state_transition = IdentityCreditTransferTransition::try_from_identity( @@ -666,14 +682,16 @@ impl WasmSdk { sdk.version(), None, // No version override ) - .map_err(|e| JsValue::from_str(&format!("Failed to create transfer transition: {}", e)))?; + .map_err(|e| { + WasmSdkError::generic(format!("Failed to create transfer transition: {}", e)) + })?; // Broadcast the transition use dash_sdk::dpp::state_transition::proof_result::StateTransitionProofResult; let _result = state_transition .broadcast_and_wait::(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transfer: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast transfer: {}", e)))?; // Create JavaScript result object let result_obj = js_sys::Object::new(); @@ -683,31 +701,31 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("senderId"), &JsValue::from_str(&sender_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set senderId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set senderId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("recipientId"), &JsValue::from_str(&recipient_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set recipientId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set recipientId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("amount"), &JsValue::from_f64(amount as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set amount: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set amount: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Credits transferred successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } @@ -735,43 +753,43 @@ impl WasmSdk { core_fee_per_byte: Option, private_key_wif: String, key_id: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identity identifier let identifier = Identifier::from_string(&identity_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid identity ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; // Parse the Dash address use dash_sdk::dpp::dashcore::Address; use std::str::FromStr; let address = Address::from_str(&to_address) - .map_err(|e| JsValue::from_str(&format!("Invalid Dash address: {}", e)))? + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid Dash address: {}", e)))? .assume_checked(); // Validate amount if amount == 0 { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "Withdrawal amount must be greater than 0", )); } // Fetch the identity let identity = dash_sdk::platform::Identity::fetch(&sdk, identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Parse private key and find matching public key let private_key_bytes = dash_sdk::dpp::dashcore::PrivateKey::from_wif(&private_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))? + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))? .inner .secret_bytes(); let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); - let secret_key = - dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice(&private_key_bytes) - .map_err(|e| JsValue::from_str(&format!("Invalid secret key: {}", e)))?; + let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( + &private_key_bytes, + ) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize(); @@ -797,7 +815,7 @@ impl WasmSdk { && key.data().as_slice() == public_key_hash160.as_slice() }) .ok_or_else(|| { - JsValue::from_str(&format!( + WasmSdkError::not_found(format!( "Key with ID {} not found or doesn't match private key", requested_key_id )) @@ -821,7 +839,7 @@ impl WasmSdk { }) .map(|(_, key)| key) .ok_or_else(|| { - JsValue::from_str( + WasmSdkError::not_found( "No matching withdrawal key found for the provided private key", ) })? @@ -829,7 +847,7 @@ impl WasmSdk { // Create signer let signer = SingleKeySigner::from_string(&private_key_wif, self.network()) - .map_err(|e| JsValue::from_str(&e))?; + .map_err(WasmSdkError::invalid_argument)?; // Import the withdraw trait use dash_sdk::platform::transition::withdraw_from_identity::WithdrawFromIdentity; @@ -846,7 +864,7 @@ impl WasmSdk { None, // No special settings ) .await - .map_err(|e| JsValue::from_str(&format!("Withdrawal failed: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Withdrawal failed: {}", e)))?; // Create JavaScript result object let result_obj = js_sys::Object::new(); @@ -856,37 +874,37 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("identityId"), &JsValue::from_str(&identity_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set identityId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set identityId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("toAddress"), &JsValue::from_str(&to_address), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set toAddress: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set toAddress: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("amount"), &JsValue::from_f64(amount as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set amount: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set amount: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("remainingBalance"), &JsValue::from_f64(remaining_balance as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set remainingBalance: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set remainingBalance: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Credits withdrawn successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } @@ -910,32 +928,31 @@ impl WasmSdk { add_public_keys: Option, disable_public_keys: Option>, private_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse identity identifier let identifier = Identifier::from_string(&identity_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid identity ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; // Fetch the identity let identity = dash_sdk::platform::Identity::fetch(&sdk, identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get current revision let current_revision = identity.revision(); // Parse private key and verify it's a master key let private_key = PrivateKey::from_wif(&private_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid private key: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid private key: {}", e)))?; // Create public key hash to find matching master key let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( &private_key.inner.secret_bytes(), ) - .map_err(|e| JsValue::from_str(&format!("Invalid secret key: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize(); @@ -960,19 +977,19 @@ impl WasmSdk { }) .map(|(id, _)| *id) .ok_or_else(|| { - JsValue::from_str("Provided private key does not match any master key") + WasmSdkError::invalid_argument("Provided private key does not match any master key") })?; // Parse and prepare keys to add let keys_to_add: Vec = if let Some(keys_json) = add_public_keys { // Parse JSON array of keys let keys_data: serde_json::Value = serde_json::from_str(&keys_json).map_err(|e| { - JsValue::from_str(&format!("Invalid JSON for add_public_keys: {}", e)) + WasmSdkError::invalid_argument(format!("Invalid JSON for add_public_keys: {}", e)) })?; - let keys_array = keys_data - .as_array() - .ok_or_else(|| JsValue::from_str("add_public_keys must be a JSON array"))?; + let keys_array = keys_data.as_array().ok_or_else(|| { + WasmSdkError::invalid_argument("add_public_keys must be a JSON array") + })?; // Get the current max key ID let mut next_key_id = identity.public_keys().keys().max().copied().unwrap_or(0) + 1; @@ -982,14 +999,14 @@ impl WasmSdk { .map(|key_data| { let key_type_str = key_data["keyType"] .as_str() - .ok_or_else(|| JsValue::from_str("keyType is required"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("keyType is required"))?; let purpose_str = key_data["purpose"] .as_str() - .ok_or_else(|| JsValue::from_str("purpose is required"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("purpose is required"))?; let security_level_str = key_data["securityLevel"].as_str().unwrap_or("HIGH"); let data_str = key_data["data"] .as_str() - .ok_or_else(|| JsValue::from_str("data is required"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("data is required"))?; // Parse key type let key_type = match key_type_str { @@ -999,10 +1016,10 @@ impl WasmSdk { "BIP13_SCRIPT_HASH" => KeyType::BIP13_SCRIPT_HASH, "EDDSA_25519_HASH160" => KeyType::EDDSA_25519_HASH160, _ => { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Unknown key type: {}", key_type_str - ))) + ))); } }; @@ -1015,10 +1032,10 @@ impl WasmSdk { "SYSTEM" => Purpose::SYSTEM, "VOTING" => Purpose::VOTING, _ => { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Unknown purpose: {}", purpose_str - ))) + ))); } }; @@ -1034,7 +1051,10 @@ impl WasmSdk { // Decode key data from base64 let key_data = dash_sdk::dpp::dashcore::base64::decode(data_str).map_err(|e| { - JsValue::from_str(&format!("Invalid base64 key data: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid base64 key data: {}", + e + )) })?; // Create the identity public key @@ -1053,7 +1073,7 @@ impl WasmSdk { next_key_id += 1; Ok(public_key) }) - .collect::, _>>()? + .collect::, WasmSdkError>>()? } else { Vec::new() }; @@ -1069,7 +1089,7 @@ impl WasmSdk { for key_id in &keys_to_disable { if let Some(key) = identity.public_keys().get(key_id) { if key.security_level() == SecurityLevel::MASTER { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Cannot disable master key {}", key_id ))); @@ -1078,19 +1098,19 @@ impl WasmSdk { && key.security_level() == SecurityLevel::CRITICAL && key.key_type() == KeyType::ECDSA_SECP256K1 { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Cannot disable critical authentication key {}", key_id ))); } if key.purpose() == Purpose::TRANSFER { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Cannot disable transfer key {}", key_id ))); } } else { - return Err(JsValue::from_str(&format!("Key {} not found", key_id))); + return Err(WasmSdkError::not_found(format!("Key {} not found", key_id))); } } @@ -1098,11 +1118,11 @@ impl WasmSdk { let identity_nonce = sdk .get_identity_nonce(identifier, true, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to get identity nonce: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to get identity nonce: {}", e)))?; // Create signer let signer = SingleKeySigner::from_string(&private_key_wif, self.network()) - .map_err(|e| JsValue::from_str(&e))?; + .map_err(WasmSdkError::invalid_argument)?; // Create the identity update transition use dash_sdk::dpp::state_transition::identity_update_transition::methods::IdentityUpdateTransitionMethodsV0; @@ -1119,14 +1139,14 @@ impl WasmSdk { sdk.version(), None, // No version override ) - .map_err(|e| JsValue::from_str(&format!("Failed to create update transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create update transition: {}", e)))?; // Broadcast the transition use dash_sdk::dpp::state_transition::proof_result::StateTransitionProofResult; let result = state_transition .broadcast_and_wait::(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast update: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast update: {}", e)))?; // Extract updated identity from result let updated_revision = match result { @@ -1147,37 +1167,37 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("identityId"), &JsValue::from_str(&identity_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set identityId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set identityId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("revision"), &JsValue::from_f64(updated_revision as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set revision: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set revision: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("addedKeys"), &JsValue::from_f64(added_keys_count as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set addedKeys: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set addedKeys: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("disabledKeys"), &JsValue::from_f64(disabled_keys_count as f64), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set disabledKeys: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set disabledKeys: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Identity updated successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } @@ -1197,6 +1217,7 @@ impl WasmSdk { /// # Returns /// /// Returns a Promise that resolves to a JsValue containing the vote result + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = masternodeVote)] pub async fn masternode_vote( &self, @@ -1207,7 +1228,7 @@ impl WasmSdk { index_values: String, vote_choice: String, voting_key_wif: String, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse ProTxHash (try hex first, then base58) @@ -1217,25 +1238,29 @@ impl WasmSdk { .all(|c| c.is_ascii_hexdigit()) { // Looks like hex - Identifier::from_string(&masternode_pro_tx_hash, Encoding::Hex) - .map_err(|e| JsValue::from_str(&format!("Invalid ProTxHash (hex): {}", e)))? + Identifier::from_string(&masternode_pro_tx_hash, Encoding::Hex).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid ProTxHash (hex): {}", e)) + })? } else { // Try base58 - Identifier::from_string(&masternode_pro_tx_hash, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid ProTxHash (base58): {}", e)))? + Identifier::from_string(&masternode_pro_tx_hash, Encoding::Base58).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid ProTxHash (base58): {}", e)) + })? }; // Parse contract ID let data_contract_id = Identifier::from_string(&contract_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid contract ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; // Parse index values from JSON - let index_values_json: serde_json::Value = serde_json::from_str(&index_values) - .map_err(|e| JsValue::from_str(&format!("Invalid index values JSON: {}", e)))?; + let index_values_json: serde_json::Value = + serde_json::from_str(&index_values).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid index values JSON: {}", e)) + })?; let index_values_array = index_values_json .as_array() - .ok_or_else(|| JsValue::from_str("index_values must be a JSON array"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("index_values must be a JSON array"))?; let index_values_vec: Vec = index_values_array .iter() @@ -1255,9 +1280,11 @@ impl WasmSdk { } } serde_json::Value::Bool(b) => Ok(dash_sdk::dpp::platform_value::Value::Bool(*b)), - _ => Err(JsValue::from_str("Unsupported index value type")), + _ => Err(WasmSdkError::invalid_argument( + "Unsupported index value type", + )), }) - .collect::, _>>()?; + .collect::, WasmSdkError>>()?; // Parse vote choice use dash_sdk::dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; @@ -1268,14 +1295,17 @@ impl WasmSdk { } else if vote_choice.starts_with("towardsIdentity:") { let identity_id_str = vote_choice .strip_prefix("towardsIdentity:") - .ok_or_else(|| JsValue::from_str("Invalid vote choice format"))?; + .ok_or_else(|| WasmSdkError::invalid_argument("Invalid vote choice format"))?; let identity_id = Identifier::from_string(identity_id_str, Encoding::Base58).map_err(|e| { - JsValue::from_str(&format!("Invalid identity ID in vote choice: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid identity ID in vote choice: {}", + e + )) })?; ResourceVoteChoice::TowardsIdentity(identity_id) } else { - return Err(JsValue::from_str("Invalid vote choice. Must be 'abstain', 'lock', or 'towardsIdentity:'")); + return Err(WasmSdkError::invalid_argument("Invalid vote choice. Must be 'abstain', 'lock', or 'towardsIdentity:'")); }; // Parse private key (try WIF first, then hex) @@ -1283,21 +1313,26 @@ impl WasmSdk { && voting_key_wif.chars().all(|c| c.is_ascii_hexdigit()) { // Looks like hex - let key_bytes = hex::decode(&voting_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid hex private key: {}", e)))?; + let key_bytes = hex::decode(&voting_key_wif).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid hex private key: {}", e)) + })?; if key_bytes.len() != 32 { - return Err(JsValue::from_str("Private key must be 32 bytes")); + return Err(WasmSdkError::invalid_argument( + "Private key must be 32 bytes", + )); } let key_array: [u8; 32] = key_bytes .as_slice() .try_into() - .map_err(|_| JsValue::from_str("Private key must be 32 bytes"))?; - PrivateKey::from_byte_array(&key_array, self.network()) - .map_err(|e| JsValue::from_str(&format!("Invalid private key bytes: {}", e)))? + .map_err(|_| WasmSdkError::invalid_argument("Private key must be 32 bytes"))?; + PrivateKey::from_byte_array(&key_array, self.network()).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid private key bytes: {}", e)) + })? } else { // Try WIF - PrivateKey::from_wif(&voting_key_wif) - .map_err(|e| JsValue::from_str(&format!("Invalid WIF private key: {}", e)))? + PrivateKey::from_wif(&voting_key_wif).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid WIF private key: {}", e)) + })? }; // Create the voting public key from the private key @@ -1305,7 +1340,7 @@ impl WasmSdk { let secret_key = dash_sdk::dpp::dashcore::secp256k1::SecretKey::from_slice( &private_key.inner.secret_bytes(), ) - .map_err(|e| JsValue::from_str(&format!("Invalid secret key: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let public_key = dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); let public_key_bytes = public_key.serialize(); @@ -1357,14 +1392,13 @@ impl WasmSdk { // Create signer let signer = SingleKeySigner::from_string(&voting_key_wif, self.network()) - .map_err(|e| JsValue::from_str(&e))?; + .map_err(WasmSdkError::invalid_argument)?; // Submit the vote using PutVote trait use dash_sdk::platform::transition::vote::PutVote; vote.put_to_platform(pro_tx_hash, &voting_public_key, &sdk, &signer, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to submit vote: {}", e)))?; + .await?; // Create JavaScript result object let result_obj = js_sys::Object::new(); @@ -1374,37 +1408,37 @@ impl WasmSdk { &JsValue::from_str("status"), &JsValue::from_str("success"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set status: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set status: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("proTxHash"), &JsValue::from_str(&masternode_pro_tx_hash), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set proTxHash: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set proTxHash: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("contractId"), &JsValue::from_str(&contract_id), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set contractId: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set contractId: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("documentType"), &JsValue::from_str(&document_type_name), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set documentType: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set documentType: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("voteChoice"), &JsValue::from_str(&vote_choice), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set voteChoice: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set voteChoice: {:?}", e)))?; js_sys::Reflect::set( &result_obj, &JsValue::from_str("message"), &JsValue::from_str("Vote submitted successfully"), ) - .map_err(|e| JsValue::from_str(&format!("Failed to set message: {:?}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to set message: {:?}", e)))?; Ok(result_obj.into()) } diff --git a/packages/wasm-sdk/src/state_transitions/tokens/mod.rs b/packages/wasm-sdk/src/state_transitions/tokens/mod.rs index 951d7982fe1..e4956090e54 100644 --- a/packages/wasm-sdk/src/state_transitions/tokens/mod.rs +++ b/packages/wasm-sdk/src/state_transitions/tokens/mod.rs @@ -2,6 +2,7 @@ //! //! This module provides WASM bindings for token operations like mint, burn, transfer, etc. +use crate::error::WasmSdkError; use crate::sdk::WasmSdk; use dash_sdk::dpp::balances::credits::TokenAmount; use dash_sdk::dpp::document::DocumentV0Getters; @@ -29,18 +30,19 @@ impl WasmSdk { identity_id: &str, amount: &str, recipient_id: Option, - ) -> Result<(Identifier, Identifier, TokenAmount, Option), JsValue> { + ) -> Result<(Identifier, Identifier, TokenAmount, Option), WasmSdkError> { // Parse identifiers let contract_id = Identifier::from_string(data_contract_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid contract ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid contract ID: {}", e)))?; let identity_identifier = Identifier::from_string(identity_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid identity ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid identity ID: {}", e)))?; let recipient = if let Some(recipient_str) = recipient_id { Some( - Identifier::from_string(&recipient_str, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid recipient ID: {}", e)))?, + Identifier::from_string(&recipient_str, Encoding::Base58).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid recipient ID: {}", e)) + })?, ) } else { None @@ -49,7 +51,7 @@ impl WasmSdk { // Parse amount let token_amount = amount .parse::() - .map_err(|e| JsValue::from_str(&format!("Invalid amount: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid amount: {}", e)))?; Ok((contract_id, identity_identifier, token_amount, recipient)) } @@ -58,14 +60,13 @@ impl WasmSdk { async fn fetch_and_cache_token_contract( &self, contract_id: Identifier, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Fetch the data contract let data_contract = dash_sdk::platform::DataContract::fetch(&sdk, contract_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch data contract: {}", e)))? - .ok_or_else(|| JsValue::from_str("Data contract not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Data contract not found"))?; // Add the contract to the context provider's cache if using trusted mode match sdk.network { @@ -89,7 +90,7 @@ impl WasmSdk { fn format_token_result( &self, proof_result: StateTransitionProofResult, - ) -> Result { + ) -> Result { match proof_result { StateTransitionProofResult::VerifiedTokenBalance(recipient_id, new_balance) => { to_value(&serde_json::json!({ @@ -97,7 +98,9 @@ impl WasmSdk { "recipientId": recipient_id.to_string(Encoding::Base58), "newBalance": new_balance.to_string() })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))) + .map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize result: {}", e)) + }) } StateTransitionProofResult::VerifiedTokenActionWithDocument(doc) => { to_value(&serde_json::json!({ @@ -105,7 +108,9 @@ impl WasmSdk { "documentId": doc.id().to_string(Encoding::Base58), "message": "Token operation recorded successfully" })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))) + .map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize result: {}", e)) + }) } StateTransitionProofResult::VerifiedTokenGroupActionWithDocument(power, doc) => { to_value(&serde_json::json!({ @@ -113,7 +118,9 @@ impl WasmSdk { "groupPower": power, "document": doc.is_some() })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))) + .map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize result: {}", e)) + }) } StateTransitionProofResult::VerifiedTokenGroupActionWithTokenBalance( power, @@ -125,8 +132,8 @@ impl WasmSdk { "status": format!("{:?}", status), "balance": balance.map(|b| b.to_string()) })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))), - _ => Err(JsValue::from_str( + .map_err(|e| WasmSdkError::serialization(format!("Failed to serialize result: {}", e))), + _ => Err(WasmSdkError::generic( "Unexpected result type for token transition", )), } @@ -150,6 +157,7 @@ impl WasmSdk { /// # Returns /// /// Returns a Promise that resolves to a JsValue containing the state transition result + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = tokenMint)] pub async fn token_mint( &self, @@ -160,7 +168,7 @@ impl WasmSdk { private_key_wif: String, recipient_id: Option, public_note: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse and validate parameters @@ -173,15 +181,13 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, issuer_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(issuer_id, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = @@ -210,13 +216,12 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create mint transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create mint transition: {}", e)))?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -245,7 +250,7 @@ impl WasmSdk { identity_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse and validate parameters (no recipient for burn) @@ -258,15 +263,13 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, burner_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(burner_id, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = @@ -294,13 +297,12 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create burn transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create burn transition: {}", e)))?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -321,6 +323,7 @@ impl WasmSdk { /// # Returns /// /// Returns a Promise that resolves to a JsValue containing the state transition result + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = tokenTransfer)] pub async fn token_transfer( &self, @@ -331,7 +334,7 @@ impl WasmSdk { recipient_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse and validate parameters @@ -341,22 +344,20 @@ impl WasmSdk { // Parse recipient ID let recipient_identifier = Identifier::from_string(&recipient_id, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid recipient ID: {}", e)))?; + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid recipient ID: {}", e)))?; // Fetch and cache the data contract let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, sender_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(sender_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = @@ -386,13 +387,14 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create transfer transition: {}", e)))?; + .map_err(|e| { + WasmSdkError::generic(format!("Failed to create transfer transition: {}", e)) + })?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -421,7 +423,7 @@ impl WasmSdk { freezer_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse and validate parameters @@ -436,22 +438,22 @@ impl WasmSdk { // Parse identity to freeze let frozen_identity_id = Identifier::from_string(&identity_to_freeze, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid identity to freeze: {}", e)))?; + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid identity to freeze: {}", e)) + })?; // Fetch and cache the data contract let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, freezer_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(freezer_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = @@ -479,13 +481,12 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create freeze transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create freeze transition: {}", e)))?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -514,7 +515,7 @@ impl WasmSdk { unfreezer_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse and validate parameters @@ -528,24 +529,23 @@ impl WasmSdk { .await?; // Parse identity to unfreeze - let frozen_identity_id = - Identifier::from_string(&identity_to_unfreeze, Encoding::Base58) - .map_err(|e| JsValue::from_str(&format!("Invalid identity to unfreeze: {}", e)))?; + let frozen_identity_id = Identifier::from_string(&identity_to_unfreeze, Encoding::Base58) + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid identity to unfreeze: {}", e)) + })?; // Fetch and cache the data contract let _data_contract = self.fetch_and_cache_token_contract(contract_id).await?; // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, unfreezer_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(unfreezer_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = @@ -573,13 +573,14 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create unfreeze transition: {}", e)))?; + .map_err(|e| { + WasmSdkError::generic(format!("Failed to create unfreeze transition: {}", e)) + })?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -608,7 +609,7 @@ impl WasmSdk { destroyer_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { let sdk = self.inner_clone(); // Parse and validate parameters @@ -624,7 +625,10 @@ impl WasmSdk { // Parse identity whose frozen tokens to destroy let frozen_identity_id = Identifier::from_string(&identity_id, Encoding::Base58).map_err(|e| { - JsValue::from_str(&format!("Invalid identity to destroy frozen funds: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid identity to destroy frozen funds: {}", + e + )) })?; // Fetch and cache the data contract @@ -632,15 +636,13 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, destroyer_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(destroyer_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Find matching authentication key and create signer let (_, matching_key) = @@ -669,17 +671,13 @@ impl WasmSdk { None, // state_transition_creation_options ) .map_err(|e| { - JsValue::from_str(&format!( - "Failed to create destroy frozen transition: {}", - e - )) + WasmSdkError::generic(format!("Failed to create destroy frozen transition: {}", e)) })?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -701,6 +699,7 @@ impl WasmSdk { /// # Returns /// /// Returns a Promise that resolves to a JsValue containing the state transition result + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = tokenSetPriceForDirectPurchase)] pub async fn token_set_price_for_direct_purchase( &self, @@ -711,7 +710,7 @@ impl WasmSdk { price_data: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { use dash_sdk::dpp::fee::Credits; use dash_sdk::dpp::tokens::token_pricing_schedule::TokenPricingSchedule; use std::collections::BTreeMap; @@ -739,23 +738,26 @@ impl WasmSdk { match price_type.to_lowercase().as_str() { "single" => { // Parse single price - let price_credits: Credits = price_data - .parse::() - .map_err(|e| JsValue::from_str(&format!("Invalid price credits: {}", e)))?; + let price_credits: Credits = price_data.parse::().map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid price credits: {}", e)) + })?; Some(TokenPricingSchedule::SinglePrice(price_credits)) } "tiered" | "set" => { // Parse tiered pricing map from JSON let price_map: std::collections::HashMap = serde_json::from_str(&price_data).map_err(|e| { - JsValue::from_str(&format!("Invalid tiered pricing JSON: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid tiered pricing JSON: {}", + e + )) })?; // Convert to BTreeMap let mut btree_map = BTreeMap::new(); for (amount_str, credits) in price_map { let amount: TokenAmount = amount_str.parse().map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::invalid_argument(format!( "Invalid token amount '{}': {}", amount_str, e )) @@ -764,13 +766,15 @@ impl WasmSdk { } if btree_map.is_empty() { - return Err(JsValue::from_str("Tiered pricing map cannot be empty")); + return Err(WasmSdkError::invalid_argument( + "Tiered pricing map cannot be empty", + )); } Some(TokenPricingSchedule::SetPrices(btree_map)) } _ => { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "Invalid price type. Use 'single' or 'tiered'", )) } @@ -779,9 +783,8 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, actor_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Find matching authentication key and create signer let (_, matching_key) = @@ -795,8 +798,7 @@ impl WasmSdk { // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(actor_id, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Create the state transition let platform_version = sdk.version(); @@ -815,13 +817,14 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create set price transition: {}", e)))?; + .map_err(|e| { + WasmSdkError::generic(format!("Failed to create set price transition: {}", e)) + })?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result based on the proof result type match proof_result { @@ -846,7 +849,9 @@ impl WasmSdk { } }) })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))) + .map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize result: {}", e)) + }) } StateTransitionProofResult::VerifiedTokenGroupActionWithTokenPricingSchedule( power, @@ -873,7 +878,7 @@ impl WasmSdk { } }) })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))), + .map_err(|e| WasmSdkError::serialization(format!("Failed to serialize result: {}", e))), _ => self.format_token_result(proof_result), } } @@ -901,7 +906,7 @@ impl WasmSdk { identity_id: String, total_agreed_price: Option, private_key_wif: String, - ) -> Result { + ) -> Result { use dash_sdk::dpp::fee::Credits; let sdk = self.inner_clone(); @@ -915,55 +920,49 @@ impl WasmSdk { let price_credits: Credits = match total_agreed_price { Some(price_str) => { // Use provided price - price_str - .parse::() - .map_err(|e| JsValue::from_str(&format!("Invalid total agreed price: {}", e)))? + price_str.parse::().map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid total agreed price: {}", e)) + })? } None => { // Fetch price from pricing schedule - let token_id = crate::queries::token::calculate_token_id_from_contract( - &data_contract_id, - token_position, - ) - .map_err(|e| { - JsValue::from_str(&format!("Failed to calculate token ID: {:?}", e)) - })?; - - let token_ids = vec![token_id]; - let prices = - crate::queries::token::get_token_direct_purchase_prices(self, token_ids) - .await + let token_id = + Self::calculate_token_id_from_contract(&data_contract_id, token_position) .map_err(|e| { - JsValue::from_str(&format!("Failed to fetch token price: {:?}", e)) + WasmSdkError::generic(format!("Failed to calculate token ID: {:?}", e)) })?; + let token_ids = vec![token_id]; + let prices = self.get_token_direct_purchase_prices(token_ids).await?; + // Use js_sys to work with JavaScript objects use js_sys::{Array, Reflect}; // Get the prices array from the result let prices_prop = Reflect::get(&prices, &JsValue::from_str("prices")) - .map_err(|_| JsValue::from_str("Failed to get prices property"))?; + .map_err(|_| WasmSdkError::generic("Failed to get prices property"))?; // Convert to array and get first element let prices_array = Array::from(&prices_prop); if prices_array.length() == 0 { - return Err(JsValue::from_str("No prices found for token")); + return Err(WasmSdkError::not_found("No prices found for token")); } let first_price = prices_array.get(0); // Get current price from the price object let current_price_prop = - Reflect::get(&first_price, &JsValue::from_str("currentPrice")) - .map_err(|_| JsValue::from_str("Failed to get currentPrice property"))?; + Reflect::get(&first_price, &JsValue::from_str("currentPrice")).map_err( + |_| WasmSdkError::generic("Failed to get currentPrice property"), + )?; // Convert to string and parse - let price_str = current_price_prop - .as_string() - .ok_or_else(|| JsValue::from_str("Current price is not a string"))?; + let price_str = current_price_prop.as_string().ok_or_else(|| { + WasmSdkError::invalid_argument("Current price is not a string") + })?; let price_per_token = price_str.parse::().map_err(|e| { - JsValue::from_str(&format!("Invalid current price format: {}", e)) + WasmSdkError::invalid_argument(format!("Invalid current price format: {}", e)) })?; price_per_token * token_amount @@ -975,9 +974,8 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, purchaser_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Find matching authentication key and create signer let (_, matching_key) = @@ -991,8 +989,7 @@ impl WasmSdk { // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(purchaser_id, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Create the state transition let platform_version = sdk.version(); @@ -1011,7 +1008,7 @@ impl WasmSdk { None, // state_transition_creation_options ) .map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::generic(format!( "Failed to create direct purchase transition: {}", e )) @@ -1021,7 +1018,7 @@ impl WasmSdk { let proof_result = state_transition .broadcast_and_wait::(&sdk, None) .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to broadcast transition: {}", e)))?; // Format and return result self.format_token_result(proof_result) @@ -1048,7 +1045,7 @@ impl WasmSdk { identity_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { use dash_sdk::dpp::data_contract::associated_token::token_distribution_key::TokenDistributionType; let sdk = self.inner_clone(); @@ -1073,7 +1070,7 @@ impl WasmSdk { TokenDistributionType::PreProgrammed } _ => { - return Err(JsValue::from_str( + return Err(WasmSdkError::invalid_argument( "Invalid distribution type. Use 'perpetual' or 'preprogrammed'", )) } @@ -1081,9 +1078,8 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, identity_identifier) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Find matching authentication key and create signer let (_, matching_key) = @@ -1097,8 +1093,7 @@ impl WasmSdk { // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(identity_identifier, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Create the state transition directly as a token claim transition let platform_version = sdk.version(); @@ -1117,13 +1112,12 @@ impl WasmSdk { platform_version, None, // state_transition_creation_options ) - .map_err(|e| JsValue::from_str(&format!("Failed to create claim transition: {}", e)))?; + .map_err(|e| WasmSdkError::generic(format!("Failed to create claim transition: {}", e)))?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) @@ -1144,6 +1138,7 @@ impl WasmSdk { /// # Returns /// /// Returns a Promise that resolves to a JsValue containing the state transition result + #[allow(clippy::too_many_arguments)] #[wasm_bindgen(js_name = tokenConfigUpdate)] pub async fn token_config_update( &self, @@ -1154,7 +1149,7 @@ impl WasmSdk { identity_id: String, private_key_wif: String, public_note: Option, - ) -> Result { + ) -> Result { use dash_sdk::dpp::data_contract::associated_token::token_configuration_convention::TokenConfigurationConvention; use dash_sdk::dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use dash_sdk::dpp::data_contract::associated_token::token_perpetual_distribution::TokenPerpetualDistribution; @@ -1180,16 +1175,18 @@ impl WasmSdk { "conventions" => { // Parse JSON for conventions let convention: TokenConfigurationConvention = serde_json::from_str(&config_value) - .map_err(|e| JsValue::from_str(&format!("Invalid conventions JSON: {}", e)))?; + .map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid conventions JSON: {}", e)) + })?; TokenConfigurationChangeItem::Conventions(convention) } "max_supply" => { if config_value.is_empty() || config_value == "null" { TokenConfigurationChangeItem::MaxSupply(None) } else { - let max_supply: TokenAmount = config_value - .parse() - .map_err(|e| JsValue::from_str(&format!("Invalid max supply: {}", e)))?; + let max_supply: TokenAmount = config_value.parse().map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid max supply: {}", e)) + })?; TokenConfigurationChangeItem::MaxSupply(Some(max_supply)) } } @@ -1200,7 +1197,7 @@ impl WasmSdk { // Parse JSON for perpetual distribution config let distribution: TokenPerpetualDistribution = serde_json::from_str(&config_value).map_err(|e| { - JsValue::from_str(&format!( + WasmSdkError::invalid_argument(format!( "Invalid perpetual distribution JSON: {}", e )) @@ -1214,7 +1211,10 @@ impl WasmSdk { } else { let dest_id = Identifier::from_string(&config_value, Encoding::Base58) .map_err(|e| { - JsValue::from_str(&format!("Invalid destination identity ID: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid destination identity ID: {}", + e + )) })?; TokenConfigurationChangeItem::NewTokensDestinationIdentity(Some(dest_id)) } @@ -1222,7 +1222,7 @@ impl WasmSdk { "minting_allow_choosing_destination" => { let allow: bool = config_value .parse() - .map_err(|_| JsValue::from_str("Invalid boolean value"))?; + .map_err(|_| WasmSdkError::invalid_argument("Invalid boolean value"))?; TokenConfigurationChangeItem::MintingAllowChoosingDestination(allow) } "manual_minting" @@ -1242,7 +1242,10 @@ impl WasmSdk { // Parse AuthorizedActionTakers from JSON let action_takers: AuthorizedActionTakers = serde_json::from_str(&config_value) .map_err(|e| { - JsValue::from_str(&format!("Invalid authorized action takers JSON: {}", e)) + WasmSdkError::invalid_argument(format!( + "Invalid authorized action takers JSON: {}", + e + )) })?; match config_item_type.as_str() { @@ -1298,7 +1301,7 @@ impl WasmSdk { } } _ => { - return Err(JsValue::from_str(&format!( + return Err(WasmSdkError::invalid_argument(format!( "Invalid config item type: {}", config_item_type ))) @@ -1307,9 +1310,8 @@ impl WasmSdk { // Get identity to find matching authentication key let identity = dash_sdk::platform::Identity::fetch(&sdk, owner_id) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch identity: {}", e)))? - .ok_or_else(|| JsValue::from_str("Identity not found"))?; + .await? + .ok_or_else(|| WasmSdkError::not_found("Identity not found"))?; // Find matching authentication key and create signer let (_, matching_key) = @@ -1323,8 +1325,7 @@ impl WasmSdk { // Get identity contract nonce let identity_contract_nonce = sdk .get_identity_contract_nonce(owner_id, contract_id, true, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to fetch nonce: {}", e)))?; + .await?; // Create the state transition let platform_version = sdk.version(); @@ -1344,14 +1345,13 @@ impl WasmSdk { None, // state_transition_creation_options ) .map_err(|e| { - JsValue::from_str(&format!("Failed to create config update transition: {}", e)) + WasmSdkError::generic(format!("Failed to create config update transition: {}", e)) })?; // Broadcast the transition let proof_result = state_transition .broadcast_and_wait::(&sdk, None) - .await - .map_err(|e| JsValue::from_str(&format!("Failed to broadcast transition: {}", e)))?; + .await?; // Format and return result self.format_token_result(proof_result) diff --git a/packages/wasm-sdk/src/verify.rs b/packages/wasm-sdk/src/verify.rs index b926a63b774..012c63a5977 100644 --- a/packages/wasm-sdk/src/verify.rs +++ b/packages/wasm-sdk/src/verify.rs @@ -22,7 +22,7 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::context_provider::WasmContext; use crate::dpp::{DataContractWasm, IdentityWasm}; -#[wasm_bindgen] +#[wasm_bindgen(js_name = "verifyIdentityResponse")] pub async fn verify_identity_response() -> Option { let request = dash_sdk::dapi_grpc::platform::v0::GetIdentityRequest { version: Some(GetIdentityRequestVersion::V0(GetIdentityRequestV0 { @@ -67,7 +67,7 @@ pub async fn verify_identity_response() -> Option { response.map(IdentityWasm::from) } -#[wasm_bindgen] +#[wasm_bindgen(js_name = "verifyDataContract")] pub async fn verify_data_contract() -> Option { let request = dash_sdk::dapi_grpc::platform::v0::GetDataContractRequest { version: Some( @@ -125,7 +125,7 @@ pub async fn verify_data_contract() -> Option { response.map(DataContractWasm::from) } -#[wasm_bindgen] +#[wasm_bindgen(js_name = "verifyDocuments")] pub async fn verify_documents() -> Vec { // TODO: this is a dummy implementation, replace with actual verification let data_contract = diff --git a/packages/wasm-sdk/src/wallet/dip14.rs b/packages/wasm-sdk/src/wallet/dip14.rs index 124619d20ef..a1a5c3a3b56 100644 --- a/packages/wasm-sdk/src/wallet/dip14.rs +++ b/packages/wasm-sdk/src/wallet/dip14.rs @@ -8,7 +8,6 @@ use dash_sdk::dpp::dashcore::secp256k1::{self, PublicKey, Scalar, Secp256k1, Sec use dash_sdk::dpp::dashcore::Network; use dash_sdk::dpp::key_wallet; use dash_sdk::dpp::key_wallet::bip32::{ExtendedPrivKey, ExtendedPubKey}; -use hex; use hmac::{Hmac, Mac}; use sha2::Sha512; use std::convert::TryInto; @@ -140,31 +139,12 @@ impl Dip14ExtendedPrivKey { Dip14Error::DerivationFailed("Failed to convert IL to scalar".to_string()) })?; - // Debug: log parent and IL values before addition - web_sys::console::log_1( - &format!( - "Parent key: {}", - hex::encode(&self.private_key.secret_bytes()) - ) - .into(), - ); - web_sys::console::log_1(&format!("IL (tweak): {}", hex::encode(&il_scalar_bytes)).into()); - // Perform scalar addition: child_key = parent_key + IL (mod n) let child_key = self .private_key .add_tweak(&tweak) .map_err(|e| Dip14Error::DerivationFailed(format!("Failed to add tweak: {}", e)))?; - // Debug: log result after addition - web_sys::console::log_1( - &format!( - "Child key after addition: {}", - hex::encode(&child_key.secret_bytes()) - ) - .into(), - ); - // Calculate parent fingerprint (first 4 bytes of parent pubkey hash160) let parent_pubkey = PublicKey::from_secret_key(&secp, &self.private_key); // Use sha256 then ripemd160 to create hash160 diff --git a/packages/wasm-sdk/src/wallet/extended_derivation.rs b/packages/wasm-sdk/src/wallet/extended_derivation.rs index 6131dac4b8d..b97c0d89cef 100644 --- a/packages/wasm-sdk/src/wallet/extended_derivation.rs +++ b/packages/wasm-sdk/src/wallet/extended_derivation.rs @@ -2,223 +2,229 @@ //! //! Implements 256-bit derivation paths for DashPay contact keys -use crate::wallet::key_derivation::mnemonic_to_seed; +use crate::error::WasmSdkError; +use crate::WasmSdk; use dash_sdk::dpp::dashcore; use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; use dash_sdk::dpp::key_wallet::{bip32, DerivationPath, ExtendedPrivKey}; use std::str::FromStr; +use tracing::debug; use wasm_bindgen::prelude::*; -use web_sys; -/// Derive a key from seed phrase with extended path supporting 256-bit indices -/// This supports DIP14/DIP15 paths with identity IDs #[wasm_bindgen] -pub fn derive_key_from_seed_with_extended_path( - mnemonic: &str, - passphrase: Option, - path: &str, - network: &str, -) -> Result { - // Debug: Log the path being processed - web_sys::console::log_1(&format!("Processing extended path: {}", path).into()); - - // Get seed from mnemonic - let seed = mnemonic_to_seed(mnemonic, passphrase)?; - - let net = match network { - "mainnet" => dashcore::Network::Dash, - "testnet" => dashcore::Network::Testnet, - _ => return Err(JsError::new("Invalid network")), - }; - - // Create master extended private key from seed - let master_key = ExtendedPrivKey::new_master(net, &seed) - .map_err(|e| JsError::new(&format!("Failed to create master key: {}", e)))?; - - // Parse the derivation path using dashcore's built-in parser - // This already supports 256-bit hex values like 0x775d3854... - let derivation_path = DerivationPath::from_str(path) - .map_err(|e| JsError::new(&format!("Invalid derivation path: {}", e)))?; - - // Use dashcore's built-in derive_priv method which handles DIP14 - let secp = Secp256k1::new(); - let derived_key = master_key - .derive_priv(&secp, &derivation_path) - .map_err(|e| JsError::new(&format!("Failed to derive key: {}", e)))?; - - // Get the extended public key - let xpub = bip32::ExtendedPubKey::from_priv(&secp, &derived_key); - - // Get the private key - let private_key = dashcore::PrivateKey::new(derived_key.private_key, net); - - // Get public key - let public_key = private_key.public_key(&secp); - - // Get address - let address = dashcore::Address::p2pkh(&public_key, net); - - // Create result object - let obj = js_sys::Object::new(); - - js_sys::Reflect::set(&obj, &JsValue::from_str("path"), &JsValue::from_str(path)) - .map_err(|_| JsError::new("Failed to set path property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("private_key_wif"), - &JsValue::from_str(&private_key.to_wif()), - ) - .map_err(|_| JsError::new("Failed to set private_key_wif property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("private_key_hex"), - &JsValue::from_str(&hex::encode(private_key.inner.secret_bytes())), - ) - .map_err(|_| JsError::new("Failed to set private_key_hex property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("public_key"), - &JsValue::from_str(&hex::encode(public_key.to_bytes())), - ) - .map_err(|_| JsError::new("Failed to set public_key property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("address"), - &JsValue::from_str(&address.to_string()), - ) - .map_err(|_| JsError::new("Failed to set address property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("network"), - &JsValue::from_str(network), - ) - .map_err(|_| JsError::new("Failed to set network property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("xprv"), - &JsValue::from_str(&derived_key.to_string()), - ) - .map_err(|_| JsError::new("Failed to set xprv property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("xpub"), - &JsValue::from_str(&xpub.to_string()), - ) - .map_err(|_| JsError::new("Failed to set xpub property"))?; - - Ok(obj.into()) -} - -/// Derive a DashPay contact key using DIP15 with full identity IDs -#[wasm_bindgen] -pub fn derive_dashpay_contact_key( - mnemonic: &str, - passphrase: Option, - sender_identity_id: &str, - receiver_identity_id: &str, - account: u32, - address_index: u32, - network: &str, -) -> Result { - use bs58; - - // Convert base58 identity IDs to hex format if needed - let sender_id_formatted = if sender_identity_id.starts_with("0x") { - sender_identity_id.to_string() - } else { - // Decode base58 to bytes, then convert to hex - let bytes = bs58::decode(sender_identity_id) - .into_vec() - .map_err(|e| JsError::new(&format!("Invalid sender identity ID: {}", e)))?; - format!("0x{}", hex::encode(bytes)) - }; - - let receiver_id_formatted = if receiver_identity_id.starts_with("0x") { - receiver_identity_id.to_string() - } else { - // Decode base58 to bytes, then convert to hex - let bytes = bs58::decode(receiver_identity_id) - .into_vec() - .map_err(|e| JsError::new(&format!("Invalid receiver identity ID: {}", e)))?; - format!("0x{}", hex::encode(bytes)) - }; - - // Build the DIP15 path - // m / 9' / coin_type' / 15' / account' / sender_id / receiver_id / index - let coin_type = match network { - "mainnet" => 5, - "testnet" => 1, - _ => return Err(JsError::new("Invalid network")), - }; - - let path = format!( - "m/9'/{}'/{}'/{}'/{}/{}/{}", - coin_type, - 15, // DIP15 feature - account, - sender_id_formatted, - receiver_id_formatted, - address_index - ); - - web_sys::console::log_1(&format!("DashPay contact path: {}", path).into()); - - // Use the extended derivation function - let result = derive_key_from_seed_with_extended_path(mnemonic, passphrase, &path, network)?; - - // Add DIP15-specific metadata - let obj = result - .dyn_into::() - .map_err(|_| JsError::new("Failed to cast result to object"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("dipStandard"), - &JsValue::from_str("DIP15"), - ) - .map_err(|_| JsError::new("Failed to set dipStandard property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("purpose"), - &JsValue::from_str("DashPay Contact Payment"), - ) - .map_err(|_| JsError::new("Failed to set purpose property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("senderIdentity"), - &JsValue::from_str(sender_identity_id), - ) - .map_err(|_| JsError::new("Failed to set senderIdentity property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("receiverIdentity"), - &JsValue::from_str(receiver_identity_id), - ) - .map_err(|_| JsError::new("Failed to set receiverIdentity property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("account"), - &JsValue::from_f64(account as f64), - ) - .map_err(|_| JsError::new("Failed to set account property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("addressIndex"), - &JsValue::from_f64(address_index as f64), - ) - .map_err(|_| JsError::new("Failed to set addressIndex property"))?; - - Ok(obj.into()) +impl WasmSdk { + /// Derive a key from seed phrase with extended path supporting 256-bit indices + /// This supports DIP14/DIP15 paths with identity IDs + #[wasm_bindgen(js_name = "deriveKeyFromSeedWithExtendedPath")] + pub fn derive_key_from_seed_with_extended_path( + mnemonic: &str, + passphrase: Option, + path: &str, + network: &str, + ) -> Result { + // Debug: Log the path being processed + debug!(target: "wasm_sdk", path, "Processing extended path"); + + // Get seed from mnemonic + let seed = Self::mnemonic_to_seed(mnemonic, passphrase)?; + + let net = match network { + "mainnet" => dashcore::Network::Dash, + "testnet" => dashcore::Network::Testnet, + _ => return Err(WasmSdkError::invalid_argument("Invalid network")), + }; + + // Create master extended private key from seed + let master_key = ExtendedPrivKey::new_master(net, &seed) + .map_err(|e| WasmSdkError::generic(format!("Failed to create master key: {}", e)))?; + + // Parse the derivation path using dashcore's built-in parser + // This already supports 256-bit hex values like 0x775d3854... + let derivation_path = DerivationPath::from_str(path).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid derivation path: {}", e)) + })?; + + // Use dashcore's built-in derive_priv method which handles DIP14 + let secp = Secp256k1::new(); + let derived_key = master_key + .derive_priv(&secp, &derivation_path) + .map_err(|e| WasmSdkError::generic(format!("Failed to derive key: {}", e)))?; + + // Get the extended public key + let xpub = bip32::ExtendedPubKey::from_priv(&secp, &derived_key); + + // Get the private key + let private_key = dashcore::PrivateKey::new(derived_key.private_key, net); + + // Get public key + let public_key = private_key.public_key(&secp); + + // Get address + let address = dashcore::Address::p2pkh(&public_key, net); + + // Create result object + let obj = js_sys::Object::new(); + + js_sys::Reflect::set(&obj, &JsValue::from_str("path"), &JsValue::from_str(path)) + .map_err(|_| WasmSdkError::generic("Failed to set path property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("private_key_wif"), + &JsValue::from_str(&private_key.to_wif()), + ) + .map_err(|_| WasmSdkError::generic("Failed to set private_key_wif property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("private_key_hex"), + &JsValue::from_str(&hex::encode(private_key.inner.secret_bytes())), + ) + .map_err(|_| WasmSdkError::generic("Failed to set private_key_hex property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("public_key"), + &JsValue::from_str(&hex::encode(public_key.to_bytes())), + ) + .map_err(|_| WasmSdkError::generic("Failed to set public_key property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("address"), + &JsValue::from_str(&address.to_string()), + ) + .map_err(|_| WasmSdkError::generic("Failed to set address property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("network"), + &JsValue::from_str(network), + ) + .map_err(|_| WasmSdkError::generic("Failed to set network property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("xprv"), + &JsValue::from_str(&derived_key.to_string()), + ) + .map_err(|_| WasmSdkError::generic("Failed to set xprv property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("xpub"), + &JsValue::from_str(&xpub.to_string()), + ) + .map_err(|_| WasmSdkError::generic("Failed to set xpub property"))?; + + Ok(obj.into()) + } + + /// Derive a DashPay contact key using DIP15 with full identity IDs + #[wasm_bindgen(js_name = "deriveDashpayContactKey")] + pub fn derive_dashpay_contact_key( + mnemonic: &str, + passphrase: Option, + sender_identity_id: &str, + receiver_identity_id: &str, + account: u32, + address_index: u32, + network: &str, + ) -> Result { + use bs58; + + // Convert base58 identity IDs to hex format if needed + let sender_id_formatted = if sender_identity_id.starts_with("0x") { + sender_identity_id.to_string() + } else { + // Decode base58 to bytes, then convert to hex + let bytes = bs58::decode(sender_identity_id).into_vec().map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid sender identity ID: {}", e)) + })?; + format!("0x{}", hex::encode(bytes)) + }; + + let receiver_id_formatted = if receiver_identity_id.starts_with("0x") { + receiver_identity_id.to_string() + } else { + // Decode base58 to bytes, then convert to hex + let bytes = bs58::decode(receiver_identity_id).into_vec().map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid receiver identity ID: {}", e)) + })?; + format!("0x{}", hex::encode(bytes)) + }; + + // Build the DIP15 path + // m / 9' / coin_type' / 15' / account' / sender_id / receiver_id / index + let coin_type = match network { + "mainnet" => 5, + "testnet" => 1, + _ => return Err(WasmSdkError::invalid_argument("Invalid network")), + }; + + let path = format!( + "m/9'/{}'/{}'/{}'/{}/{}/{}", + coin_type, + 15, // DIP15 feature + account, + sender_id_formatted, + receiver_id_formatted, + address_index + ); + + debug!(target: "wasm_sdk", path = %path, "DashPay contact path"); + + // Use the extended derivation function + let result = + Self::derive_key_from_seed_with_extended_path(mnemonic, passphrase, &path, network)?; + + // Add DIP15-specific metadata + let obj = result + .dyn_into::() + .map_err(|_| WasmSdkError::generic("Failed to cast result to object"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("dipStandard"), + &JsValue::from_str("DIP15"), + ) + .map_err(|_| WasmSdkError::generic("Failed to set dipStandard property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("purpose"), + &JsValue::from_str("DashPay Contact Payment"), + ) + .map_err(|_| WasmSdkError::generic("Failed to set purpose property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("senderIdentity"), + &JsValue::from_str(sender_identity_id), + ) + .map_err(|_| WasmSdkError::generic("Failed to set senderIdentity property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("receiverIdentity"), + &JsValue::from_str(receiver_identity_id), + ) + .map_err(|_| WasmSdkError::generic("Failed to set receiverIdentity property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("account"), + &JsValue::from_f64(account as f64), + ) + .map_err(|_| WasmSdkError::generic("Failed to set account property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("addressIndex"), + &JsValue::from_f64(address_index as f64), + ) + .map_err(|_| WasmSdkError::generic("Failed to set addressIndex property"))?; + + Ok(obj.into()) + } } diff --git a/packages/wasm-sdk/src/wallet/key_derivation.rs b/packages/wasm-sdk/src/wallet/key_derivation.rs index 6f78ca68237..f7627cdb855 100644 --- a/packages/wasm-sdk/src/wallet/key_derivation.rs +++ b/packages/wasm-sdk/src/wallet/key_derivation.rs @@ -2,6 +2,8 @@ //! //! Implements BIP32, BIP39, and BIP44 standards for hierarchical deterministic key derivation +use crate::error::WasmSdkError; +use crate::WasmSdk; use bip39::{Language, Mnemonic}; use dash_sdk::dpp::dashcore; use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; @@ -85,6 +87,7 @@ impl DerivationPath { } /// Convert to string representation (e.g., "m/44'/5'/0'/0/0") + #[allow(clippy::inherent_to_string)] pub fn to_string(&self) -> String { format!( "m/{}'/{}'/{}'/{}/{}", @@ -93,17 +96,19 @@ impl DerivationPath { } /// Parse from string representation - pub fn from_string(path: &str) -> Result { + pub fn from_string(path: &str) -> Result { let parts: Vec<&str> = path.trim_start_matches("m/").split('/').collect(); if parts.len() != 5 { - return Err(JsError::new("Invalid derivation path format")); + return Err(WasmSdkError::invalid_argument( + "Invalid derivation path format", + )); } - let parse_hardened = |s: &str| -> Result { + let parse_hardened = |s: &str| -> Result { s.trim_end_matches('\'') .trim_end_matches('h') .parse::() - .map_err(|_| JsError::new("Invalid path component")) + .map_err(|_| WasmSdkError::invalid_argument("Invalid path component")) }; Ok(DerivationPath { @@ -112,237 +117,14 @@ impl DerivationPath { account: parse_hardened(parts[2])?, change: parts[3] .parse() - .map_err(|_| JsError::new("Invalid change index"))?, + .map_err(|_| WasmSdkError::invalid_argument("Invalid change index"))?, index: parts[4] .parse() - .map_err(|_| JsError::new("Invalid address index"))?, + .map_err(|_| WasmSdkError::invalid_argument("Invalid address index"))?, }) } } -/// Generate a new mnemonic phrase -#[wasm_bindgen] -pub fn generate_mnemonic( - word_count: Option, - language_code: Option, -) -> Result { - let words = word_count.unwrap_or(12); - - // Validate word count and calculate entropy bytes - let entropy_bytes = match words { - 12 => 16, // 128 bits - 15 => 20, // 160 bits - 18 => 24, // 192 bits - 21 => 28, // 224 bits - 24 => 32, // 256 bits - _ => return Err(JsError::new("Word count must be 12, 15, 18, 21, or 24")), - }; - - // Select language based on language code - let language = match language_code.as_deref() { - Some("en") | None => Language::English, - Some("zh-cn") | Some("zh_CN") | Some("zh-Hans") => Language::SimplifiedChinese, - Some("zh-tw") | Some("zh_TW") | Some("zh-Hant") => Language::TraditionalChinese, - Some("cs") => Language::Czech, - Some("fr") => Language::French, - Some("it") => Language::Italian, - Some("ja") => Language::Japanese, - Some("ko") => Language::Korean, - Some("pt") => Language::Portuguese, - Some("es") => Language::Spanish, - Some(code) => return Err(JsError::new(&format!("Unsupported language code: {}. Supported: en, zh-cn, zh-tw, cs, fr, it, ja, ko, pt, es", code))), - }; - - // Generate random entropy - let mut entropy = vec![0u8; entropy_bytes]; - thread_rng().fill_bytes(&mut entropy); - - // Create mnemonic from entropy - let mnemonic = Mnemonic::from_entropy_in(language, &entropy) - .map_err(|e| JsError::new(&format!("Failed to generate mnemonic: {}", e)))?; - - Ok(mnemonic.to_string()) -} - -/// Validate a mnemonic phrase -#[wasm_bindgen] -pub fn validate_mnemonic(mnemonic: &str, language_code: Option) -> bool { - // If language is specified, validate in that language - if let Some(code) = language_code { - let language = match code.as_str() { - "en" => Language::English, - "zh-cn" | "zh_CN" | "zh-Hans" => Language::SimplifiedChinese, - "zh-tw" | "zh_TW" | "zh-Hant" => Language::TraditionalChinese, - "cs" => Language::Czech, - "fr" => Language::French, - "it" => Language::Italian, - "ja" => Language::Japanese, - "ko" => Language::Korean, - "pt" => Language::Portuguese, - "es" => Language::Spanish, - _ => return false, - }; - return Mnemonic::parse_in(language, mnemonic).is_ok(); - } - - // Otherwise, try to parse in any language - Mnemonic::parse_normalized(mnemonic).is_ok() -} - -/// Derive a seed from a mnemonic phrase -#[wasm_bindgen] -pub fn mnemonic_to_seed(mnemonic: &str, passphrase: Option) -> Result, JsError> { - // Parse the mnemonic - let mnemonic = Mnemonic::parse_normalized(mnemonic) - .map_err(|e| JsError::new(&format!("Invalid mnemonic phrase: {}", e)))?; - - // Generate seed with optional passphrase - let seed = mnemonic.to_seed(passphrase.as_deref().unwrap_or("")); - - Ok(seed.to_vec()) -} - -/// Derive a key from mnemonic phrase using BIP39/BIP44 -#[wasm_bindgen] -pub fn derive_key_from_seed_phrase( - mnemonic: &str, - passphrase: Option, - network: &str, -) -> Result { - use crate::wallet::key_generation::KeyPair; - - // Get seed from mnemonic - let seed = mnemonic_to_seed(mnemonic, passphrase)?; - - // For now, we'll use the first 32 bytes of the seed as the private key - // Note: This is a simplified approach. Proper BIP32/BIP44 would use HD derivation - // with the path m/44'/5'/0'/0/0 for Dash mainnet or m/44'/1'/0'/0/0 for testnet - let key_bytes = if seed.len() >= 32 { - &seed[0..32] - } else { - // This shouldn't happen with BIP39, but handle it just in case - return Err(JsError::new("Seed too short")); - }; - - let net = match network { - "mainnet" => dashcore::Network::Dash, - "testnet" => dashcore::Network::Testnet, - _ => return Err(JsError::new("Invalid network")), - }; - - // Create private key from seed bytes - let key_array: [u8; 32] = key_bytes.try_into().map_err(|_| JsError::new("Seed must be 32 bytes"))?; - let private_key = dashcore::PrivateKey::from_byte_array(&key_array, net) - .map_err(|e| JsError::new(&format!("Failed to create private key: {}", e)))?; - - // Get public key - use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; - let secp = Secp256k1::new(); - - let public_key = private_key.public_key(&secp); - let public_key_bytes = public_key.inner.serialize(); - // Get address - let address = dashcore::Address::p2pkh(&public_key, net); - - let key_pair = KeyPair { - private_key_wif: private_key.to_wif(), - private_key_hex: hex::encode(key_bytes), - public_key: hex::encode(&public_key_bytes), - address: address.to_string(), - network: network.to_string(), - }; - - serde_wasm_bindgen::to_value(&key_pair) - .map_err(|e| JsError::new(&format!("Failed to serialize key pair: {}", e))) -} - -/// Derive a key from seed phrase with arbitrary path -#[wasm_bindgen] -pub fn derive_key_from_seed_with_path( - mnemonic: &str, - passphrase: Option, - path: &str, - network: &str, -) -> Result { - use dash_sdk::dpp::key_wallet::{DerivationPath, ExtendedPrivKey}; - - // Get seed from mnemonic - let seed = mnemonic_to_seed(mnemonic, passphrase)?; - - let net = match network { - "mainnet" => dashcore::Network::Dash, - "testnet" => dashcore::Network::Testnet, - _ => return Err(JsError::new("Invalid network")), - }; - - // Parse derivation path - let derivation_path = DerivationPath::from_str(path) - .map_err(|e| JsError::new(&format!("Invalid derivation path: {}", e)))?; - - // Create master extended private key from seed - let master_key = ExtendedPrivKey::new_master(net, &seed) - .map_err(|e| JsError::new(&format!("Failed to create master key: {}", e)))?; - - // Derive the key at the specified path - let derived_key = master_key - .derive_priv(&dashcore::secp256k1::Secp256k1::new(), &derivation_path) - .map_err(|e| JsError::new(&format!("Failed to derive key: {}", e)))?; - - // In v0.40-dev, ExtendedPrivKey might have a different structure - // Create a PrivateKey from the derived key - let private_key = dashcore::PrivateKey::new(derived_key.private_key, net); - - // Get public key - let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); - let public_key = private_key.public_key(&secp); - - // Get address - let address = dashcore::Address::p2pkh(&public_key, net); - - // Create a JavaScript object directly - let obj = js_sys::Object::new(); - - js_sys::Reflect::set(&obj, &JsValue::from_str("path"), &JsValue::from_str(path)) - .map_err(|_| JsError::new("Failed to set path property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("private_key_wif"), - &JsValue::from_str(&private_key.to_wif()), - ) - .map_err(|_| JsError::new("Failed to set private_key_wif property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("private_key_hex"), - &JsValue::from_str(&hex::encode(private_key.inner.secret_bytes())), - ) - .map_err(|_| JsError::new("Failed to set private_key_hex property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("public_key"), - &JsValue::from_str(&hex::encode(public_key.to_bytes())), - ) - .map_err(|_| JsError::new("Failed to set public_key property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("address"), - &JsValue::from_str(&address.to_string()), - ) - .map_err(|_| JsError::new("Failed to set address property"))?; - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("network"), - &JsValue::from_str(network), - ) - .map_err(|_| JsError::new("Failed to set network property"))?; - - Ok(obj.into()) -} - /// HD Key information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HDKeyInfo { @@ -360,164 +142,413 @@ pub struct HDKeyInfo { pub xprv: Option, } -/// Create a BIP44 mainnet derivation path #[wasm_bindgen] -pub fn derivation_path_bip44_mainnet(account: u32, change: u32, index: u32) -> JsValue { - let path = DerivationPath::new_bip44_mainnet(account, change, index); - serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) -} +impl WasmSdk { + /// Generate a new mnemonic phrase + #[wasm_bindgen(js_name = "generateMnemonic")] + pub fn generate_mnemonic( + word_count: Option, + language_code: Option, + ) -> Result { + let words = word_count.unwrap_or(12); + + // Validate word count and calculate entropy bytes + let entropy_bytes = match words { + 12 => 16, // 128 bits + 15 => 20, // 160 bits + 18 => 24, // 192 bits + 21 => 28, // 224 bits + 24 => 32, // 256 bits + _ => { + return Err(WasmSdkError::invalid_argument( + "Word count must be 12, 15, 18, 21, or 24", + )) + } + }; -/// Create a BIP44 testnet derivation path -#[wasm_bindgen] -pub fn derivation_path_bip44_testnet(account: u32, change: u32, index: u32) -> JsValue { - let path = DerivationPath::new_bip44_testnet(account, change, index); - serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) -} + // Select language based on language code + let language = match language_code.as_deref() { + Some("en") | None => Language::English, + Some("zh-cn") | Some("zh_CN") | Some("zh-Hans") => Language::SimplifiedChinese, + Some("zh-tw") | Some("zh_TW") | Some("zh-Hant") => Language::TraditionalChinese, + Some("cs") => Language::Czech, + Some("fr") => Language::French, + Some("it") => Language::Italian, + Some("ja") => Language::Japanese, + Some("ko") => Language::Korean, + Some("pt") => Language::Portuguese, + Some("es") => Language::Spanish, + Some(code) => { + return Err(WasmSdkError::invalid_argument(format!( + "Unsupported language code: {}. Supported: en, zh-cn, zh-tw, cs, fr, it, ja, ko, pt, es", + code + ))) + } + }; -/// Create a DIP9 mainnet derivation path -#[wasm_bindgen] -pub fn derivation_path_dip9_mainnet(feature_type: u32, account: u32, index: u32) -> JsValue { - let path = DerivationPath::new_dip9_mainnet(feature_type, account, index); - serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) -} + // Generate random entropy + let mut entropy = vec![0u8; entropy_bytes]; + thread_rng().fill_bytes(&mut entropy); -/// Create a DIP9 testnet derivation path -#[wasm_bindgen] -pub fn derivation_path_dip9_testnet(feature_type: u32, account: u32, index: u32) -> JsValue { - let path = DerivationPath::new_dip9_testnet(feature_type, account, index); - serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) -} + // Create mnemonic from entropy + let mnemonic = Mnemonic::from_entropy_in(language, &entropy) + .map_err(|e| WasmSdkError::generic(format!("Failed to generate mnemonic: {}", e)))?; -/// Create a DIP13 mainnet derivation path (for HD masternode keys) -#[wasm_bindgen] -pub fn derivation_path_dip13_mainnet(account: u32) -> JsValue { - // DIP13 uses m/9'/5'/account' format (DIP13 uses purpose 9, not 13) - let path_str = format!("m/{}'/{}'/{}'", DIP13_PURPOSE, DASH_COIN_TYPE, account); - - let obj = js_sys::Object::new(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("path"), - &JsValue::from_str(&path_str), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("purpose"), - &JsValue::from_f64(DIP13_PURPOSE as f64), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("coin_type"), - &JsValue::from_f64(DASH_COIN_TYPE as f64), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("account"), - &JsValue::from_f64(account as f64), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("description"), - &JsValue::from_str("DIP13 HD identity key path"), - ) - .unwrap(); - - obj.into() -} + Ok(mnemonic.to_string()) + } -/// Create a DIP13 testnet derivation path (for HD masternode keys) -#[wasm_bindgen] -pub fn derivation_path_dip13_testnet(account: u32) -> JsValue { - // DIP13 uses m/9'/1'/account' format for testnet - let path_str = format!("m/{}'/{}'/{}'", DIP13_PURPOSE, TESTNET_COIN_TYPE, account); - - let obj = js_sys::Object::new(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("path"), - &JsValue::from_str(&path_str), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("purpose"), - &JsValue::from_f64(DIP13_PURPOSE as f64), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("coin_type"), - &JsValue::from_f64(TESTNET_COIN_TYPE as f64), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("account"), - &JsValue::from_f64(account as f64), - ) - .unwrap(); - - js_sys::Reflect::set( - &obj, - &JsValue::from_str("description"), - &JsValue::from_str("DIP13 HD identity key path (testnet)"), - ) - .unwrap(); - - obj.into() -} + /// Validate a mnemonic phrase + #[wasm_bindgen(js_name = "validateMnemonic")] + pub fn validate_mnemonic(mnemonic: &str, language_code: Option) -> bool { + // If language is specified, validate in that language + if let Some(code) = language_code { + let language = match code.as_str() { + "en" => Language::English, + "zh-cn" | "zh_CN" | "zh-Hans" => Language::SimplifiedChinese, + "zh-tw" | "zh_TW" | "zh-Hant" => Language::TraditionalChinese, + "cs" => Language::Czech, + "fr" => Language::French, + "it" => Language::Italian, + "ja" => Language::Japanese, + "ko" => Language::Korean, + "pt" => Language::Portuguese, + "es" => Language::Spanish, + _ => return false, + }; + return Mnemonic::parse_in(language, mnemonic).is_ok(); + } -/// Get child public key from extended public key -#[wasm_bindgen] -pub fn derive_child_public_key(xpub: &str, index: u32, hardened: bool) -> Result { - if hardened { - return Err(JsError::new( - "Cannot derive hardened child from extended public key", - )); + // Otherwise, try to parse in any language + Mnemonic::parse_normalized(mnemonic).is_ok() } - // Disallow indices in the hardened range for non-hardened derivation - if index >= 0x8000_0000 { - return Err(JsError::new( - "Index is in hardened range; use a value < 2^31", - )); + /// Derive a seed from a mnemonic phrase + #[wasm_bindgen(js_name = "mnemonicToSeed")] + pub fn mnemonic_to_seed( + mnemonic: &str, + passphrase: Option, + ) -> Result, WasmSdkError> { + // Parse the mnemonic + let mnemonic = Mnemonic::parse_normalized(mnemonic).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid mnemonic phrase: {}", e)) + })?; + + // Generate seed with optional passphrase + let seed = mnemonic.to_seed(passphrase.as_deref().unwrap_or("")); + + Ok(seed.to_vec()) } - // Parse the extended public key - let parent_xpub = BIP32ExtendedPubKey::from_str(xpub) - .map_err(|e| JsError::new(&format!("Invalid extended public key: {}", e)))?; + /// Derive a key from mnemonic phrase using BIP39/BIP44 + #[wasm_bindgen(js_name = "deriveKeyFromSeedPhrase")] + pub fn derive_key_from_seed_phrase( + mnemonic: &str, + passphrase: Option, + network: &str, + ) -> Result { + use crate::wallet::key_generation::KeyPair; + + // Get seed from mnemonic + let seed = Self::mnemonic_to_seed(mnemonic, passphrase)?; + + // For now, we'll use the first 32 bytes of the seed as the private key + // Note: This is a simplified approach. Proper BIP32/BIP44 would use HD derivation + // with the path m/44'/5'/0'/0/0 for Dash mainnet or m/44'/1'/0'/0/0 for testnet + let key_bytes = if seed.len() >= 32 { + &seed[0..32] + } else { + // This shouldn't happen with BIP39, but handle it just in case + return Err(WasmSdkError::generic("Seed too short")); + }; - // Build a one-step derivation path and derive - let child_number: ChildNumber = ChildNumber::from(index); - let path = BIP32DerivationPath::from(vec![child_number]); - let secp = Secp256k1::new(); - let child_xpub = parent_xpub - .derive_pub(&secp, &path) - .map_err(|e| JsError::new(&format!("Failed to derive child key: {}", e)))?; + let net = match network { + "mainnet" => dashcore::Network::Dash, + "testnet" => dashcore::Network::Testnet, + _ => return Err(WasmSdkError::invalid_argument("Invalid network")), + }; - Ok(child_xpub.to_string()) -} + // Create private key from seed bytes + let key_array: [u8; 32] = key_bytes + .try_into() + .map_err(|_| WasmSdkError::invalid_argument("Seed must be 32 bytes"))?; + let private_key = dashcore::PrivateKey::from_byte_array(&key_array, net) + .map_err(|e| WasmSdkError::generic(format!("Failed to create private key: {}", e)))?; + + // Get public key + use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; + let secp = Secp256k1::new(); + + let public_key = private_key.public_key(&secp); + let public_key_bytes = public_key.inner.serialize(); + // Get address + let address = dashcore::Address::p2pkh(&public_key, net); + + let key_pair = KeyPair { + private_key_wif: private_key.to_wif(), + private_key_hex: hex::encode(key_bytes), + public_key: hex::encode(public_key_bytes), + address: address.to_string(), + network: network.to_string(), + }; -/// Convert extended private key to extended public key -#[wasm_bindgen] -pub fn xprv_to_xpub(xprv: &str) -> Result { - // Parse the extended private key and convert to extended public key - let ext_prv = BIP32ExtendedPrivKey::from_str(xprv) - .map_err(|e| JsError::new(&format!("Invalid extended private key: {}", e)))?; - let secp = Secp256k1::new(); - let ext_pub = BIP32ExtendedPubKey::from_priv(&secp, &ext_prv); - Ok(ext_pub.to_string()) + serde_wasm_bindgen::to_value(&key_pair).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize key pair: {}", e)) + }) + } + + /// Derive a key from seed phrase with arbitrary path + #[wasm_bindgen(js_name = "deriveKeyFromSeedWithPath")] + pub fn derive_key_from_seed_with_path( + mnemonic: &str, + passphrase: Option, + path: &str, + network: &str, + ) -> Result { + use dash_sdk::dpp::key_wallet::{DerivationPath, ExtendedPrivKey}; + + // Get seed from mnemonic + let seed = Self::mnemonic_to_seed(mnemonic, passphrase)?; + + let net = match network { + "mainnet" => dashcore::Network::Dash, + "testnet" => dashcore::Network::Testnet, + _ => return Err(WasmSdkError::invalid_argument("Invalid network")), + }; + + // Parse derivation path + let derivation_path = DerivationPath::from_str(path).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid derivation path: {}", e)) + })?; + + // Create master extended private key from seed + let master_key = ExtendedPrivKey::new_master(net, &seed) + .map_err(|e| WasmSdkError::generic(format!("Failed to create master key: {}", e)))?; + + // Derive the key at the specified path + let derived_key = master_key + .derive_priv(&dashcore::secp256k1::Secp256k1::new(), &derivation_path) + .map_err(|e| WasmSdkError::generic(format!("Failed to derive key: {}", e)))?; + + // In v0.40-dev, ExtendedPrivKey might have a different structure + // Create a PrivateKey from the derived key + let private_key = dashcore::PrivateKey::new(derived_key.private_key, net); + + // Get public key + let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); + let public_key = private_key.public_key(&secp); + + // Get address + let address = dashcore::Address::p2pkh(&public_key, net); + + // Create a JavaScript object directly + let obj = js_sys::Object::new(); + + js_sys::Reflect::set(&obj, &JsValue::from_str("path"), &JsValue::from_str(path)) + .map_err(|_| WasmSdkError::generic("Failed to set path property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("private_key_wif"), + &JsValue::from_str(&private_key.to_wif()), + ) + .map_err(|_| WasmSdkError::generic("Failed to set private_key_wif property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("private_key_hex"), + &JsValue::from_str(&hex::encode(private_key.inner.secret_bytes())), + ) + .map_err(|_| WasmSdkError::generic("Failed to set private_key_hex property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("public_key"), + &JsValue::from_str(&hex::encode(public_key.to_bytes())), + ) + .map_err(|_| WasmSdkError::generic("Failed to set public_key property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("address"), + &JsValue::from_str(&address.to_string()), + ) + .map_err(|_| WasmSdkError::generic("Failed to set address property"))?; + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("network"), + &JsValue::from_str(network), + ) + .map_err(|_| WasmSdkError::generic("Failed to set network property"))?; + + Ok(obj.into()) + } + + /// Create a BIP44 mainnet derivation path + #[wasm_bindgen(js_name = "derivationPathBip44Mainnet")] + pub fn derivation_path_bip44_mainnet(account: u32, change: u32, index: u32) -> JsValue { + let path = DerivationPath::new_bip44_mainnet(account, change, index); + serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) + } + + /// Create a BIP44 testnet derivation path + #[wasm_bindgen(js_name = "derivationPathBip44Testnet")] + pub fn derivation_path_bip44_testnet(account: u32, change: u32, index: u32) -> JsValue { + let path = DerivationPath::new_bip44_testnet(account, change, index); + serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) + } + + /// Create a DIP9 mainnet derivation path + #[wasm_bindgen(js_name = "derivationPathDip9Mainnet")] + pub fn derivation_path_dip9_mainnet(feature_type: u32, account: u32, index: u32) -> JsValue { + let path = DerivationPath::new_dip9_mainnet(feature_type, account, index); + serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) + } + + /// Create a DIP9 testnet derivation path + #[wasm_bindgen(js_name = "derivationPathDip9Testnet")] + pub fn derivation_path_dip9_testnet(feature_type: u32, account: u32, index: u32) -> JsValue { + let path = DerivationPath::new_dip9_testnet(feature_type, account, index); + serde_wasm_bindgen::to_value(&path).unwrap_or(JsValue::NULL) + } + + /// Create a DIP13 mainnet derivation path (for HD masternode keys) + #[wasm_bindgen(js_name = "derivationPathDip13Mainnet")] + pub fn derivation_path_dip13_mainnet(account: u32) -> JsValue { + // DIP13 uses m/9'/5'/account' format (DIP13 uses purpose 9, not 13) + let path_str = format!("m/{}'/{}'/{}'", DIP13_PURPOSE, DASH_COIN_TYPE, account); + + let obj = js_sys::Object::new(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("path"), + &JsValue::from_str(&path_str), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("purpose"), + &JsValue::from_f64(DIP13_PURPOSE as f64), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("coin_type"), + &JsValue::from_f64(DASH_COIN_TYPE as f64), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("account"), + &JsValue::from_f64(account as f64), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("description"), + &JsValue::from_str("DIP13 HD identity key path"), + ) + .unwrap(); + + obj.into() + } + + /// Create a DIP13 testnet derivation path (for HD masternode keys) + #[wasm_bindgen(js_name = "derivationPathDip13Testnet")] + pub fn derivation_path_dip13_testnet(account: u32) -> JsValue { + // DIP13 uses m/9'/1'/account' format for testnet + let path_str = format!("m/{}'/{}'/{}'", DIP13_PURPOSE, TESTNET_COIN_TYPE, account); + + let obj = js_sys::Object::new(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("path"), + &JsValue::from_str(&path_str), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("purpose"), + &JsValue::from_f64(DIP13_PURPOSE as f64), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("coin_type"), + &JsValue::from_f64(TESTNET_COIN_TYPE as f64), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("account"), + &JsValue::from_f64(account as f64), + ) + .unwrap(); + + js_sys::Reflect::set( + &obj, + &JsValue::from_str("description"), + &JsValue::from_str("DIP13 HD identity key path (testnet)"), + ) + .unwrap(); + + obj.into() + } + + /// Get child public key from extended public key + #[wasm_bindgen(js_name = "deriveChildPublicKey")] + pub fn derive_child_public_key( + xpub: &str, + index: u32, + hardened: bool, + ) -> Result { + if hardened { + return Err(WasmSdkError::invalid_argument( + "Cannot derive hardened child from extended public key", + )); + } + + // Disallow indices in the hardened range for non-hardened derivation + if index >= 0x8000_0000 { + return Err(WasmSdkError::invalid_argument( + "Index is in hardened range; use a value < 2^31", + )); + } + + // Parse the extended public key + let parent_xpub = BIP32ExtendedPubKey::from_str(xpub).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid extended public key: {}", e)) + })?; + + // Build a one-step derivation path and derive + let child_number: ChildNumber = ChildNumber::from(index); + let path = BIP32DerivationPath::from(vec![child_number]); + let secp = Secp256k1::new(); + let child_xpub = parent_xpub + .derive_pub(&secp, &path) + .map_err(|e| WasmSdkError::generic(format!("Failed to derive child key: {}", e)))?; + + Ok(child_xpub.to_string()) + } + + /// Convert extended private key to extended public key + #[wasm_bindgen(js_name = "xprvToXpub")] + pub fn xprv_to_xpub(xprv: &str) -> Result { + // Parse the extended private key and convert to extended public key + let ext_prv = BIP32ExtendedPrivKey::from_str(xprv).map_err(|e| { + WasmSdkError::invalid_argument(format!("Invalid extended private key: {}", e)) + })?; + let secp = Secp256k1::new(); + let ext_pub = BIP32ExtendedPubKey::from_priv(&secp, &ext_prv); + Ok(ext_pub.to_string()) + } } diff --git a/packages/wasm-sdk/src/wallet/key_generation.rs b/packages/wasm-sdk/src/wallet/key_generation.rs index c50d4f0bcf1..1cbc8d30540 100644 --- a/packages/wasm-sdk/src/wallet/key_generation.rs +++ b/packages/wasm-sdk/src/wallet/key_generation.rs @@ -2,6 +2,8 @@ //! //! Provides key generation and address derivation without full HD wallet support +use crate::error::WasmSdkError; +use crate::WasmSdk; use dash_sdk::dpp::dashcore::hashes::{sha256, Hash}; use dash_sdk::dpp::dashcore::secp256k1::{Secp256k1, SecretKey}; use dash_sdk::dpp::dashcore::{Address, Network, PrivateKey, PublicKey}; @@ -24,182 +26,207 @@ pub struct KeyPair { pub network: String, } -/// Generate a new random key pair #[wasm_bindgen] -pub fn generate_key_pair(network: &str) -> Result { - let net = match network { - "mainnet" => Network::Dash, - "testnet" => Network::Testnet, - _ => return Err(JsError::new("Invalid network. Use 'mainnet' or 'testnet'")), - }; - - // Generate random 32 bytes - let mut key_bytes = [0u8; 32]; - getrandom::getrandom(&mut key_bytes) - .map_err(|e| JsError::new(&format!("Failed to generate random bytes: {}", e)))?; - - // Create private key - let private_key = PrivateKey::from_byte_array(&key_bytes, net) - .map_err(|e| JsError::new(&format!("Failed to create private key: {}", e)))?; - - // Get public key - let secp = Secp256k1::new(); - let secret_key = SecretKey::from_slice(&key_bytes) - .map_err(|e| JsError::new(&format!("Invalid secret key: {}", e)))?; - let public_key = - dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); - let public_key_bytes = public_key.serialize(); - - // Get address - let address = Address::p2pkh( - &PublicKey::from_slice(&public_key_bytes) - .map_err(|e| JsError::new(&format!("Failed to create public key: {}", e)))?, - net, - ); - - let key_pair = KeyPair { - private_key_wif: private_key.to_wif(), - private_key_hex: hex::encode(&key_bytes), - public_key: hex::encode(&public_key_bytes), - address: address.to_string(), - network: network.to_string(), - }; - - serde_wasm_bindgen::to_value(&key_pair) - .map_err(|e| JsError::new(&format!("Failed to serialize key pair: {}", e))) -} - -/// Generate multiple key pairs -#[wasm_bindgen] -pub fn generate_key_pairs(network: &str, count: u32) -> Result, JsError> { - if count == 0 || count > 100 { - return Err(JsError::new("Count must be between 1 and 100")); +impl WasmSdk { + /// Generate a new random key pair + #[wasm_bindgen(js_name = "generateKeyPair")] + pub fn generate_key_pair(network: &str) -> Result { + let net = match network { + "mainnet" => Network::Dash, + "testnet" => Network::Testnet, + _ => { + return Err(WasmSdkError::invalid_argument( + "Invalid network. Use 'mainnet' or 'testnet'", + )) + } + }; + + // Generate random 32 bytes + let mut key_bytes = [0u8; 32]; + getrandom::getrandom(&mut key_bytes).map_err(|e| { + WasmSdkError::generic(format!("Failed to generate random bytes: {}", e)) + })?; + + // Create private key + let private_key = PrivateKey::from_byte_array(&key_bytes, net) + .map_err(|e| WasmSdkError::generic(format!("Failed to create private key: {}", e)))?; + + // Get public key + let secp = Secp256k1::new(); + let secret_key = SecretKey::from_slice(&key_bytes) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; + let public_key = + dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); + let public_key_bytes = public_key.serialize(); + + // Get address + let address = Address::p2pkh( + &PublicKey::from_slice(&public_key_bytes).map_err(|e| { + WasmSdkError::generic(format!("Failed to create public key: {}", e)) + })?, + net, + ); + + let key_pair = KeyPair { + private_key_wif: private_key.to_wif(), + private_key_hex: hex::encode(key_bytes), + public_key: hex::encode(public_key_bytes), + address: address.to_string(), + network: network.to_string(), + }; + + serde_wasm_bindgen::to_value(&key_pair).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize key pair: {}", e)) + }) } - let mut pairs = Vec::new(); - for _ in 0..count { - pairs.push(generate_key_pair(network)?); + /// Generate multiple key pairs + #[wasm_bindgen(js_name = "generateKeyPairs")] + pub fn generate_key_pairs(network: &str, count: u32) -> Result, WasmSdkError> { + if count == 0 || count > 100 { + return Err(WasmSdkError::invalid_argument( + "Count must be between 1 and 100", + )); + } + + let mut pairs = Vec::new(); + for _ in 0..count { + pairs.push(Self::generate_key_pair(network)?); + } + Ok(pairs) } - Ok(pairs) -} - -/// Create key pair from private key WIF -#[wasm_bindgen] -pub fn key_pair_from_wif(private_key_wif: &str) -> Result { - let private_key = PrivateKey::from_wif(private_key_wif) - .map_err(|e| JsError::new(&format!("Invalid WIF: {}", e)))?; - - let network = match private_key.network { - Network::Dash => "mainnet", - Network::Testnet => "testnet", - _ => return Err(JsError::new("Unsupported network")), - }; - - // Get public key - let secp = Secp256k1::new(); - let secret_key = SecretKey::from_slice(&private_key.inner.secret_bytes()) - .map_err(|e| JsError::new(&format!("Invalid secret key: {}", e)))?; - let public_key = - dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); - let public_key_bytes = public_key.serialize(); - - // Get address - let address = Address::p2pkh( - &PublicKey::from_slice(&public_key_bytes) - .map_err(|e| JsError::new(&format!("Failed to create public key: {}", e)))?, - private_key.network, - ); - - let key_pair = KeyPair { - private_key_wif: private_key_wif.to_string(), - private_key_hex: hex::encode(&private_key.inner.secret_bytes()), - public_key: hex::encode(&public_key_bytes), - address: address.to_string(), - network: network.to_string(), - }; - - serde_wasm_bindgen::to_value(&key_pair) - .map_err(|e| JsError::new(&format!("Failed to serialize key pair: {}", e))) -} -/// Create key pair from private key hex -#[wasm_bindgen] -pub fn key_pair_from_hex(private_key_hex: &str, network: &str) -> Result { - if private_key_hex.len() != 64 { - return Err(JsError::new( - "Private key hex must be exactly 64 characters", - )); + /// Create key pair from private key WIF + #[wasm_bindgen(js_name = "keyPairFromWif")] + pub fn key_pair_from_wif(private_key_wif: &str) -> Result { + let private_key = PrivateKey::from_wif(private_key_wif) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid WIF: {}", e)))?; + + let network = match private_key.network { + Network::Dash => "mainnet", + Network::Testnet => "testnet", + _ => return Err(WasmSdkError::invalid_argument("Unsupported network")), + }; + + // Get public key + let secp = Secp256k1::new(); + let secret_key = SecretKey::from_slice(&private_key.inner.secret_bytes()) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; + let public_key = + dash_sdk::dpp::dashcore::secp256k1::PublicKey::from_secret_key(&secp, &secret_key); + let public_key_bytes = public_key.serialize(); + + // Get address + let address = Address::p2pkh( + &PublicKey::from_slice(&public_key_bytes).map_err(|e| { + WasmSdkError::generic(format!("Failed to create public key: {}", e)) + })?, + private_key.network, + ); + + let key_pair = KeyPair { + private_key_wif: private_key_wif.to_string(), + private_key_hex: hex::encode(private_key.inner.secret_bytes()), + public_key: hex::encode(public_key_bytes), + address: address.to_string(), + network: network.to_string(), + }; + + serde_wasm_bindgen::to_value(&key_pair).map_err(|e| { + WasmSdkError::serialization(format!("Failed to serialize key pair: {}", e)) + }) } - let net = match network { - "mainnet" => Network::Dash, - "testnet" => Network::Testnet, - _ => return Err(JsError::new("Invalid network. Use 'mainnet' or 'testnet'")), - }; - - let key_bytes = - hex::decode(private_key_hex).map_err(|e| JsError::new(&format!("Invalid hex: {}", e)))?; - - let key_array: [u8; 32] = key_bytes - .try_into() - .map_err(|_| JsError::new("Private key bytes must be 32 bytes"))?; - let private_key = PrivateKey::from_byte_array(&key_array, net) - .map_err(|e| JsError::new(&format!("Failed to create private key: {}", e)))?; - - key_pair_from_wif(&private_key.to_wif()) -} - -/// Get address from public key -#[wasm_bindgen] -pub fn pubkey_to_address(pubkey_hex: &str, network: &str) -> Result { - let net = match network { - "mainnet" => Network::Dash, - "testnet" => Network::Testnet, - _ => return Err(JsError::new("Invalid network. Use 'mainnet' or 'testnet'")), - }; - - let pubkey_bytes = - hex::decode(pubkey_hex).map_err(|e| JsError::new(&format!("Invalid hex: {}", e)))?; - - let public_key = PublicKey::from_slice(&pubkey_bytes) - .map_err(|e| JsError::new(&format!("Invalid public key: {}", e)))?; + /// Create key pair from private key hex + #[wasm_bindgen(js_name = "keyPairFromHex")] + pub fn key_pair_from_hex( + private_key_hex: &str, + network: &str, + ) -> Result { + if private_key_hex.len() != 64 { + return Err(WasmSdkError::invalid_argument( + "Private key hex must be exactly 64 characters", + )); + } + + let net = match network { + "mainnet" => Network::Dash, + "testnet" => Network::Testnet, + _ => { + return Err(WasmSdkError::invalid_argument( + "Invalid network. Use 'mainnet' or 'testnet'", + )) + } + }; + + let key_bytes = hex::decode(private_key_hex) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid hex: {}", e)))?; + + let key_array: [u8; 32] = key_bytes + .try_into() + .map_err(|_| WasmSdkError::invalid_argument("Private key bytes must be 32 bytes"))?; + let private_key = PrivateKey::from_byte_array(&key_array, net) + .map_err(|e| WasmSdkError::generic(format!("Failed to create private key: {}", e)))?; + + Self::key_pair_from_wif(&private_key.to_wif()) + } - let address = Address::p2pkh(&public_key, net); - Ok(address.to_string()) -} + /// Get address from public key + #[wasm_bindgen(js_name = "pubkeyToAddress")] + pub fn pubkey_to_address(pubkey_hex: &str, network: &str) -> Result { + let net = match network { + "mainnet" => Network::Dash, + "testnet" => Network::Testnet, + _ => { + return Err(WasmSdkError::invalid_argument( + "Invalid network. Use 'mainnet' or 'testnet'", + )) + } + }; + + let pubkey_bytes = hex::decode(pubkey_hex) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid hex: {}", e)))?; + + let public_key = PublicKey::from_slice(&pubkey_bytes) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid public key: {}", e)))?; + + let address = Address::p2pkh(&public_key, net); + Ok(address.to_string()) + } -/// Validate a Dash address -#[wasm_bindgen] -pub fn validate_address(address: &str, network: &str) -> bool { - let net = match network { - "mainnet" => Network::Dash, - "testnet" => Network::Testnet, - _ => return false, - }; - - Address::from_str(address) - .map(|addr| *addr.network() == net) - .unwrap_or(false) -} + /// Validate a Dash address + #[wasm_bindgen(js_name = "validateAddress")] + pub fn validate_address(address: &str, network: &str) -> bool { + let net = match network { + "mainnet" => Network::Dash, + "testnet" => Network::Testnet, + _ => return false, + }; + + Address::from_str(address) + .map(|addr| *addr.network() == net) + .unwrap_or(false) + } -/// Sign a message with a private key -#[wasm_bindgen] -pub fn sign_message(message: &str, private_key_wif: &str) -> Result { - let private_key = PrivateKey::from_wif(private_key_wif) - .map_err(|e| JsError::new(&format!("Invalid WIF: {}", e)))?; + /// Sign a message with a private key + #[wasm_bindgen(js_name = "signMessage")] + pub fn sign_message(message: &str, private_key_wif: &str) -> Result { + let private_key = PrivateKey::from_wif(private_key_wif) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid WIF: {}", e)))?; - // Create message hash - let message_bytes = message.as_bytes(); - let hash = sha256::Hash::hash(message_bytes); + // Create message hash + let message_bytes = message.as_bytes(); + let hash = sha256::Hash::hash(message_bytes); - // Sign the hash - let secp = Secp256k1::new(); - let secret_key = SecretKey::from_slice(&private_key.inner.secret_bytes()) - .map_err(|e| JsError::new(&format!("Invalid secret key: {}", e)))?; + // Sign the hash + let secp = Secp256k1::new(); + let secret_key = SecretKey::from_slice(&private_key.inner.secret_bytes()) + .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; - let message_hash = - dash_sdk::dpp::dashcore::secp256k1::Message::from_digest(hash.to_byte_array()); - let signature = secp.sign_ecdsa(&message_hash, &secret_key); + let message_hash = + dash_sdk::dpp::dashcore::secp256k1::Message::from_digest(hash.to_byte_array()); + let signature = secp.sign_ecdsa(&message_hash, &secret_key); - Ok(hex::encode(signature.serialize_compact())) + Ok(hex::encode(signature.serialize_compact())) + } } diff --git a/packages/wasm-sdk/src/wallet/mod.rs b/packages/wasm-sdk/src/wallet/mod.rs index d3f282883ac..52ebdd17c0b 100644 --- a/packages/wasm-sdk/src/wallet/mod.rs +++ b/packages/wasm-sdk/src/wallet/mod.rs @@ -12,6 +12,5 @@ pub mod extended_derivation; pub mod key_derivation; pub mod key_generation; -pub use extended_derivation::*; pub use key_derivation::*; pub use key_generation::*; diff --git a/packages/wasm-sdk/test-address-validation.mjs b/packages/wasm-sdk/test-address-validation.mjs deleted file mode 100644 index 9c8a576667d..00000000000 --- a/packages/wasm-sdk/test-address-validation.mjs +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env node -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -import init, * as wasmSdk from './pkg/wasm_sdk.js'; - -const wasmPath = join(__dirname, 'pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -console.log('Testing address validation...\n'); - -// Test addresses from the failing test -const mainnetAddress = "XdRhagDMpNbHZSvgMXqkcCCWmrDYYty5Nh"; -const testnetAddress = "yXdRhagDMpNbHZSvgMXqkcCCWmrDYYty5Nh"; - -console.log(`Mainnet address: ${mainnetAddress}`); -console.log(`Validate on mainnet: ${wasmSdk.validate_address(mainnetAddress, "mainnet")}`); -console.log(`Validate on testnet: ${wasmSdk.validate_address(mainnetAddress, "testnet")}`); - -console.log(`\nTestnet address: ${testnetAddress}`); -console.log(`Validate on mainnet: ${wasmSdk.validate_address(testnetAddress, "mainnet")}`); -console.log(`Validate on testnet: ${wasmSdk.validate_address(testnetAddress, "testnet")}`); - -// Generate a real address to test -console.log('\nGenerating real addresses...'); -const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - -const mainnetKey = wasmSdk.derive_key_from_seed_with_path( - mnemonic, - undefined, - "m/44'/5'/0'/0/0", - "mainnet" -); - -const testnetKey = wasmSdk.derive_key_from_seed_with_path( - mnemonic, - undefined, - "m/44'/1'/0'/0/0", - "testnet" -); - -console.log(`\nGenerated mainnet address: ${mainnetKey.address}`); -console.log(`Validate on mainnet: ${wasmSdk.validate_address(mainnetKey.address, "mainnet")}`); - -console.log(`\nGenerated testnet address: ${testnetKey.address}`); -console.log(`Validate on testnet: ${wasmSdk.validate_address(testnetKey.address, "testnet")}`); \ No newline at end of file diff --git a/packages/wasm-sdk/test-base58.html b/packages/wasm-sdk/test-base58.html deleted file mode 100644 index 6e4ff0fd84e..00000000000 --- a/packages/wasm-sdk/test-base58.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Test Base58 Identity IDs - - -

    Testing Base58 Identity IDs in DashPay Keys

    -
    - - - \ No newline at end of file diff --git a/packages/wasm-sdk/test/COMPREHENSIVE_TEST_SUMMARY.md b/packages/wasm-sdk/test/COMPREHENSIVE_TEST_SUMMARY.md deleted file mode 100644 index 44653c49a95..00000000000 --- a/packages/wasm-sdk/test/COMPREHENSIVE_TEST_SUMMARY.md +++ /dev/null @@ -1,98 +0,0 @@ -# WASM SDK Comprehensive Test Suite - Summary - -## Overview - -A complete unit test suite has been created for the Dash Platform WASM SDK, covering all exported functions and capabilities. The test suite is designed to run in Node.js using ES modules and includes proper WASM initialization, crypto polyfills, and comprehensive test coverage. - -## Test Files Created - -### 1. Core Functionality Tests -- **sdk-init-simple.test.mjs** - SDK initialization and builder patterns (10 tests) -- **key-generation.test.mjs** - BIP39/BIP32/BIP44 key derivation and address generation (53 tests) -- **dpns.test.mjs** - DPNS-specific functions and validation (34 tests) -- **utilities-simple.test.mjs** - Utility functions and helpers (14 tests) - -### 2. Query Function Tests -- **identity-queries.test.mjs** - Identity-related queries using documented testnet values (12 tests) -- **document-queries.test.mjs** - Document queries with where/orderBy clauses (13 tests) -- **specialized-queries.test.mjs** - Masternode, group, and specialized queries (13 tests) - -### 3. State Transition Tests -- **state-transitions.test.mjs** - All state transition functions (identity, document, token, contract) (18 tests) - -### 4. Proof Verification Tests -- **proof-verification.test.mjs** - Proof verification and validation (8 tests) - -### 5. Infrastructure -- **run-all-tests.mjs** - Main test runner with HTML report generation -- **test-plan.md** - Comprehensive test planning document -- **EXPECTED_FAILURES.md** - Documentation of known issues and expected failures - -## Key Achievements - -### 1. Complete Coverage -- **175 total tests** covering all WASM SDK exports -- Tests organized into logical categories -- Both positive and negative test cases included - -### 2. Proper Test Values -- Uses documented testnet values from docs.html: - - Test Identity: `5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk` - - DPNS Contract: `GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec` - - Token Contract: `Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv` - -### 3. Known Issues Documented -- Path helper functions (bip44_path_dash, etc.) missing 'path' property -- testSerialization returns undefined -- identity_put causes panic in WASM -- Non-trusted mode not supported for proof verification in WASM -- Various functions require network connectivity - -### 4. Test Infrastructure -- Automatic HTML report generation -- Colored console output -- Error categorization and tracking -- Batch test execution with timing - -## Running the Tests - -### Run All Tests -```bash -node test/run-all-tests.mjs -``` - -### Run Individual Test Suites -```bash -node test/sdk-init-simple.test.mjs -node test/key-generation.test.mjs -node test/identity-queries.test.mjs -# etc... -``` - -### View Results -- Console output with pass/fail summary -- HTML report at `test/test-report.html` - -## Current Status - -### Standalone Tests (No Network Required) -- **111 tests total** -- **102 passed** (91.9% pass rate) -- **9 failed** (known issues documented) - -### Network-Dependent Tests -- **64 tests total** -- Expected to fail when offline -- Will pass when connected to Dash Platform testnet - -## Future Improvements - -1. **Mock Network Responses** - Add mock responses for offline testing of queries -2. **Integration Tests** - Test complete workflows (create identity β†’ fund β†’ create documents) -3. **Performance Tests** - Benchmark key generation and crypto operations -4. **Browser Tests** - Run tests in actual browser environment -5. **CI/CD Integration** - Automate test execution in CI pipeline - -## Conclusion - -The comprehensive test suite successfully covers all exported functions in the WASM SDK. Tests are properly organized, use correct testnet values, and provide clear documentation of expected failures. The test infrastructure supports both individual and batch execution with detailed reporting. \ No newline at end of file diff --git a/packages/wasm-sdk/test/DIP_DERIVATION_SUMMARY.md b/packages/wasm-sdk/test/DIP_DERIVATION_SUMMARY.md deleted file mode 100644 index 9819b6e904c..00000000000 --- a/packages/wasm-sdk/test/DIP_DERIVATION_SUMMARY.md +++ /dev/null @@ -1,73 +0,0 @@ -# DIP Derivation Tests - Summary - -## Overview - -Comprehensive test coverage has been added for all Dash Improvement Proposals (DIPs) related to key derivation: - -- **DIP9**: Feature-based derivation paths -- **DIP13**: HD derivation for Dash Identities -- **DIP14**: Extended key derivation (256-bit paths) -- **DIP15**: DashPay HD derivation paths - -## Test Results - -**19 tests created, 19 passing (100% pass rate)** - -## Test Categories - -### DIP9 - Feature Derivation Paths (4 tests) -- βœ… Basic structure validation for mainnet/testnet -- βœ… Different feature values (0, 1, 2, 3, 5, 10, 15) -- βœ… Actual key derivation with DIP9 paths - -### DIP13 - HD Derivation for Dash Identities (7 tests) -- βœ… Identity root paths (mainnet/testnet) -- βœ… Multiple identity indices -- βœ… Authentication key paths: `m/9'/5'/5'/0'/0'/identity_index'/key_index'` -- βœ… Registration funding paths: `m/9'/5'/5'/1'/identity_index` -- βœ… Top-up funding paths: `m/9'/5'/5'/2'/funding_index` -- βœ… Invitation funding paths: `m/9'/5'/5'/3'/funding_index'` - -### DIP14 - Extended Key Derivation (2 tests) -- βœ… Backwards compatibility with BIP32 -- βœ… Large index support (up to 2^31-1) - -### DIP15 - DashPay HD Derivation (2 tests) -- βœ… Feature 15 path structure -- βœ… Incoming funds base path - -### Cross-DIP Integration (2 tests) -- βœ… DIP9 + DIP13 integration -- βœ… Multiple identity key derivation - -### Edge Cases (2 tests) -- βœ… Hardened vs non-hardened paths -- βœ… Identity recovery determinism - -## Key Findings - -1. **DIP9 Implementation**: Correctly implements feature-based paths with purpose 9' -2. **DIP13 Support**: Full support for all identity key types (auth, funding, top-up, invitation) -3. **Path Components**: BIP44/DIP9 helper functions return components, DIP13 functions return full paths -4. **Hardening**: SDK accepts both hardened and non-hardened paths (produces different keys) -5. **Deterministic**: Same mnemonic + path always produces same keys - -## Path Examples - -```javascript -// DIP9 Feature Path -"m/9'/5'/5'/0/0" // purpose=9', coin=5' (Dash), feature=5' - -// DIP13 Identity Authentication Key -"m/9'/5'/5'/0'/0'/0'/0'" // First auth key of first identity - -// DIP13 Registration Funding -"m/9'/5'/5'/1'/0" // Funding address for first identity - -// DIP15 DashPay Base Path -"m/9'/5'/15'/0'" // Feature 15 for DashPay -``` - -## Integration with Test Suite - -The DIP derivation tests have been integrated into the main test runner and will be executed as part of the comprehensive test suite. This ensures that all DIP standards are properly tested alongside other SDK functionality. \ No newline at end of file diff --git a/packages/wasm-sdk/test/EXPECTED_FAILURES.md b/packages/wasm-sdk/test/EXPECTED_FAILURES.md deleted file mode 100644 index 9b8e5e78ae2..00000000000 --- a/packages/wasm-sdk/test/EXPECTED_FAILURES.md +++ /dev/null @@ -1,132 +0,0 @@ -# Expected Test Failures - -This document categorizes and explains expected test failures in the WASM SDK test suite. - -## Categories of Expected Failures - -### 1. Network-Required Tests ❌ - -These tests require an active network connection to Dash Platform nodes: - -#### Query Functions -- All identity queries (`identity_fetch`, `get_identity_balance`, etc.) -- All document queries (`get_documents`, `get_document`) -- All data contract queries (`data_contract_fetch`, `get_data_contract_history`) -- All token queries (`get_token_statuses`, `get_token_total_supply`, etc.) -- All epoch queries (`get_epochs_info`, `get_current_epoch`, etc.) -- All voting/contested resource queries -- All group queries - -**Reason**: These functions need to communicate with Dash Platform nodes via gRPC. - -#### State Transitions -- All state transition functions (`tokenMint`, `documentCreate`, `identityUpdate`, etc.) - -**Reason**: State transitions require: -1. Valid identity with sufficient credits -2. Network connection to submit transitions -3. Proper authentication (private keys) - -### 2. Implementation Bugs πŸ› - -These are actual bugs that should be fixed: - -#### testSerialization Returns Undefined -- **Test**: `testSerialization method availability` -- **Issue**: Method exists but returns `undefined` instead of test data -- **Expected**: Should return serialized test objects - -#### Path Helper Functions -- **Tests**: `derivation_path_bip44_mainnet/testnet`, `derivation_path_dip9_mainnet/testnet` -- **Issue**: Return structure missing 'path' property -- **Expected**: Should return object with `{ path: "m/44'/5'/0'/0/0", ... }` - -#### DPNS Homograph Conversion -- **Tests**: `dpns_convert_to_homograph_safe - special characters`, `dpns_convert_to_homograph_safe - unicode homographs` -- **Issue**: Doesn't remove special characters or handle unicode homographs -- **Expected**: Should convert/remove problematic characters - -### 3. Test Environment Issues ⚠️ - -These fail due to test environment limitations: - -#### Address Validation -- **Test**: `Can validate addresses` -- **Issue**: Test uses invalid example addresses -- **Fix**: Use actual valid Dash addresses for testing - -#### Functions Causing Panics -- **`identity_put`**: Invalid secret key error -- **`epoch_testing`**: Connection pool configuration error -- **`start` (called twice)**: Trace dispatcher already set - -**Note**: These are internal test functions and may not be intended for public use. - -### 4. Not Yet Implemented 🚧 - -These functions are stubs or not fully implemented: - -#### Extended Key Functions -- `derive_child_public_key` -- `xprv_to_xpub` - -**Status**: Return "not yet implemented" error (expected behavior) - -## Test Suite Status - -| Category | Total | Pass | Fail | Notes | -|----------|-------|------|------|-------| -| SDK Init | 10 | 9 | 1 | Address validation needs fix | -| Key Gen | 53 | 49 | 4 | Path helpers return wrong structure | -| DPNS | 34 | 31 | 3 | Homograph handling incomplete | -| Utilities | 14 | 13 | 1 | testSerialization bug | -| **Total** | **111** | **102** | **9** | **91.9% pass rate** | - -## Recommendations - -### High Priority Fixes -1. Fix path helper functions to return correct structure -2. Fix testSerialization to return proper test data -3. Update address validation test with valid addresses - -### Medium Priority -1. Implement DPNS homograph protection -2. Handle panics in test functions gracefully -3. Document which functions are internal/test-only - -### Low Priority -1. Implement child key derivation functions -2. Add comprehensive network connectivity tests - -## Running Tests - -### Run All Tests -```bash -node test/run-all-tests.mjs -``` - -### Run Individual Test Suites -```bash -node test/sdk-init-simple.test.mjs -node test/key-generation.test.mjs -node test/dpns.test.mjs -node test/utilities-simple.test.mjs -``` - -### View Test Report -After running all tests, open `test/test-report.html` in a browser for detailed results. - -## Network Testing - -To test network-dependent functions: -1. Ensure you have internet connectivity -2. Dash Platform testnet nodes must be accessible -3. For state transitions, you need funded test identities - -## Contributing - -When adding new tests: -1. Categorize as network-dependent or standalone -2. Document expected failures in this file -3. Use try/catch to handle expected errors gracefully -4. Update the test runner if adding new test files \ No newline at end of file diff --git a/packages/wasm-sdk/test/README.md b/packages/wasm-sdk/test/README.md deleted file mode 100644 index 1e879e8c4a1..00000000000 --- a/packages/wasm-sdk/test/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# WASM SDK Test Suite - -Comprehensive unit tests for the Dash Platform WASM SDK. - -## Test Structure - -``` -test/ -β”œβ”€β”€ README.md # This file -β”œβ”€β”€ test-plan.md # Comprehensive test plan -β”œβ”€β”€ test-summary.md # Current test results summary -β”œβ”€β”€ EXPECTED_FAILURES.md # Documentation of expected failures -β”œβ”€β”€ run-all-tests.mjs # Main test runner -β”œβ”€β”€ test-report.html # Generated HTML report -β”‚ -β”œβ”€β”€ sdk-init-simple.test.mjs # SDK initialization tests -β”œβ”€β”€ key-generation.test.mjs # Key generation and derivation tests -β”œβ”€β”€ dpns.test.mjs # DPNS functionality tests -β”œβ”€β”€ utilities-simple.test.mjs # Utility functions tests -β”œβ”€β”€ sample-query.test.mjs # Sample query tests (network-dependent) -β”‚ -└── run-tests.mjs # Key derivation test runner (legacy) -``` - -## Quick Start - -### Run All Tests -```bash -node test/run-all-tests.mjs -``` - -### Run Individual Test Suites -```bash -# SDK initialization -node test/sdk-init-simple.test.mjs - -# Key generation (BIP39/BIP32/BIP44) -node test/key-generation.test.mjs - -# DPNS functions -node test/dpns.test.mjs - -# Utility functions -node test/utilities-simple.test.mjs - -# Sample network queries -node test/sample-query.test.mjs -``` - -## Test Categories - -### βœ… Standalone Tests (No Network Required) -- **SDK Initialization**: Builder patterns, version checking -- **Key Generation**: Mnemonic generation, key derivation, address generation -- **DPNS Validation**: Username validation, homograph safety -- **Utility Functions**: Error handling, type validation - -### 🌐 Network-Dependent Tests -- **Query Functions**: Identity, document, contract, token queries -- **State Transitions**: Token operations, document operations -- **System Queries**: Platform status, epoch information - -## Current Status - -| Test Suite | Total | Pass | Fail | Notes | -|------------|-------|------|------|-------| -| SDK Init | 10 | 9 | 1 | Address validation needs fix | -| Key Gen | 53 | 49 | 4 | Path helpers need fixes | -| DPNS | 34 | 31 | 3 | Homograph handling incomplete | -| Utilities | 14 | 13 | 1 | testSerialization bug | -| **Total** | **111** | **102** | **9** | **91.9% pass rate** | - -## Test Reports - -After running all tests, view the detailed report: -1. Open `test/test-report.html` in a browser -2. Click on test suites to expand details -3. Failed tests are highlighted in red - -## Writing New Tests - -### Test Template -```javascript -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Set up WASM environment -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - configurable: true - }); -} - -// Import and initialize WASM -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Your tests here... -``` - -### Best Practices -1. Use descriptive test names -2. Handle expected errors gracefully -3. Document network dependencies -4. Clean up resources (call `.free()` on SDK instances) -5. Update `EXPECTED_FAILURES.md` for known issues - -## Troubleshooting - -### Common Issues - -1. **"using deprecated parameters" warning** - - This is a known issue and can be ignored - - Use `2>&1 | grep -v "using deprecated parameters"` to filter - -2. **Panics in test functions** - - Some internal test functions cause panics - - These are documented in `EXPECTED_FAILURES.md` - -3. **Network timeouts** - - Ensure internet connectivity - - Check if Dash Platform testnet is operational - - Some queries may timeout without valid data - -4. **Module import errors** - - Ensure Node.js v16+ with ES modules support - - Run from the correct directory - -## Contributing - -When adding tests: -1. Follow the existing test structure -2. Add new test files to `run-all-tests.mjs` -3. Document expected failures -4. Update this README - -## Future Work - -- [ ] Complete query function tests -- [ ] Add state transition tests -- [ ] Implement proof verification tests -- [ ] Add performance benchmarks -- [ ] Create integration test suite - -## Resources - -- [WASM SDK Documentation](../AI_REFERENCE.md) -- [Dash Platform Documentation](https://docs.dash.org/projects/platform/) -- [Test Plan](test-plan.md) -- [Expected Failures](EXPECTED_FAILURES.md) \ No newline at end of file diff --git a/packages/wasm-sdk/test/README_TOKEN_TESTS.md b/packages/wasm-sdk/test/README_TOKEN_TESTS.md deleted file mode 100644 index b53572bc3cc..00000000000 --- a/packages/wasm-sdk/test/README_TOKEN_TESTS.md +++ /dev/null @@ -1,64 +0,0 @@ -# Token Transition Tests - -## Overview -This directory contains tests for the token state transitions in the WASM SDK. - -## New Token Transitions (Implemented) -The following token transitions have been implemented and added to the SDK: - -1. **tokenTransfer** - Transfer tokens between identities -2. **tokenFreeze** - Freeze tokens for a specific identity -3. **tokenUnfreeze** - Unfreeze tokens for a specific identity -4. **tokenDestroyFrozen** - Destroy frozen tokens - -## Test Files - -### token-transitions.test.mjs -New test file that tests the four newly implemented token transitions: -- Tests parameter validation -- Tests error handling for invalid inputs -- Tests permission requirements -- Verifies all methods are available on the SDK instance - -### state-transitions.test.mjs (Needs Update) -The existing state transitions test file contains an outdated test for `token_transfer` (line 307-325) that uses the old function signature: -```javascript -// OLD (no longer exists) -await wasmSdk.token_transfer(sdk, mnemonic, identity, contract, recipient, amount, keyIndex) - -// NEW (implemented) -await sdk.tokenTransfer(contractId, position, amount, senderId, recipientId, privateKey, publicNote) -``` - -This test should be updated or removed since the old function no longer exists. - -## Running Tests - -To run the token transition tests: - -1. First build the WASM SDK: - ```bash - ./build.sh - ``` - -2. Then run the tests: - ```bash - node test/token-transitions.test.mjs - ``` - -## Expected Results - -Most tests will fail with permission/identity errors, which is expected behavior since we're testing without real funded identities. The important validations are: - -1. All methods are available on the SDK instance -2. Parameter validation works correctly -3. Invalid inputs are rejected with appropriate errors -4. The methods attempt to connect to the network (even if they fail due to permissions) - -## Integration with UI - -The token transitions are also exposed in the HTML UI (index.html) and defined in api-definitions.json, allowing users to: -- Execute token transfers through the web interface -- Freeze and unfreeze tokens -- Destroy frozen tokens -- All with optional public notes for transparency \ No newline at end of file diff --git a/packages/wasm-sdk/test/check-usernames.mjs b/packages/wasm-sdk/test/check-usernames.mjs deleted file mode 100644 index 2b3908f1b73..00000000000 --- a/packages/wasm-sdk/test/check-usernames.mjs +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env node - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Prefetch quorums -await wasmSdk.prefetch_trusted_quorums_testnet(); - -// Use trusted builder -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -try { - const usernames = await wasmSdk.get_dpns_usernames( - sdk, - '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', - 10 - ); - console.log('Usernames for identity:', usernames); - - // Also try to resolve a specific username - if (usernames && usernames.length > 0) { - const username = usernames[0]; - // For contested resources, we'd use the parent domain and label - console.log(`\nFor contested resources, use: -- Parent domain: ${username.parentDomainName || 'dash'} -- Label: ${username.label}`); - } -} catch (e) { - console.log('Error:', e.message); -} - -sdk.free(); \ No newline at end of file diff --git a/packages/wasm-sdk/test/dashpay-contact-keys.test.mjs b/packages/wasm-sdk/test/dashpay-contact-keys.test.mjs deleted file mode 100755 index 1d5275015aa..00000000000 --- a/packages/wasm-sdk/test/dashpay-contact-keys.test.mjs +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env node - -import { Worker } from 'worker_threads'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { readFileSync } from 'fs'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Load WASM module in main thread first -const wasmBuffer = readFileSync(join(__dirname, '../pkg/wasm_sdk_bg.wasm')); - -// Test runner -async function runTest() { - console.log('Testing DashPay Contact Keys (DIP15) Implementation...\n'); - - const worker = new Worker(join(__dirname, '../shared-sdk-worker.js'), { - workerData: { wasmBuffer } - }); - - let wasmSdk = null; - let testsPassed = 0; - let testsFailed = 0; - - // Helper to call SDK methods - const callSdk = (method, ...args) => { - return new Promise((resolve, reject) => { - const id = Date.now() + Math.random(); - worker.postMessage({ id, method, args }); - - const handler = (msg) => { - if (msg.id === id) { - worker.off('message', handler); - if (msg.error) { - reject(new Error(msg.error)); - } else { - resolve(msg.result); - } - } - }; - - worker.on('message', handler); - }); - }; - - // Test helper - const test = async (name, fn) => { - try { - await fn(); - console.log(`βœ“ ${name}`); - testsPassed++; - } catch (error) { - console.error(`βœ— ${name}:`, error.message); - testsFailed++; - } - }; - - // Wait for worker to be ready - await new Promise(resolve => { - worker.once('message', (msg) => { - if (msg.ready) resolve(); - }); - }); - - // Test constants - const testMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - const testPassphrase = ""; - - // Test identity IDs (from docs.html) - const senderIdentity = "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk"; - const receiverIdentity = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - - // DIP15 Tests - console.log('=== DIP15 DashPay Contact Keys Tests ===\n'); - - await test('DIP15 base path for mainnet', async () => { - const path = "m/9'/5'/15'/0'"; - const result = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, path, 'mainnet'); - if (!result) throw new Error('No result'); - if (!result.private_key) throw new Error('No private key'); - if (!result.public_key) throw new Error('No public key'); - if (!result.address) throw new Error('No address'); - console.log(` Base path address: ${result.address}`); - }); - - await test('DIP15 base path for testnet', async () => { - const path = "m/9'/1'/15'/0'"; - const result = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, path, 'testnet'); - if (!result) throw new Error('No result'); - if (!result.private_key) throw new Error('No private key'); - if (!result.public_key) throw new Error('No public key'); - if (!result.address) throw new Error('No address'); - console.log(` Base path address: ${result.address}`); - }); - - await test('DIP15 multiple accounts', async () => { - for (let account = 0; account < 3; account++) { - const path = `m/9'/5'/15'/${account}'`; - const result = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, path, 'mainnet'); - if (!result) throw new Error(`No result for account ${account}`); - console.log(` Account ${account} address: ${result.address}`); - } - }); - - await test('DIP15 conceptual path structure', async () => { - // Test the base path that would be extended with identity IDs - const basePath = "m/9'/5'/15'/0'"; - const result = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, basePath, 'mainnet'); - - // Log what the full path would look like with identity IDs - const conceptualPath = `${basePath}/${senderIdentity}/${receiverIdentity}/0`; - console.log(` Base path: ${basePath}`); - console.log(` Conceptual full path: ${conceptualPath}`); - console.log(` Note: Full 256-bit identity ID paths require DIP14 implementation`); - - if (!result || !result.address) throw new Error('Failed to derive base path'); - }); - - await test('DIP15 vs DIP13 key isolation', async () => { - // DIP15 key - const dip15Path = "m/9'/5'/15'/0'"; - const dip15Key = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, dip15Path, 'mainnet'); - - // DIP13 identity key - const dip13Path = "m/9'/5'/5'/0'/0'/0'/0'"; - const dip13Key = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, dip13Path, 'mainnet'); - - if (!dip15Key || !dip13Key) throw new Error('Failed to derive keys'); - if (dip15Key.address === dip13Key.address) throw new Error('DIP15 and DIP13 keys should be different'); - - console.log(` DIP15 address: ${dip15Key.address}`); - console.log(` DIP13 address: ${dip13Key.address}`); - }); - - await test('DIP15 deterministic derivation', async () => { - // Same path should always produce same key - const path = "m/9'/1'/15'/0'"; - const result1 = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, path, 'testnet'); - const result2 = await callSdk('derive_key_from_seed_with_path', testMnemonic, testPassphrase, path, 'testnet'); - - if (!result1 || !result2) throw new Error('Failed to derive keys'); - if (result1.address !== result2.address) throw new Error('Same path should produce same address'); - if (result1.private_key !== result2.private_key) throw new Error('Same path should produce same private key'); - }); - - // Summary - console.log('\n=== DIP15 Test Summary ==='); - console.log(`Passed: ${testsPassed}`); - console.log(`Failed: ${testsFailed}`); - console.log(`Total: ${testsPassed + testsFailed}`); - - console.log('\n=== DIP15 Implementation Notes ==='); - console.log('1. Current implementation derives base DIP15 paths (m/9\'/coin\'/15\'/account\')'); - console.log('2. Full DIP15 paths include 256-bit identity IDs as per DIP14'); - console.log('3. The WASM SDK can derive the base path, but extending with 256-bit IDs requires additional work'); - console.log('4. UI shows conceptual full path for educational purposes'); - console.log('5. Each user pair gets unique deterministic addresses for privacy'); - - // Cleanup - await worker.terminate(); - process.exit(testsFailed > 0 ? 1 : 0); -} - -runTest().catch(console.error); \ No newline at end of file diff --git a/packages/wasm-sdk/test/dip-derivation.test.mjs b/packages/wasm-sdk/test/dip-derivation.test.mjs deleted file mode 100644 index c60cd2f6f4c..00000000000 --- a/packages/wasm-sdk/test/dip-derivation.test.mjs +++ /dev/null @@ -1,339 +0,0 @@ -#!/usr/bin/env node -// dip-derivation.test.mjs - Comprehensive tests for DIP9, DIP11, DIP13, DIP14, DIP15 key derivation - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -// Test constants -const TEST_MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - -console.log('\nDIP-based Key Derivation Tests\n'); - -// DIP9 Tests - Feature Derivation Paths -describe('DIP9 - Feature Derivation Paths'); - -await test('DIP9 basic structure - mainnet', () => { - const result = wasmSdk.derivation_path_dip9_mainnet(5, 0, 0); - if (!result) throw new Error('Missing result'); - if (result.purpose !== 9) throw new Error('DIP9 purpose should be 9'); - if (result.coin_type !== 5) throw new Error('Dash mainnet coin type should be 5'); - if (result.account !== 5) throw new Error('Feature should be 5 for this test'); - - // Build expected path: m / purpose' / coin_type' / feature' / change / index - const expectedPath = `m/${result.purpose}'/${result.coin_type}'/${result.account}'/${result.change}/${result.index}`; - if (expectedPath !== "m/9'/5'/5'/0/0") throw new Error(`Unexpected path: ${expectedPath}`); -}); - -await test('DIP9 basic structure - testnet', () => { - const result = wasmSdk.derivation_path_dip9_testnet(5, 0, 0); - if (!result) throw new Error('Missing result'); - if (result.purpose !== 9) throw new Error('DIP9 purpose should be 9'); - if (result.coin_type !== 1) throw new Error('Testnet coin type should be 1'); - if (result.account !== 5) throw new Error('Feature should be 5 for this test'); -}); - -await test('DIP9 with different features', () => { - // Test different feature values - const features = [0, 1, 2, 3, 5, 10, 15]; - - for (const feature of features) { - const result = wasmSdk.derivation_path_dip9_mainnet(feature, 0, 0); - if (result.account !== feature) { - throw new Error(`Feature ${feature} not properly set in path`); - } - if (result.purpose !== 9) { - throw new Error(`Purpose should always be 9 for DIP9, got ${result.purpose}`); - } - } -}); - -await test('DIP9 key derivation - mainnet', () => { - // Test actual key derivation with DIP9 path - const path = "m/9'/5'/5'/0/0"; - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, path, "mainnet"); - - if (!result.private_key_wif) throw new Error('Missing private_key_wif'); - if (!result.address) throw new Error('Missing address'); - if (!result.address.startsWith('X')) throw new Error('Mainnet address should start with X'); - if (result.path !== path) throw new Error('Path mismatch'); -}); - -// DIP13 Tests - HD Derivation Path for Dash Identities -describe('DIP13 - HD Derivation for Dash Identities'); - -await test('DIP13 identity root path - mainnet', () => { - const result = wasmSdk.derivation_path_dip13_mainnet(0); - if (!result || !result.path) throw new Error('Missing path'); - if (result.path !== "m/9'/5'/0'") throw new Error(`Expected m/9'/5'/0', got ${result.path}`); - if (result.purpose !== 9) throw new Error('DIP13 uses DIP9 purpose (9)'); - if (result.coin_type !== 5) throw new Error('Dash mainnet coin type should be 5'); - if (result.description !== "DIP13 HD identity key path") throw new Error('Wrong description'); -}); - -await test('DIP13 identity root path - testnet', () => { - const result = wasmSdk.derivation_path_dip13_testnet(0); - if (!result || !result.path) throw new Error('Missing path'); - if (result.path !== "m/9'/1'/0'") throw new Error(`Expected m/9'/1'/0', got ${result.path}`); - if (result.coin_type !== 1) throw new Error('Testnet coin type should be 1'); -}); - -await test('DIP13 multiple identity indices', () => { - // Test different identity indices - for (let i = 0; i < 5; i++) { - const result = wasmSdk.derivation_path_dip13_mainnet(i); - const expectedPath = `m/9'/5'/${i}'`; - if (result.path !== expectedPath) { - throw new Error(`Identity ${i}: expected ${expectedPath}, got ${result.path}`); - } - } -}); - -await test('DIP13 authentication key path', () => { - // DIP13 specifies: m/9'/5'/5'/0'/0'/identity_index'/key_index' - // First, get identity root - const identityIndex = 0; - const keyIndex = 0; - - // Build full authentication key path - const authPath = `m/9'/5'/5'/0'/0'/${identityIndex}'/${keyIndex}'`; - - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, authPath, "mainnet"); - if (!result.private_key_wif) throw new Error('Missing private key'); - if (!result.public_key) throw new Error('Missing public key'); - if (result.path !== authPath) throw new Error('Path mismatch'); -}); - -await test('DIP13 registration funding key path', () => { - // DIP13 specifies: m/9'/5'/5'/1'/identity_index - const identityIndex = 0; - const fundingPath = `m/9'/5'/5'/1'/${identityIndex}`; - - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, fundingPath, "mainnet"); - if (!result.address) throw new Error('Missing address'); - if (!result.address.startsWith('X')) throw new Error('Should be mainnet address'); -}); - -await test('DIP13 top-up funding key path', () => { - // DIP13 specifies: m/9'/5'/5'/2'/funding_index - const fundingIndex = 0; - const topUpPath = `m/9'/5'/5'/2'/${fundingIndex}`; - - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, topUpPath, "mainnet"); - if (!result.address) throw new Error('Missing address'); - if (result.path !== topUpPath) throw new Error('Path mismatch'); -}); - -await test('DIP13 invitation funding key path', () => { - // DIP13 specifies: m/9'/5'/5'/3'/funding_index' - const fundingIndex = 0; - const invitePath = `m/9'/5'/5'/3'/${fundingIndex}'`; - - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, invitePath, "mainnet"); - if (!result.address) throw new Error('Missing address'); -}); - -// DIP14 Tests - Extended Key Derivation (256-bit paths) -describe('DIP14 - Extended Key Derivation'); - -await test('DIP14 backwards compatibility with BIP32', () => { - // DIP14 should be backwards compatible for indices < 2^32-1 - const normalPath = "m/44'/5'/0'/0/0"; - const result1 = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, normalPath, "mainnet"); - - // Same path should produce same results - const result2 = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, normalPath, "mainnet"); - - if (result1.address !== result2.address) { - throw new Error('DIP14 backwards compatibility failed'); - } -}); - -await test('DIP14 large index support', () => { - // Test with indices larger than 31 bits but within 32 bits - const largePath = "m/9'/5'/2147483647'/0/0"; // Max 31-bit value - - try { - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, largePath, "mainnet"); - if (!result.address) throw new Error('Large index derivation failed'); - } catch (error) { - // Some implementations might not support this yet - console.log(' Note: Large index derivation not yet supported'); - } -}); - -// DIP15 Tests - DashPay HD Derivation Path -describe('DIP15 - DashPay HD Derivation'); - -await test('DIP15 feature path structure', () => { - // DIP15 uses feature 15' for DashPay incoming funds - const dashPayFeature = 15; - - // Try to derive a path with feature 15 - const result = wasmSdk.derivation_path_dip9_mainnet(dashPayFeature, 0, 0); - if (result.account !== dashPayFeature) { - throw new Error(`DashPay feature (15) not properly set`); - } -}); - -await test('DIP15 incoming funds base path', () => { - // Base path: m/9'/5'/15'/0'/ - const basePath = "m/9'/5'/15'/0'"; - - // Note: Full DIP15 paths require 256-bit user IDs which may not be - // fully supported in current implementation - try { - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, basePath, "mainnet"); - if (!result.private_key_wif) throw new Error('Missing private key'); - } catch (error) { - if (error.message.includes('Invalid derivation path')) { - console.log(' Note: DIP15 base path derivation may require special handling'); - } else { - throw error; - } - } -}); - -// Cross-DIP Integration Tests -describe('Cross-DIP Integration'); - -await test('DIP9 + DIP13 identity derivation', () => { - // DIP13 builds on DIP9's feature system - // Feature 5 is reserved for identity-related keys - const dip9Result = wasmSdk.derivation_path_dip9_mainnet(5, 0, 0); - const dip13Result = wasmSdk.derivation_path_dip13_mainnet(0); - - // Both should use purpose 9 - if (dip9Result.purpose !== 9 || dip13Result.purpose !== 9) { - throw new Error('Both DIP9 and DIP13 should use purpose 9'); - } - - // Both should use same coin type for mainnet - if (dip9Result.coin_type !== 5 || dip13Result.coin_type !== 5) { - throw new Error('Coin type mismatch'); - } -}); - -await test('Multiple identity key derivation', () => { - // Test deriving keys for multiple identities - const identities = []; - - for (let i = 0; i < 3; i++) { - // Authentication key for identity i - const authPath = `m/9'/5'/5'/0'/0'/${i}'/0'`; - const authKey = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, authPath, "mainnet"); - - // Registration funding key for identity i - const fundPath = `m/9'/5'/5'/1'/${i}`; - const fundKey = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, fundPath, "mainnet"); - - identities.push({ - index: i, - authKey: authKey.public_key, - fundingAddress: fundKey.address - }); - } - - // Ensure all identities have unique keys - const authKeys = identities.map(id => id.authKey); - const uniqueAuthKeys = new Set(authKeys); - if (uniqueAuthKeys.size !== authKeys.length) { - throw new Error('Identity auth keys should be unique'); - } - - const fundAddresses = identities.map(id => id.fundingAddress); - const uniqueFundAddresses = new Set(fundAddresses); - if (uniqueFundAddresses.size !== fundAddresses.length) { - throw new Error('Identity funding addresses should be unique'); - } -}); - -// Edge Cases and Error Handling -describe('Edge Cases and Error Handling'); - -await test('Non-hardened vs hardened DIP9 paths', () => { - // Test that SDK accepts both hardened and non-hardened paths - const hardenedPath = "m/9'/5'/5'/0/0"; - const nonHardenedPath = "m/9/5/5/0/0"; - - const hardenedResult = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, hardenedPath, "mainnet"); - const nonHardenedResult = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, nonHardenedPath, "mainnet"); - - // They should produce different keys - if (hardenedResult.address === nonHardenedResult.address) { - throw new Error('Hardened and non-hardened paths should produce different keys'); - } - - // Both should be valid - if (!hardenedResult.address || !nonHardenedResult.address) { - throw new Error('Both paths should produce valid addresses'); - } -}); - -await test('DIP13 identity recovery', () => { - // Test that same mnemonic produces same identity keys - const path = "m/9'/5'/5'/0'/0'/0'/0'"; // First auth key of first identity - - const key1 = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, path, "mainnet"); - const key2 = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, path, "mainnet"); - - if (key1.public_key !== key2.public_key) { - throw new Error('Identity keys should be deterministic'); - } - if (key1.private_key_wif !== key2.private_key_wif) { - throw new Error('Private keys should match for same path'); - } -}); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ DIP Standards Summary:'); -console.log('- DIP9: Feature-based derivation paths (purpose 9\')'); -console.log('- DIP13: Identity key management (feature 5\')'); -console.log('- DIP14: Extended 256-bit derivation (backwards compatible)'); -console.log('- DIP15: DashPay contact paths (feature 15\')'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/document-queries.test.mjs b/packages/wasm-sdk/test/document-queries.test.mjs deleted file mode 100644 index 0f2d6b1fc39..00000000000 --- a/packages/wasm-sdk/test/document-queries.test.mjs +++ /dev/null @@ -1,356 +0,0 @@ -#!/usr/bin/env node -// document-queries.test.mjs - Tests for document query functions using documented testnet values - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nDocument Query Tests Using Documented Testnet Values\n'); - -// DOCUMENTED TEST VALUES FROM docs.html and test-data.js -const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; -const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; -const TOKEN_CONTRACT = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'; -const CONTRACT_WITH_HISTORY = 'HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM'; -const DASHPAY_CONTRACT = 'ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A'; - -console.log('Test Values:'); -console.log(`- Identity: ${TEST_IDENTITY}`); -console.log(`- DPNS Contract: ${DPNS_CONTRACT}`); -console.log(`- Token Contract: ${TOKEN_CONTRACT}`); -console.log(`- Contract with History: ${CONTRACT_WITH_HISTORY}`); -console.log(`- Dashpay Contract: ${DASHPAY_CONTRACT}`); - -// Prefetch trusted quorums for testnet to avoid epoch query issues -console.log('Prefetching trusted quorums...'); -try { - await wasmSdk.prefetch_trusted_quorums_testnet(); -} catch (error) { - console.warn('Failed to prefetch trusted quorums (offline mode?):', error.message); -} - -// Initialize SDK - use trusted builder for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Document Query Tests -describe('Document Queries'); - -await test('get_documents - DPNS domains (no filters)', async () => { - try { - const result = await wasmSdk.get_documents( - sdk, - DPNS_CONTRACT, - "domain", - null, // no where clause - null, // no order by - 10, // limit - null, // no start after - null // no start at - ); - console.log(` Found ${result?.length || 0} documents`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_documents - with where clause', async () => { - try { - // Search for domains under .dash parent domain (more likely to exist) - const whereClause = JSON.stringify([ - ["normalizedParentDomainName", "==", "dash"] - ]); - - const result = await wasmSdk.get_documents( - sdk, - DPNS_CONTRACT, - "domain", - whereClause, - null, // no order by - 10, // limit - null, // no start after - null // no start at - ); - console.log(` Found ${result?.length || 0} domains under .dash`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_documents - with orderBy clause', async () => { - try { - // Use indexed properties for orderBy - normalizedParentDomainName is indexed - const orderBy = JSON.stringify([ - ["normalizedParentDomainName", "asc"] - ]); - - const result = await wasmSdk.get_documents( - sdk, - DPNS_CONTRACT, - "domain", - null, // no where - orderBy, // order by normalizedParentDomainName ascending - 5, // limit - null, // no start after - null // no start at - ); - console.log(` Found ${result?.length || 0} documents ordered by parent domain`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_documents - with complex where clause', async () => { - try { - // Multiple conditions - need orderBy when using ranges like startsWith - const whereClause = JSON.stringify([ - ["normalizedLabel", "startsWith", "test"], - ["normalizedParentDomainName", "==", "dash"] - ]); - - // Required orderBy for range queries - const orderBy = JSON.stringify([ - ["normalizedParentDomainName", "asc"], - ["normalizedLabel", "asc"] - ]); - - const result = await wasmSdk.get_documents( - sdk, - DPNS_CONTRACT, - "domain", - whereClause, - orderBy, - 10, - null, - null - ); - console.log(` Found ${result?.length || 0} test domains under .dash`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_document - by specific ID', async () => { - try { - // This would need a real document ID - const result = await wasmSdk.get_document( - sdk, - DPNS_CONTRACT, - "domain", - "invalidDocumentId" - ); - throw new Error('Should have failed with invalid ID'); - } catch (error) { - if (error.message.includes('Should have failed')) { - throw error; - } - console.log(' Expected error with invalid document ID'); - } -}); - -// Data Contract Query Tests -describe('Data Contract Queries'); - -await test('data_contract_fetch - DPNS contract', async () => { - try { - const result = await wasmSdk.data_contract_fetch(sdk, DPNS_CONTRACT); - console.log(` Contract fetched: ${result?.id || 'N/A'}`); - console.log(` Version: ${result?.version || 'N/A'}`); - console.log(` Owner: ${result?.ownerId || 'N/A'}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('data_contract_fetch - Dashpay contract', async () => { - try { - // Use Dashpay contract which should exist - const result = await wasmSdk.data_contract_fetch(sdk, DASHPAY_CONTRACT); - console.log(` Contract fetched: ${result?.id || 'N/A'}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_data_contract_history - contract with history', async () => { - try { - const result = await wasmSdk.get_data_contract_history( - sdk, - CONTRACT_WITH_HISTORY, - 10, // limit - 0, // offset - null // start at ms - ); - console.log(` Found ${result?.length || 0} historical versions`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_data_contracts - fetch multiple contracts', async () => { - try { - // Note: This function expects Vec in Rust, which should work with JS array - const result = await wasmSdk.get_data_contracts( - sdk, - [DPNS_CONTRACT, DASHPAY_CONTRACT] - ); - console.log(` Found ${result?.length || 0} data contracts`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Token Document Queries -describe('Token Document Queries'); - -await test('get_documents - token documents', async () => { - try { - const result = await wasmSdk.get_documents( - sdk, - TOKEN_CONTRACT, - "token", // assuming token document type - null, - null, - 10, - null, - null - ); - console.log(` Found ${result?.length || 0} token documents`); - } catch (error) { - // Token queries might fail if contract doesn't have 'token' document type - console.log(' Expected error (token contract may not have token document type)'); - } -}); - -// System Status Queries -describe('System Status Queries'); - -await test('get_status - platform status', async () => { - try { - const result = await wasmSdk.get_status(sdk); - console.log(` Status received: ${JSON.stringify(result)}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Epoch Queries -describe('Epoch Queries'); - -await test('get_current_epoch', async () => { - try { - const result = await wasmSdk.get_current_epoch(sdk); - console.log(` Current epoch: ${result}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_epochs_info', async () => { - try { - const result = await wasmSdk.get_epochs_info(sdk, 1, 1); // Get info for epoch 1, count 1 - console.log(` Epoch info fetched`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- These tests use the documented testnet values from docs.html'); -console.log('- Network errors are expected when running offline'); -console.log('- Some queries may fail due to missing data or incorrect document types'); -console.log('- Where and orderBy clauses are properly formatted as JSON strings'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/dpns.test.mjs b/packages/wasm-sdk/test/dpns.test.mjs deleted file mode 100644 index 1fdb01de6f0..00000000000 --- a/packages/wasm-sdk/test/dpns.test.mjs +++ /dev/null @@ -1,360 +0,0 @@ -#!/usr/bin/env node -// dpns.test.mjs - Tests for DPNS (Dash Platform Name Service) functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nDPNS Tests\n'); - -// Homograph Safety Tests -describe('Homograph Safety'); - -await test('dpns_convert_to_homograph_safe - basic ASCII', () => { - const result = wasmSdk.dpns_convert_to_homograph_safe("test"); - if (result !== "test") { - throw new Error(`Expected "test", got "${result}"`); - } -}); - -await test('dpns_convert_to_homograph_safe - with numbers', () => { - const result = wasmSdk.dpns_convert_to_homograph_safe("test123"); - if (result !== "test123") { - throw new Error(`Expected "test123", got "${result}"`); - } -}); - -await test('dpns_convert_to_homograph_safe - with hyphens', () => { - const result = wasmSdk.dpns_convert_to_homograph_safe("test-name"); - if (result !== "test-name") { - throw new Error(`Expected "test-name", got "${result}"`); - } -}); - -await test('dpns_convert_to_homograph_safe - uppercase to lowercase', () => { - const result = wasmSdk.dpns_convert_to_homograph_safe("TestName"); - if (result !== "testname") { - throw new Error(`Expected "testname", got "${result}"`); - } -}); - -await test('dpns_convert_to_homograph_safe - special characters', () => { - // Only homograph characters (o,i,l) are converted, other special chars are lowercased but preserved - const result = wasmSdk.dpns_convert_to_homograph_safe("test@name!"); - if (result !== "test@name!") { - throw new Error(`Expected "test@name!", got "${result}"`); - } -}); - -await test('dpns_convert_to_homograph_safe - ASCII homograph conversions (o,i,l)', () => { - const input = "IlIooLi"; // mix of I,l,i,o - const result = wasmSdk.dpns_convert_to_homograph_safe(input); - // Expect: I->i->1, l->1, I->i->1, o->0, o->0, L->l->1, i->1 = "1110011" - if (result !== "1110011") { - throw new Error(`Expected "1110011" for "${input}", got "${result}"`); - } -}); - -await test('dpns_convert_to_homograph_safe - unicode homographs', () => { - // Only o,i,l are converted to 0,1,1 - other Unicode characters are preserved - const result = wasmSdk.dpns_convert_to_homograph_safe("tΠ΅st"); // Π΅ is Cyrillic - // Cyrillic 'Π΅' should remain as-is, only lowercased - if (result !== "tΠ΅st") { // Should be the same (just lowercased) - throw new Error(`Expected Cyrillic to be preserved (lowercased), got "${result}"`); - } -}); - -// Username Validation Tests -describe('Username Validation'); - -await test('dpns_is_valid_username - valid basic username', () => { - if (!wasmSdk.dpns_is_valid_username("alice")) { - throw new Error('Basic username "alice" should be valid'); - } -}); - -await test('dpns_is_valid_username - valid with numbers', () => { - if (!wasmSdk.dpns_is_valid_username("alice123")) { - throw new Error('Username with numbers should be valid'); - } -}); - -await test('dpns_is_valid_username - valid with hyphen', () => { - if (!wasmSdk.dpns_is_valid_username("alice-bob")) { - throw new Error('Username with hyphen should be valid'); - } -}); - -await test('dpns_is_valid_username - too short', () => { - if (wasmSdk.dpns_is_valid_username("ab")) { - throw new Error('Username shorter than 3 characters should be invalid'); - } -}); - -await test('dpns_is_valid_username - too long', () => { - const longName = "a".repeat(64); - if (wasmSdk.dpns_is_valid_username(longName)) { - throw new Error('Username longer than 63 characters should be invalid'); - } -}); - -await test('dpns_is_valid_username - starts with hyphen', () => { - if (wasmSdk.dpns_is_valid_username("-alice")) { - throw new Error('Username starting with hyphen should be invalid'); - } -}); - -await test('dpns_is_valid_username - ends with hyphen', () => { - if (wasmSdk.dpns_is_valid_username("alice-")) { - throw new Error('Username ending with hyphen should be invalid'); - } -}); - -await test('dpns_is_valid_username - double hyphen', () => { - if (wasmSdk.dpns_is_valid_username("alice--bob")) { - throw new Error('Username with double hyphen should be invalid'); - } -}); - - -await test('dpns_is_valid_username - special characters', () => { - if (wasmSdk.dpns_is_valid_username("alice@bob")) { - throw new Error('Username with special characters should be invalid'); - } -}); - -await test('dpns_is_valid_username - spaces', () => { - if (wasmSdk.dpns_is_valid_username("alice bob")) { - throw new Error('Username with spaces should be invalid'); - } -}); - -// Contested Username Tests -describe('Contested Username Detection'); - -await test('dpns_is_contested_username - non-contested name', () => { - if (wasmSdk.dpns_is_contested_username("uniquename123")) { - throw new Error('Unique username should not be contested'); - } -}); - -await test('dpns_is_contested_username - common name', () => { - // Common names like "alice", "bob", "test" might be contested - const result = wasmSdk.dpns_is_contested_username("alice"); - // This depends on implementation - just check it returns a boolean - if (typeof result !== 'boolean') { - throw new Error('Should return boolean'); - } -}); - -await test('dpns_is_contested_username - single letter', () => { - const result = wasmSdk.dpns_is_contested_username("a"); - if (typeof result !== 'boolean') { - throw new Error('Should return boolean'); - } -}); - -await test('dpns_is_contested_username - three letter', () => { - const result = wasmSdk.dpns_is_contested_username("abc"); - if (typeof result !== 'boolean') { - throw new Error('Should return boolean'); - } -}); - -// Network-dependent DPNS tests (expected to fail without network) -describe('DPNS Network Operations (Expected to Fail)'); - -// Initialize SDK for network tests -let sdk = null; -try { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - sdk = await builder.build(); -} catch (error) { - console.log(' Failed to create SDK for network tests'); -} - -if (sdk) { - await test('get_dpns_usernames - get usernames for identity', async () => { - try { - const result = await wasmSdk.get_dpns_usernames( - sdk, - '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', - 10 // limit parameter - ); - console.log(` Found ${result?.length || 0} usernames for identity`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection') || - error.message.includes('Non-trusted mode is not supported in WASM')) { - console.log(' Expected error (network or non-trusted mode)'); - } else { - throw error; - } - } - }); - - await test('dpns_register_name - requires identity and network', async () => { - try { - // This will fail without a valid identity and network connection - const result = await wasmSdk.dpns_register_name( - sdk, - "testname", - "invalididentityid", - 0, - "invalidprivatekey" - ); - throw new Error('Should have failed without valid identity'); - } catch (error) { - // Expected to fail - if (error.message.includes('Should have failed')) { - throw error; - } - } - }); - - await test('dpns_is_name_available - requires network', async () => { - try { - const result = await wasmSdk.dpns_is_name_available(sdk, "testname"); - // If this succeeds, it means network is available - if (typeof result !== 'boolean') { - throw new Error('Should return boolean'); - } - } catch (error) { - // Expected to fail without network - // This is acceptable - } - }); - - await test('dpns_resolve_name - requires network', async () => { - try { - const result = await wasmSdk.dpns_resolve_name(sdk, "alice.dash"); - // If this succeeds, it means network is available - if (result && typeof result !== 'object') { - throw new Error('Should return object or null'); - } - } catch (error) { - // Expected to fail without network - // This is acceptable - } - }); - - await test('get_dpns_username_by_name - requires network', async () => { - try { - const result = await wasmSdk.get_dpns_username_by_name(sdk, "alice"); - // If this succeeds, it means network is available - if (result && typeof result !== 'object') { - throw new Error('Should return object or null'); - } - } catch (error) { - // Expected to fail without network - // This is acceptable - } - }); - - await test('get_dpns_usernames - requires network and identity', async () => { - try { - const result = await wasmSdk.get_dpns_usernames(sdk, "invalididentityid", 10); - // If this succeeds, it means network is available - if (!Array.isArray(result)) { - throw new Error('Should return array'); - } - } catch (error) { - // Expected to fail without valid identity - // This is acceptable - } - }); - - await test('get_dpns_username - requires network and identity', async () => { - try { - const result = await wasmSdk.get_dpns_username(sdk, "invalididentityid"); - // If this succeeds, it means network is available - if (result && typeof result !== 'object') { - throw new Error('Should return object or null'); - } - } catch (error) { - // Expected to fail without valid identity - // This is acceptable - } - }); -} - -// Edge Case Tests -describe('DPNS Edge Cases'); - -await test('dpns_convert_to_homograph_safe - empty string', () => { - const result = wasmSdk.dpns_convert_to_homograph_safe(""); - if (result !== "") { - throw new Error(`Expected empty string, got "${result}"`); - } -}); - -await test('dpns_is_valid_username - empty string', () => { - if (wasmSdk.dpns_is_valid_username("")) { - throw new Error('Empty string should not be valid username'); - } -}); - -await test('dpns_is_contested_username - empty string', () => { - const result = wasmSdk.dpns_is_contested_username(""); - if (typeof result !== 'boolean') { - throw new Error('Should return boolean even for empty string'); - } -}); - -await test('dpns_convert_to_homograph_safe - only special characters', () => { - const result = wasmSdk.dpns_convert_to_homograph_safe("@#$%"); - // Special characters are preserved, only homograph chars (o,i,l) are converted - if (result !== "@#$%") { - throw new Error(`Expected special characters to be preserved, got "${result}"`); - } -}); - -// Clean up -if (sdk) { - sdk.free(); -} - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/epoch-block-queries.test.mjs b/packages/wasm-sdk/test/epoch-block-queries.test.mjs deleted file mode 100644 index 9e6cef8ea7d..00000000000 --- a/packages/wasm-sdk/test/epoch-block-queries.test.mjs +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env node -// epoch-block-queries.test.mjs - Tests for epoch and block query functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nEpoch & Block Query Tests\n'); - -// Test values from docs.html -const TEST_EVONODE_ID = '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113'; - -// Initialize SDK - prefetch quorums for trusted mode -console.log('Prefetching trusted quorums...'); -try { - await wasmSdk.prefetch_trusted_quorums_testnet(); - console.log('Quorums prefetched successfully'); -} catch (error) { - console.log('Warning: Could not prefetch quorums:', error.message); -} - -// Use trusted builder as required for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Epoch Info Queries -describe('Epoch Information Queries'); - -await test('get_epochs_info - fetch epoch information', async () => { - try { - const result = await wasmSdk.get_epochs_info( - sdk, - 1000, // start epoch - 100, // count - true // ascending - ); - console.log(` Found ${result?.length || 0} epochs`); - if (result && result.length > 0) { - const firstEpoch = result[0]; - console.log(` First epoch: ${firstEpoch.epochIndex || 'N/A'}`); - console.log(` Start time: ${firstEpoch.startTime || 'N/A'}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_finalized_epoch_infos - fetch finalized epoch infos', async () => { - try { - const result = await wasmSdk.get_finalized_epoch_infos( - sdk, - 8635, // start epoch - 100 // count - ); - console.log(` Found ${result?.length || 0} finalized epochs`); - if (result && result.length > 0) { - const firstEpoch = result[0]; - console.log(` First epoch: ${firstEpoch.epochIndex || 'N/A'}`); - console.log(` Finalized: ${firstEpoch.isFinalized || false}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Evonode Block Queries -describe('Evonode Block Queries'); - -await test('get_evonodes_proposed_epoch_blocks_by_ids - fetch blocks by IDs', async () => { - try { - const result = await wasmSdk.get_evonodes_proposed_epoch_blocks_by_ids( - sdk, - 8635, // epoch number - [TEST_EVONODE_ID] // evonode IDs - ); - console.log(` Found ${result?.length || 0} proposed blocks`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_evonodes_proposed_epoch_blocks_by_range - fetch blocks by range', async () => { - try { - const result = await wasmSdk.get_evonodes_proposed_epoch_blocks_by_range( - sdk, - TEST_EVONODE_ID, // start after ID - 100 // limit - ); - console.log(` Found ${result?.length || 0} proposed blocks in range`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- Epochs are time periods in the Dash Platform'); -console.log('- Each epoch contains multiple blocks'); -console.log('- Evonodes propose blocks during consensus'); -console.log('- Network errors are expected when running offline'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/group-queries.test.mjs b/packages/wasm-sdk/test/group-queries.test.mjs deleted file mode 100644 index 04ca36a22a7..00000000000 --- a/packages/wasm-sdk/test/group-queries.test.mjs +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env node -// group-queries.test.mjs - Tests for group query functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nGroup Query Tests\n'); - -// Test values from docs.html -const TEST_GROUP_ID = '49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N'; -const TEST_ACTION_ID = '6XJzL6Qb8Zhwxt4HFwh8NAn7q1u4dwdoUf8EmgzDudFZ'; - -// Initialize SDK - prefetch quorums for trusted mode -console.log('Prefetching trusted quorums...'); -try { - await wasmSdk.prefetch_trusted_quorums_testnet(); - console.log('Quorums prefetched successfully'); -} catch (error) { - console.log('Warning: Could not prefetch quorums:', error.message); -} - -// Use trusted builder as required for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Group Info Queries -describe('Group Information Queries'); - -await test('get_group_info - fetch specific group info', async () => { - try { - const result = await wasmSdk.get_group_info( - sdk, - TEST_GROUP_ID, // group ID - 0 // subgroup position - ); - console.log(` Group info retrieved`); - if (result) { - console.log(` Group type: ${result.type || 'N/A'}`); - console.log(` Member count: ${result.memberCount || 'N/A'}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_group_infos - fetch multiple group infos', async () => { - try { - const result = await wasmSdk.get_group_infos( - sdk, - TEST_GROUP_ID, // group ID - null, // start after - 100 // limit - ); - console.log(` Found ${result?.length || 0} group infos`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Group Action Queries -describe('Group Action Queries'); - -await test('get_group_actions - fetch group actions', async () => { - try { - const result = await wasmSdk.get_group_actions( - sdk, - TEST_GROUP_ID, // group ID - 0, // subgroup position - 'ACTIVE', // action status - null, // start after - 100 // limit - ); - console.log(` Found ${result?.length || 0} group actions`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_group_action_signers - fetch action signers', async () => { - try { - const result = await wasmSdk.get_group_action_signers( - sdk, - TEST_GROUP_ID, // group ID - 0, // subgroup position - 'ACTIVE', // action status - TEST_ACTION_ID // action ID - ); - console.log(` Found ${result?.length || 0} action signers`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- Groups are collections of identities that can perform collective actions'); -console.log('- Group actions require signatures from multiple group members'); -console.log('- Action status can be ACTIVE, COMPLETED, or CANCELLED'); -console.log('- Network errors are expected when running offline'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/identity-queries.test.mjs b/packages/wasm-sdk/test/identity-queries.test.mjs deleted file mode 100644 index a9ecfc880cf..00000000000 --- a/packages/wasm-sdk/test/identity-queries.test.mjs +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/env node -// identity-queries.test.mjs - Tests for identity query functions using documented testnet values - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nIdentity Query Tests Using Documented Testnet Values\n'); - -// DOCUMENTED TEST VALUES FROM docs.html -const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; // Known testnet identity -const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; // DPNS contract ID -const TOKEN_CONTRACT = 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv'; // Example token contract - -console.log('Test Values:'); -console.log(`- Identity: ${TEST_IDENTITY}`); -console.log(`- DPNS Contract: ${DPNS_CONTRACT}`); -console.log(`- Token Contract: ${TOKEN_CONTRACT}`); - -// Initialize SDK - prefetch quorums for trusted mode -console.log('Prefetching trusted quorums...'); -try { - await wasmSdk.prefetch_trusted_quorums_testnet(); - console.log('Quorums prefetched successfully'); -} catch (error) { - console.log('Warning: Could not prefetch quorums:', error.message); -} - -// Use trusted builder as required for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Identity Query Tests -describe('Basic Identity Queries'); - -await test('identity_fetch - documented test identity', async () => { - try { - const result = await wasmSdk.identity_fetch(sdk, TEST_IDENTITY); - console.log(' βœ“ Identity fetched successfully'); - console.log(` ID: ${result?.id || 'N/A'}`); - console.log(` Balance: ${result?.balance || 'N/A'}`); - console.log(` Public Keys: ${result?.publicKeys?.length || 0}`); - } catch (error) { - // Network error is expected if offline - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identity_balance - documented test identity', async () => { - try { - const result = await wasmSdk.get_identity_balance(sdk, TEST_IDENTITY); - console.log(` Balance: ${result}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identity_keys - all keys', async () => { - try { - const result = await wasmSdk.get_identity_keys(sdk, TEST_IDENTITY, 'all'); - console.log(` Found ${result?.length || 0} keys`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identity_nonce', async () => { - try { - const result = await wasmSdk.get_identity_nonce(sdk, TEST_IDENTITY); - console.log(` Nonce: ${result}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identity_contract_nonce - with DPNS contract', async () => { - try { - const result = await wasmSdk.get_identity_contract_nonce(sdk, TEST_IDENTITY, DPNS_CONTRACT); - console.log(` Contract nonce: ${result}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Batch Identity Queries -describe('Batch Identity Queries'); - -await test('get_identities_balances - single identity', async () => { - try { - const result = await wasmSdk.get_identities_balances(sdk, [TEST_IDENTITY]); - console.log(` Balances: ${JSON.stringify(result)}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identity_balance_and_revision', async () => { - try { - const result = await wasmSdk.get_identity_balance_and_revision(sdk, TEST_IDENTITY); - console.log(` Balance: ${result?.balance}, Revision: ${result?.revision}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Contract Keys -describe('Contract Keys Queries'); - -await test('get_identities_contract_keys - DPNS contract', async () => { - try { - const result = await wasmSdk.get_identities_contract_keys( - sdk, - [TEST_IDENTITY], - DPNS_CONTRACT, - 'domain', // document type - 'all' // purposes - ); - console.log(` Found contract keys: ${result?.length || 0}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Token-related Identity Queries -describe('Token Balance Queries'); - -await test('get_identity_token_balances', async () => { - try { - const result = await wasmSdk.get_identity_token_balances( - sdk, - TEST_IDENTITY, - [TOKEN_CONTRACT] - ); - console.log(` Token balances: ${JSON.stringify(result)}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identities_token_balances', async () => { - try { - const result = await wasmSdk.get_identities_token_balances( - sdk, - [TEST_IDENTITY], - TOKEN_CONTRACT - ); - console.log(` Token balances: ${JSON.stringify(result)}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identity_token_infos', async () => { - try { - const result = await wasmSdk.get_identity_token_infos( - sdk, - TEST_IDENTITY, - [TOKEN_CONTRACT] - ); - console.log(` Token infos: ${JSON.stringify(result)}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_identities_token_infos', async () => { - try { - const result = await wasmSdk.get_identities_token_infos( - sdk, - [TEST_IDENTITY], - TOKEN_CONTRACT - ); - console.log(` Token infos for identities: ${JSON.stringify(result)}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Public Key Hash Queries -describe('Public Key Hash Queries'); - -await test('get_identity_by_public_key_hash - requires valid hash', async () => { - try { - // This would need a real public key hash from the test identity - const result = await wasmSdk.get_identity_by_public_key_hash( - sdk, - "invalidhash" // Would need real hash - ); - throw new Error('Should have failed with invalid hash'); - } catch (error) { - if (error.message.includes('Should have failed')) { - throw error; - } - console.log(' Expected error with invalid hash'); - } -}); - -await test('get_identity_by_non_unique_public_key_hash - requires valid hash', async () => { - try { - // Example non-unique public key hash from docs - const result = await wasmSdk.get_identity_by_non_unique_public_key_hash( - sdk, - '518038dc858461bcee90478fd994bba8057b7531', - null // start_after parameter (optional) - ); - console.log(` Found ${result?.length || 0} identities with this public key hash`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- These tests use the documented testnet values from docs.html'); -console.log('- Network errors are expected when running offline'); -console.log('- Some queries may timeout if testnet is slow'); -console.log(`- Test identity ${TEST_IDENTITY} should have activity on testnet`); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/key-derivation.test.js b/packages/wasm-sdk/test/key-derivation.test.js deleted file mode 100644 index e6a1d4e5523..00000000000 --- a/packages/wasm-sdk/test/key-derivation.test.js +++ /dev/null @@ -1,156 +0,0 @@ -// key-derivation.test.js - Tests for key derivation functions -const { loadWasmModule } = require('./test-setup'); - -describe('Key Derivation Tests', () => { - let wasm; - const testSeed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - beforeAll(async () => { - wasm = await loadWasmModule(); - }); - - describe('derive_key_from_seed_with_path', () => { - test('should derive BIP44 mainnet key', async () => { - const path = "m/44'/5'/0'/0/0"; - const result = await wasm.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - console.log('BIP44 result:', result); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - expect(result.private_key_wif).toBeDefined(); - expect(result.private_key_hex).toBeDefined(); - expect(result.public_key).toBeDefined(); - expect(result.address).toBeDefined(); - expect(result.network).toBe("mainnet"); - }); - - test('should derive DIP13 authentication key', async () => { - const path = "m/9'/5'/5'/0'/0'/0'/0'"; - const result = await wasm.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - console.log('DIP13 Authentication key result:', result); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - expect(result.private_key_wif).toBeDefined(); - expect(result.address).toBeDefined(); - }); - - test('should derive DIP13 registration funding key', async () => { - const path = "m/9'/5'/5'/1'/0"; - const result = await wasm.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - console.log('DIP13 Registration funding key result:', result); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - expect(result.private_key_wif).toBeDefined(); - }); - - test('should work with passphrase', async () => { - const path = "m/44'/5'/0'/0/0"; - const passphrase = "test passphrase"; - const result = await wasm.derive_key_from_seed_with_path( - testSeed, - passphrase, - path, - "mainnet" - ); - - console.log('With passphrase result:', result); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - // Address should be different with passphrase - const withoutPassphrase = await wasm.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - expect(result.address).not.toBe(withoutPassphrase.address); - }); - - test('should work on testnet', async () => { - const path = "m/44'/1'/0'/0/0"; - const result = await wasm.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "testnet" - ); - - console.log('Testnet result:', result); - - expect(result).toBeDefined(); - expect(result.network).toBe("testnet"); - expect(result.address).toMatch(/^y/); // Testnet addresses start with 'y' - }); - }); - - describe('generate_mnemonic', () => { - test('should generate 12-word mnemonic', async () => { - const mnemonic = await wasm.generate_mnemonic(12); - const words = mnemonic.split(' '); - - expect(words.length).toBe(12); - expect(wasm.validate_mnemonic(mnemonic)).toBe(true); - }); - - test('should generate 24-word mnemonic', async () => { - const mnemonic = await wasm.generate_mnemonic(24); - const words = mnemonic.split(' '); - - expect(words.length).toBe(24); - expect(wasm.validate_mnemonic(mnemonic)).toBe(true); - }); - - test('should generate mnemonic in different languages', async () => { - const languages = ['en', 'es', 'fr', 'it', 'ja', 'ko', 'pt', 'cs']; - - for (const lang of languages) { - const mnemonic = await wasm.generate_mnemonic(12, lang); - console.log(`${lang} mnemonic:`, mnemonic.substring(0, 30) + '...'); - expect(wasm.validate_mnemonic(mnemonic, lang)).toBe(true); - } - }); - }); - - describe('DIP13 paths', () => { - test('should create correct DIP13 mainnet path info', () => { - const result = wasm.derivation_path_dip13_mainnet(0); - - expect(result).toBeDefined(); - expect(result.path).toBe("m/9'/5'/0'"); - expect(result.purpose).toBe(9); - expect(result.coin_type).toBe(5); - expect(result.account).toBe(0); - }); - - test('should create correct DIP13 testnet path info', () => { - const result = wasm.derivation_path_dip13_testnet(0); - - expect(result).toBeDefined(); - expect(result.path).toBe("m/9'/1'/0'"); - expect(result.purpose).toBe(9); - expect(result.coin_type).toBe(1); - expect(result.account).toBe(0); - }); - }); -}); \ No newline at end of file diff --git a/packages/wasm-sdk/test/key-generation.test.mjs b/packages/wasm-sdk/test/key-generation.test.mjs deleted file mode 100644 index e5c39e95bf3..00000000000 --- a/packages/wasm-sdk/test/key-generation.test.mjs +++ /dev/null @@ -1,595 +0,0 @@ -#!/usr/bin/env node -// key-generation.test.mjs - Tests for wallet and key generation functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -// Test constants -const TEST_MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - -console.log('\nKey Generation Tests\n'); - -// Mnemonic Generation Tests -describe('Mnemonic Generation'); - -await test('generate_mnemonic - 12 words (default)', () => { - const mnemonic = wasmSdk.generate_mnemonic(); - const words = mnemonic.split(' '); - if (words.length !== 12) { - throw new Error(`Expected 12 words, got ${words.length}`); - } - if (!wasmSdk.validate_mnemonic(mnemonic)) { - throw new Error('Generated mnemonic is invalid'); - } -}); - -await test('generate_mnemonic - 15 words', () => { - const mnemonic = wasmSdk.generate_mnemonic(15); - const words = mnemonic.split(' '); - if (words.length !== 15) { - throw new Error(`Expected 15 words, got ${words.length}`); - } - if (!wasmSdk.validate_mnemonic(mnemonic)) { - throw new Error('Generated mnemonic is invalid'); - } -}); - -await test('generate_mnemonic - 18 words', () => { - const mnemonic = wasmSdk.generate_mnemonic(18); - const words = mnemonic.split(' '); - if (words.length !== 18) { - throw new Error(`Expected 18 words, got ${words.length}`); - } - if (!wasmSdk.validate_mnemonic(mnemonic)) { - throw new Error('Generated mnemonic is invalid'); - } -}); - -await test('generate_mnemonic - 21 words', () => { - const mnemonic = wasmSdk.generate_mnemonic(21); - const words = mnemonic.split(' '); - if (words.length !== 21) { - throw new Error(`Expected 21 words, got ${words.length}`); - } - if (!wasmSdk.validate_mnemonic(mnemonic)) { - throw new Error('Generated mnemonic is invalid'); - } -}); - -await test('generate_mnemonic - 24 words', () => { - const mnemonic = wasmSdk.generate_mnemonic(24); - const words = mnemonic.split(' '); - if (words.length !== 24) { - throw new Error(`Expected 24 words, got ${words.length}`); - } - if (!wasmSdk.validate_mnemonic(mnemonic)) { - throw new Error('Generated mnemonic is invalid'); - } -}); - -await test('generate_mnemonic - invalid word count', () => { - try { - wasmSdk.generate_mnemonic(13); - throw new Error('Should have thrown error for invalid word count'); - } catch (error) { - if (!error.message.includes('Word count must be')) { - throw error; - } - } -}); - -// Language-specific mnemonic tests -describe('Mnemonic Languages'); - -const languages = [ - { code: 'en', name: 'English' }, - { code: 'es', name: 'Spanish' }, - { code: 'fr', name: 'French' }, - { code: 'it', name: 'Italian' }, - { code: 'ja', name: 'Japanese' }, - { code: 'ko', name: 'Korean' }, - { code: 'pt', name: 'Portuguese' }, - { code: 'cs', name: 'Czech' }, - { code: 'zh-cn', name: 'Simplified Chinese' }, - { code: 'zh-tw', name: 'Traditional Chinese' } -]; - -for (const { code, name } of languages) { - await test(`generate_mnemonic - ${name} (${code})`, () => { - const mnemonic = wasmSdk.generate_mnemonic(12, code); - if (!wasmSdk.validate_mnemonic(mnemonic, code)) { - throw new Error(`Generated ${name} mnemonic is invalid`); - } - }); -} - -await test('generate_mnemonic - unsupported language', () => { - try { - wasmSdk.generate_mnemonic(12, 'xx'); - throw new Error('Should have thrown error for unsupported language'); - } catch (error) { - if (!error.message.includes('Unsupported language code')) { - throw error; - } - } -}); - -// Mnemonic Validation Tests -describe('Mnemonic Validation'); - -await test('validate_mnemonic - valid mnemonic', () => { - if (!wasmSdk.validate_mnemonic(TEST_MNEMONIC)) { - throw new Error('Test mnemonic should be valid'); - } -}); - -await test('validate_mnemonic - invalid checksum', () => { - const invalidMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"; - if (wasmSdk.validate_mnemonic(invalidMnemonic)) { - throw new Error('Invalid mnemonic should not validate'); - } -}); - -await test('validate_mnemonic - wrong word count', () => { - const invalidMnemonic = "abandon abandon abandon"; - if (wasmSdk.validate_mnemonic(invalidMnemonic)) { - throw new Error('Mnemonic with wrong word count should not validate'); - } -}); - -// Mnemonic to Seed Tests -describe('Mnemonic to Seed'); - -await test('mnemonic_to_seed - without passphrase', () => { - const seed = wasmSdk.mnemonic_to_seed(TEST_MNEMONIC); - if (!seed || seed.length !== 64) { - throw new Error(`Expected 64 byte seed, got ${seed ? seed.length : 'null'}`); - } -}); - -await test('mnemonic_to_seed - with passphrase', () => { - const seed1 = wasmSdk.mnemonic_to_seed(TEST_MNEMONIC, "passphrase"); - const seed2 = wasmSdk.mnemonic_to_seed(TEST_MNEMONIC); - - if (!seed1 || seed1.length !== 64) { - throw new Error('Seed with passphrase should be 64 bytes'); - } - - // Seeds should be different with different passphrases - if (seed1.toString() === seed2.toString()) { - throw new Error('Seeds should differ with different passphrases'); - } -}); - -await test('mnemonic_to_seed - invalid mnemonic', () => { - try { - wasmSdk.mnemonic_to_seed("invalid mnemonic phrase"); - throw new Error('Should have thrown error for invalid mnemonic'); - } catch (error) { - if (!error.message.includes('Invalid mnemonic')) { - throw error; - } - } -}); - -// Key Derivation Tests -describe('Key Derivation from Seed'); - -await test('derive_key_from_seed_phrase - mainnet', () => { - const result = wasmSdk.derive_key_from_seed_phrase(TEST_MNEMONIC, null, "mainnet"); - - if (!result.private_key_wif) throw new Error('Missing private_key_wif'); - if (!result.private_key_hex) throw new Error('Missing private_key_hex'); - if (!result.public_key) throw new Error('Missing public_key'); - if (!result.address) throw new Error('Missing address'); - if (result.network !== "mainnet") throw new Error('Wrong network'); - - // Mainnet addresses should start with 'X' - if (!result.address.startsWith('X')) { - throw new Error(`Mainnet address should start with 'X', got ${result.address}`); - } -}); - -await test('derive_key_from_seed_phrase - testnet', () => { - const result = wasmSdk.derive_key_from_seed_phrase(TEST_MNEMONIC, null, "testnet"); - - if (!result.private_key_wif) throw new Error('Missing private_key_wif'); - if (!result.address) throw new Error('Missing address'); - if (result.network !== "testnet") throw new Error('Wrong network'); - - // Testnet addresses should start with 'y' - if (!result.address.startsWith('y')) { - throw new Error(`Testnet address should start with 'y', got ${result.address}`); - } -}); - -// Derivation Path Tests -describe('Derivation Path Functions'); - -await test('derive_key_from_seed_with_path - BIP44 mainnet', () => { - const path = "m/44'/5'/0'/0/0"; - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, path, "mainnet"); - - if (!result.path || result.path !== path) throw new Error('Path mismatch'); - if (!result.private_key_wif) throw new Error('Missing private_key_wif'); - if (!result.address) throw new Error('Missing address'); - if (!result.address.startsWith('X')) throw new Error('Invalid mainnet address'); -}); - -await test('derive_key_from_seed_with_path - BIP44 testnet', () => { - const path = "m/44'/1'/0'/0/0"; - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, path, "testnet"); - - if (!result.path || result.path !== path) throw new Error('Path mismatch'); - if (!result.address) throw new Error('Missing address'); - if (!result.address.startsWith('y')) throw new Error('Invalid testnet address'); -}); - -await test('derive_key_from_seed_with_path - DIP13 path', () => { - const path = "m/9'/5'/5'/0'/0'/0'/0'"; - const result = wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, path, "mainnet"); - - if (!result.path || result.path !== path) throw new Error('Path mismatch'); - if (!result.private_key_wif) throw new Error('Missing private_key_wif'); - if (!result.address) throw new Error('Missing address'); -}); - -await test('derive_key_from_seed_with_path - invalid path', () => { - try { - wasmSdk.derive_key_from_seed_with_path(TEST_MNEMONIC, null, "invalid/path", "mainnet"); - throw new Error('Should have thrown error for invalid path'); - } catch (error) { - if (!error.message.includes('Invalid derivation path')) { - throw error; - } - } -}); - -// Path Generation Tests -describe('Derivation Path Helpers'); - -await test('derivation_path_bip44_mainnet', () => { - const result = wasmSdk.derivation_path_bip44_mainnet(0, 0, 0); - if (!result) throw new Error('Missing result'); - if (result.purpose !== 44) throw new Error('Wrong purpose'); - if (result.coin_type !== 5) throw new Error('Wrong coin type'); - if (result.account !== 0) throw new Error('Wrong account'); - if (result.change !== 0) throw new Error('Wrong change'); - if (result.index !== 0) throw new Error('Wrong index'); - // Build expected path - const expectedPath = `m/${result.purpose}'/${result.coin_type}'/${result.account}'/${result.change}/${result.index}`; - if (expectedPath !== "m/44'/5'/0'/0/0") throw new Error('Invalid BIP44 mainnet path components'); -}); - -await test('derivation_path_bip44_testnet', () => { - const result = wasmSdk.derivation_path_bip44_testnet(0, 0, 0); - if (!result) throw new Error('Missing result'); - if (result.purpose !== 44) throw new Error('Wrong purpose'); - if (result.coin_type !== 1) throw new Error('Wrong coin type'); - if (result.account !== 0) throw new Error('Wrong account'); - if (result.change !== 0) throw new Error('Wrong change'); - if (result.index !== 0) throw new Error('Wrong index'); - // Build expected path - const expectedPath = `m/${result.purpose}'/${result.coin_type}'/${result.account}'/${result.change}/${result.index}`; - if (expectedPath !== "m/44'/1'/0'/0/0") throw new Error('Invalid BIP44 testnet path components'); -}); - -await test('derivation_path_dip9_mainnet', () => { - const result = wasmSdk.derivation_path_dip9_mainnet(5, 0, 0); - if (!result) throw new Error('Missing result'); - if (result.purpose !== 9) throw new Error('Wrong purpose'); - if (result.coin_type !== 5) throw new Error('Wrong coin type'); - if (result.account !== 5) throw new Error('Wrong account'); - if (result.change !== 0) throw new Error('Wrong change'); - if (result.index !== 0) throw new Error('Wrong index'); - // Build expected path - const expectedPath = `m/${result.purpose}'/${result.coin_type}'/${result.account}'/${result.change}/${result.index}`; - if (expectedPath !== "m/9'/5'/5'/0/0") throw new Error('Invalid DIP9 mainnet path components'); -}); - -await test('derivation_path_dip9_testnet', () => { - const result = wasmSdk.derivation_path_dip9_testnet(5, 0, 0); - if (!result) throw new Error('Missing result'); - if (result.purpose !== 9) throw new Error('Wrong purpose'); - if (result.coin_type !== 1) throw new Error('Wrong coin type'); - if (result.account !== 5) throw new Error('Wrong account'); - if (result.change !== 0) throw new Error('Wrong change'); - if (result.index !== 0) throw new Error('Wrong index'); - // Build expected path - const expectedPath = `m/${result.purpose}'/${result.coin_type}'/${result.account}'/${result.change}/${result.index}`; - if (expectedPath !== "m/9'/1'/5'/0/0") throw new Error('Invalid DIP9 testnet path components'); -}); - -await test('derivation_path_dip13_mainnet', () => { - const result = wasmSdk.derivation_path_dip13_mainnet(0); - if (!result || !result.path) throw new Error('Missing path'); - if (result.path !== "m/9'/5'/0'") throw new Error('Invalid DIP13 mainnet path'); - if (result.purpose !== 9) throw new Error('Wrong purpose'); - if (result.description !== "DIP13 HD identity key path") throw new Error('Wrong description'); -}); - -await test('derivation_path_dip13_testnet', () => { - const result = wasmSdk.derivation_path_dip13_testnet(0); - if (!result || !result.path) throw new Error('Missing path'); - if (result.path !== "m/9'/1'/0'") throw new Error('Invalid DIP13 testnet path'); -}); - -// Child Key Derivation Tests -describe('Child Key Derivation'); - -await test('derive_child_public_key - basic functionality', () => { - // Generate a master extended key to get a valid xpub - const masterResult = wasmSdk.derive_key_from_seed_with_extended_path(TEST_MNEMONIC, null, "m/44'/5'/0'", "mainnet"); - const parentXpub = masterResult.xpub; - - // Derive child public key - const childXpub = wasmSdk.derive_child_public_key(parentXpub, 0, false); - - if (!childXpub) throw new Error('No child xpub returned'); - if (typeof childXpub !== 'string') throw new Error('Child xpub should be string'); - if (childXpub === parentXpub) throw new Error('Child xpub should differ from parent'); - - // Test different indices produce different results - const childXpub1 = wasmSdk.derive_child_public_key(parentXpub, 1, false); - if (childXpub1 === childXpub) throw new Error('Different indices should produce different xpubs'); -}); - -await test('xprv_to_xpub - basic functionality', () => { - // Generate a master extended key to get a valid xprv - const masterResult = wasmSdk.derive_key_from_seed_with_extended_path(TEST_MNEMONIC, null, "m/44'/5'/0'", "mainnet"); - const xprv = masterResult.xprv; - const expectedXpub = masterResult.xpub; - - // Convert xprv to xpub - const derivedXpub = wasmSdk.xprv_to_xpub(xprv); - - if (!derivedXpub) throw new Error('No xpub returned'); - if (typeof derivedXpub !== 'string') throw new Error('Derived xpub should be string'); - if (derivedXpub !== expectedXpub) throw new Error('Derived xpub should match expected xpub'); -}); - -await test('derive_child_public_key - error handling', () => { - try { - wasmSdk.derive_child_public_key("invalid_xpub", 0, false); - throw new Error('Should have thrown error for invalid xpub'); - } catch (error) { - if (!error.message.includes('Invalid extended public key')) { - throw error; - } - } -}); - -await test('xprv_to_xpub - error handling', () => { - try { - wasmSdk.xprv_to_xpub("invalid_xprv"); - throw new Error('Should have thrown error for invalid xprv'); - } catch (error) { - if (!error.message.includes('Invalid extended private key')) { - throw error; - } - } -}); - -// Key Pair Generation Tests -describe('Key Pair Generation'); - -await test('generate_key_pair - mainnet', () => { - const keyPair = wasmSdk.generate_key_pair("mainnet"); - - if (!keyPair.private_key_wif) throw new Error('Missing private_key_wif'); - if (!keyPair.private_key_hex) throw new Error('Missing private_key_hex'); - if (!keyPair.public_key) throw new Error('Missing public_key'); - if (!keyPair.address) throw new Error('Missing address'); - if (keyPair.network !== "mainnet") throw new Error('Wrong network'); - if (!keyPair.address.startsWith('X')) throw new Error('Invalid mainnet address'); -}); - -await test('generate_key_pair - testnet', () => { - const keyPair = wasmSdk.generate_key_pair("testnet"); - - if (!keyPair.address) throw new Error('Missing address'); - if (keyPair.network !== "testnet") throw new Error('Wrong network'); - if (!keyPair.address.startsWith('y')) throw new Error('Invalid testnet address'); -}); - -await test('generate_key_pairs - multiple', () => { - const keyPairs = wasmSdk.generate_key_pairs("mainnet", 3); - - if (!Array.isArray(keyPairs)) throw new Error('Should return array'); - if (keyPairs.length !== 3) throw new Error(`Expected 3 key pairs, got ${keyPairs.length}`); - - // Check each key pair - for (let i = 0; i < keyPairs.length; i++) { - const kp = keyPairs[i]; - if (!kp.address) throw new Error(`Key pair ${i} missing address`); - if (!kp.address.startsWith('X')) throw new Error(`Key pair ${i} invalid address`); - } - - // Ensure all addresses are unique - const addresses = keyPairs.map(kp => kp.address); - const uniqueAddresses = new Set(addresses); - if (uniqueAddresses.size !== addresses.length) { - throw new Error('Generated duplicate addresses'); - } -}); - -// Key Import Tests -describe('Key Import'); - -await test('key_pair_from_wif - mainnet', () => { - // First generate a key pair to get a valid WIF - const generated = wasmSdk.generate_key_pair("mainnet"); - const imported = wasmSdk.key_pair_from_wif(generated.private_key_wif); - - if (imported.address !== generated.address) { - throw new Error('Imported address does not match'); - } - if (imported.public_key !== generated.public_key) { - throw new Error('Imported public key does not match'); - } -}); - -await test('key_pair_from_wif - invalid WIF', () => { - try { - wasmSdk.key_pair_from_wif("invalid_wif"); - throw new Error('Should have thrown error for invalid WIF'); - } catch (error) { - // Expected error - } -}); - -await test('key_pair_from_hex - mainnet', () => { - // Generate a key pair to get valid hex - const generated = wasmSdk.generate_key_pair("mainnet"); - const imported = wasmSdk.key_pair_from_hex(generated.private_key_hex, "mainnet"); - - if (imported.address !== generated.address) { - throw new Error('Imported address does not match'); - } - if (imported.public_key !== generated.public_key) { - throw new Error('Imported public key does not match'); - } -}); - -await test('key_pair_from_hex - invalid hex', () => { - try { - wasmSdk.key_pair_from_hex("invalid_hex", "mainnet"); - throw new Error('Should have thrown error for invalid hex'); - } catch (error) { - // Expected error - } -}); - -// Address Operations Tests -describe('Address Operations'); - -await test('pubkey_to_address - mainnet', () => { - const keyPair = wasmSdk.generate_key_pair("mainnet"); - const address = wasmSdk.pubkey_to_address(keyPair.public_key, "mainnet"); - - if (address !== keyPair.address) { - throw new Error('Address from public key does not match'); - } -}); - -await test('pubkey_to_address - testnet', () => { - const keyPair = wasmSdk.generate_key_pair("testnet"); - const address = wasmSdk.pubkey_to_address(keyPair.public_key, "testnet"); - - if (address !== keyPair.address) { - throw new Error('Address from public key does not match'); - } -}); - -await test('validate_address - valid mainnet', () => { - const keyPair = wasmSdk.generate_key_pair("mainnet"); - if (!wasmSdk.validate_address(keyPair.address, "mainnet")) { - throw new Error('Valid mainnet address should validate'); - } -}); - -await test('validate_address - valid testnet', () => { - const keyPair = wasmSdk.generate_key_pair("testnet"); - if (!wasmSdk.validate_address(keyPair.address, "testnet")) { - throw new Error('Valid testnet address should validate'); - } -}); - -await test('validate_address - wrong network', () => { - const mainnetKey = wasmSdk.generate_key_pair("mainnet"); - const testnetKey = wasmSdk.generate_key_pair("testnet"); - - if (wasmSdk.validate_address(mainnetKey.address, "testnet")) { - throw new Error('Mainnet address should not validate on testnet'); - } - if (wasmSdk.validate_address(testnetKey.address, "mainnet")) { - throw new Error('Testnet address should not validate on mainnet'); - } -}); - -await test('validate_address - invalid address', () => { - if (wasmSdk.validate_address("invalid_address", "mainnet")) { - throw new Error('Invalid address should not validate'); - } -}); - -// Message Signing Tests -describe('Message Signing'); - -await test('sign_message - basic', () => { - const keyPair = wasmSdk.generate_key_pair("mainnet"); - const message = "Hello, Dash!"; - const signature = wasmSdk.sign_message(message, keyPair.private_key_wif); - - if (!signature) throw new Error('No signature returned'); - if (typeof signature !== 'string') throw new Error('Signature should be string'); - if (signature.length < 80) throw new Error('Signature too short'); -}); - -await test('sign_message - different messages produce different signatures', () => { - const keyPair = wasmSdk.generate_key_pair("mainnet"); - const sig1 = wasmSdk.sign_message("Message 1", keyPair.private_key_wif); - const sig2 = wasmSdk.sign_message("Message 2", keyPair.private_key_wif); - - if (sig1 === sig2) { - throw new Error('Different messages should produce different signatures'); - } -}); - -await test('sign_message - same message produces same signature', () => { - const keyPair = wasmSdk.generate_key_pair("mainnet"); - const message = "Test message"; - const sig1 = wasmSdk.sign_message(message, keyPair.private_key_wif); - const sig2 = wasmSdk.sign_message(message, keyPair.private_key_wif); - - if (sig1 !== sig2) { - throw new Error('Same message should produce same signature'); - } -}); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/package-lock.json b/packages/wasm-sdk/test/package-lock.json deleted file mode 100644 index 23f9e29582b..00000000000 --- a/packages/wasm-sdk/test/package-lock.json +++ /dev/null @@ -1,3640 +0,0 @@ -{ - "name": "wasm-sdk-tests", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "wasm-sdk-tests", - "version": "1.0.0", - "devDependencies": { - "jest": "^29.7.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.191", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", - "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/packages/wasm-sdk/test/package.json b/packages/wasm-sdk/test/package.json deleted file mode 100644 index df18eb2fa4a..00000000000 --- a/packages/wasm-sdk/test/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "wasm-sdk-tests", - "version": "1.0.0", - "description": "Tests for WASM SDK", - "scripts": { - "test": "jest", - "test:watch": "jest --watch", - "test:verbose": "jest --verbose" - }, - "devDependencies": { - "jest": "^29.7.0" - }, - "jest": { - "testEnvironment": "node", - "testMatch": [ - "**/*.test.js" - ] - } -} \ No newline at end of file diff --git a/packages/wasm-sdk/test/proof-verification.test.mjs b/packages/wasm-sdk/test/proof-verification.test.mjs deleted file mode 100644 index 2295e07c670..00000000000 --- a/packages/wasm-sdk/test/proof-verification.test.mjs +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env node -// proof-verification.test.mjs - Tests for proof verification functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nProof Verification Tests\n'); - -// Initialize SDK - use trusted builder for WASM (required for proof verification) -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Test values -const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; -const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; - -describe('Proof Verification Functions'); - -await test('verify_proof - requires valid proof data', async () => { - try { - // This would need actual proof data from a query - const result = await wasmSdk.verify_proof( - sdk, - "invalidproofdata" - ); - throw new Error('Should fail with invalid proof'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with invalid proof data'); - } -}); - -await test('verify_proofs - batch proof verification', async () => { - try { - const proofs = JSON.stringify([ - "invalidproof1", - "invalidproof2" - ]); - - const result = await wasmSdk.verify_proofs(sdk, proofs); - throw new Error('Should fail with invalid proofs'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with invalid proof data'); - } -}); - -describe('Proof Request and Verification Flow'); - -await test('Query with proof then verify - identity fetch', async () => { - try { - // First, fetch with proof - console.log(' Step 1: Fetching identity with proof...'); - const fetchResult = await wasmSdk.identity_fetch(sdk, TEST_IDENTITY); - - // In a real scenario, we would extract the proof from the response - // and verify it separately - console.log(' βœ“ Identity fetched (proof verification happens internally)'); - - // The SDK automatically verifies proofs when using trusted builder - if (fetchResult && fetchResult.id) { - console.log(` βœ“ Identity verified: ${fetchResult.id}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else if (error.message.includes('quorum')) { - console.log(' Expected quorum cache error (missing quorum data)'); - } else { - throw error; - } - } -}); - -await test('Query with proof then verify - data contract fetch', async () => { - try { - console.log(' Step 1: Fetching data contract with proof...'); - const contractResult = await wasmSdk.data_contract_fetch(sdk, DPNS_CONTRACT); - - console.log(' βœ“ Contract fetched (proof verification happens internally)'); - - if (contractResult && contractResult.id) { - console.log(` βœ“ Contract verified: ${contractResult.id}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else if (error.message.includes('quorum')) { - console.log(' Expected quorum cache error (missing quorum data)'); - } else { - throw error; - } - } -}); - -describe('Proof Types and Formats'); - -await test('Different proof request types', async () => { - try { - // Example of different proof types supported - const proofTypes = [ - 'IdentityProof', - 'DocumentProof', - 'DataContractProof', - 'StateTransitionProof' - ]; - - console.log(` Supported proof types: ${proofTypes.join(', ')}`); - - // In WASM SDK, proofs are automatically requested when prove=true - // and verified when using trusted builder - console.log(' βœ“ Proof types documented'); - } catch (error) { - throw error; - } -}); - -describe('Proof Validation Edge Cases'); - -await test('Empty proof data', async () => { - try { - const result = await wasmSdk.verify_proof(sdk, ""); - throw new Error('Should fail with empty proof'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with empty proof'); - } -}); - -await test('Malformed proof data', async () => { - try { - const result = await wasmSdk.verify_proof(sdk, "not-a-valid-proof"); - throw new Error('Should fail with malformed proof'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with malformed proof'); - } -}); - -await test('Proof verification in non-trusted mode', async () => { - try { - // Create non-trusted SDK - const nonTrustedBuilder = wasmSdk.WasmSdkBuilder.new_testnet(); - const nonTrustedSdk = await nonTrustedBuilder.build(); - - // Try to fetch with proof in non-trusted mode - const result = await wasmSdk.identity_fetch(nonTrustedSdk, TEST_IDENTITY); - - nonTrustedSdk.free(); - throw new Error('Should fail in non-trusted mode'); - } catch (error) { - if (error.message.includes('Non-trusted mode is not supported')) { - console.log(' βœ“ Correctly rejected non-trusted mode for proof verification'); - } else if (error.message.includes('Should fail')) { - throw error; - } else { - console.log(' Expected error in non-trusted mode'); - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- Proof verification requires trusted SDK builder in WASM'); -console.log('- Proofs are automatically verified when using trusted builder'); -console.log('- Non-trusted mode is not supported for proof verification in WASM'); -console.log('- Quorum data must be cached for successful proof verification'); -console.log('- Network connectivity required for fetching proofs'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/protocol-version-queries.test.mjs b/packages/wasm-sdk/test/protocol-version-queries.test.mjs deleted file mode 100644 index 7e8b6ba2843..00000000000 --- a/packages/wasm-sdk/test/protocol-version-queries.test.mjs +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env node -// protocol-version-queries.test.mjs - Tests for protocol and version query functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nProtocol & Version Query Tests\n'); - -// Test values from docs.html -const TEST_PROTX_HASH = '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113'; - -// Initialize SDK - prefetch quorums for trusted mode -console.log('Prefetching trusted quorums...'); -try { - await wasmSdk.prefetch_trusted_quorums_testnet(); - console.log('Quorums prefetched successfully'); -} catch (error) { - console.log('Warning: Could not prefetch quorums:', error.message); -} - -// Use trusted builder as required for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Protocol Version Queries -describe('Protocol Version Upgrade Queries'); - -await test('get_protocol_version_upgrade_state - fetch upgrade state', async () => { - try { - const result = await wasmSdk.get_protocol_version_upgrade_state(sdk); - console.log(` Protocol version upgrade state retrieved`); - if (result) { - console.log(` Current version: ${result.currentVersion || 'N/A'}`); - console.log(` Next version: ${result.nextVersion || 'N/A'}`); - console.log(` Vote status: ${result.voteStatus || 'N/A'}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_protocol_version_upgrade_vote_status - fetch vote status', async () => { - try { - const result = await wasmSdk.get_protocol_version_upgrade_vote_status( - sdk, - TEST_PROTX_HASH, // start protx hash - 100 // count - ); - console.log(` Found ${result?.length || 0} vote statuses`); - if (result && result.length > 0) { - const firstVote = result[0]; - console.log(` First vote from: ${firstVote.proTxHash || 'N/A'}`); - console.log(` Vote: ${firstVote.vote || 'N/A'}`); - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- Protocol version upgrades require masternode voting'); -console.log('- Upgrade state shows current and proposed protocol versions'); -console.log('- Vote status shows how individual masternodes voted'); -console.log('- Network errors are expected when running offline'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/query-coverage-analysis.md b/packages/wasm-sdk/test/query-coverage-analysis.md deleted file mode 100644 index 9969c88632d..00000000000 --- a/packages/wasm-sdk/test/query-coverage-analysis.md +++ /dev/null @@ -1,96 +0,0 @@ -# Query Coverage Analysis: docs.html vs Tests - -## Queries in docs.html - -### Identity Queries -- [x] Get Identity (`identity_fetch`) - Tested in identity-queries.test.mjs -- [x] Get Identity Keys (`get_identity_keys`) - Tested in identity-queries.test.mjs -- [x] Get Identities Contract Keys (`get_identities_contract_keys`) - Tested in identity-queries.test.mjs -- [x] Get Identity Nonce (`get_identity_nonce`) - Tested in identity-queries.test.mjs -- [x] Get Identity Contract Nonce (`get_identity_contract_nonce`) - Tested in identity-queries.test.mjs -- [x] Get Identity Balance (`get_identity_balance`) - Tested in identity-queries.test.mjs -- [x] Get Identities Balances (`get_identities_balances`) - Tested in identity-queries.test.mjs -- [x] Get Identity Balance and Revision (`get_identity_balance_and_revision`) - Tested in identity-queries.test.mjs -- [x] Get Identity by Unique Public Key Hash (`get_identity_by_public_key_hash`) - Tested in identity-queries.test.mjs -- [x] Get Identity by Non-Unique Public Key Hash (`get_identity_by_non_unique_public_key_hash`) - Tested in identity-queries.test.mjs -- [x] Get Identity Token Balances (`get_identity_token_balances`) - Tested in identity-queries.test.mjs -- [x] Get Identities Token Balances (`get_identities_token_balances`) - Tested in identity-queries.test.mjs -- [x] Get Identity Token Info (`get_identity_token_infos`) - Tested in identity-queries.test.mjs -- [x] Get Identities Token Info (`get_identities_token_infos`) - Tested in identity-queries.test.mjs - -### Data Contract Queries -- [x] Get Data Contract (`data_contract_fetch`) - Tested in document-queries.test.mjs -- [x] Get Data Contract History (`data_contract_fetch_history`) - Tested in document-queries.test.mjs -- [x] Get Data Contracts (`get_data_contracts`) - Tested in document-queries.test.mjs - -### Document Queries -- [x] Get Documents (`get_documents`) - Tested in document-queries.test.mjs -- [x] Get Document (`get_single_document`) - Tested in document-queries.test.mjs - -### DPNS Queries -- [x] Get DPNS Usernames (`get_dpns_usernames`) - Tested in dpns.test.mjs -- [x] DPNS Check Availability (`dpns_is_name_available`) - Tested in dpns.test.mjs -- [x] DPNS Resolve Name (`dpns_resolve_name`) - Tested in dpns.test.mjs - -### Voting & Contested Resources -- [x] Get Contested Resources (`get_contested_resources`) - Tested in voting-contested-resources.test.mjs -- [x] Get Contested Resource Vote State (`get_contested_resource_vote_state`) - Tested in voting-contested-resources.test.mjs -- [x] Get Contested Resource Voters for Identity (`get_contested_resource_voters_for_identity`) - Tested in voting-contested-resources.test.mjs -- [x] Get Contested Resource Identity Votes (`get_contested_resource_identity_votes`) - Tested in voting-contested-resources.test.mjs -- [x] Get Vote Polls by End Date (`get_vote_polls_by_end_date`) - Tested in voting-contested-resources.test.mjs - -### Protocol & Version -- [x] Get Protocol Version Upgrade State (`get_protocol_version_upgrade_state`) - Tested in protocol-version-queries.test.mjs -- [x] Get Protocol Version Upgrade Vote Status (`get_protocol_version_upgrade_vote_status`) - Tested in protocol-version-queries.test.mjs - -### Epoch & Block -- [x] Get Epochs Info (`get_epochs_info`) - Tested in epoch-block-queries.test.mjs -- [x] Get Current Epoch (`get_current_epoch`) - Tested in sdk-init-simple.test.mjs and document-queries.test.mjs -- [x] Get Finalized Epoch Info (`get_finalized_epoch_infos`) - Tested in epoch-block-queries.test.mjs -- [x] Get Evonodes Proposed Epoch Blocks by IDs (`get_evonodes_proposed_epoch_blocks_by_ids`) - Tested in epoch-block-queries.test.mjs -- [x] Get Evonodes Proposed Epoch Blocks by Range (`get_evonodes_proposed_epoch_blocks_by_range`) - Tested in epoch-block-queries.test.mjs - -### Token Queries -- [x] Get Token Statuses (`get_token_statuses`) - Tested in token-queries.test.mjs -- [x] Get Token Direct Purchase Prices (`get_token_direct_purchase_prices`) - Tested in token-queries.test.mjs -- [x] Get Token Contract Info (`get_token_contract_info`) - Tested in token-queries.test.mjs -- [x] Get Token Perpetual Distribution Last Claim (`get_token_perpetual_distribution_last_claim`) - Tested in token-queries.test.mjs -- [x] Get Token Total Supply (`get_token_total_supply`) - Tested in token-queries.test.mjs - -### Group Queries -- [x] Get Group Info (`get_group_info`) - Tested in group-queries.test.mjs -- [x] Get Group Infos (`get_group_infos`) - Tested in group-queries.test.mjs -- [x] Get Group Actions (`get_group_actions`) - Tested in group-queries.test.mjs -- [x] Get Group Action Signers (`get_group_action_signers`) - Tested in group-queries.test.mjs - -### System & Utility -- [x] Get Status (`get_status`) - Tested in document-queries.test.mjs and sdk-init-simple.test.mjs -- [x] Get Current Quorums Info (`get_current_quorums_info`) - Tested in system-utility-queries.test.mjs -- [x] Get Prefunded Specialized Balance (`get_prefunded_specialized_balance`) - Tested in specialized-queries.test.mjs -- [x] Get Total Credits in Platform (`get_total_credits_in_platform`) - Tested in system-utility-queries.test.mjs -- [x] Get Path Elements (`get_path_elements`) - Tested in utilities.test.mjs -- [x] Wait for State Transition Result (`wait_for_state_transition_result`) - Tested in utilities.test.mjs - -### Masternode Queries (in specialized-queries.test.mjs but not in docs.html) -- [x] Get Masternode Status (`get_masternode_status`) - Tested in specialized-queries.test.mjs -- [x] Get Masternode Score (`get_masternode_score`) - Tested in specialized-queries.test.mjs - -## Summary - -**Total Queries in docs.html**: ~45 -**Total Queries Tested**: ~47 (including masternode queries) -**Coverage**: 100% - -## Test Files Created - -1. **voting-contested-resources.test.mjs** - All 5 voting & contested resources queries -2. **token-queries.test.mjs** - All 5 token queries -3. **group-queries.test.mjs** - All 4 group queries -4. **epoch-block-queries.test.mjs** - Missing 4 epoch & block queries -5. **protocol-version-queries.test.mjs** - All 2 protocol & version queries -6. **system-utility-queries.test.mjs** - Missing 2 system utility queries -7. Updated **identity-queries.test.mjs** - Added missing identity queries -8. Updated **document-queries.test.mjs** - Added get_data_contracts -9. Updated **dpns.test.mjs** - Added get_dpns_usernames - -All queries from docs.html are now covered in the test suite! \ No newline at end of file diff --git a/packages/wasm-sdk/test/run-all-tests.mjs b/packages/wasm-sdk/test/run-all-tests.mjs deleted file mode 100755 index 2db6924f6d5..00000000000 --- a/packages/wasm-sdk/test/run-all-tests.mjs +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/env node -// run-all-tests.mjs - Comprehensive test runner for WASM SDK - -import { spawn } from 'child_process'; -import { readFileSync, writeFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Test files to run -const testFiles = [ - { name: 'SDK Initialization', file: 'sdk-init-simple.test.mjs' }, - { name: 'Key Generation', file: 'key-generation.test.mjs' }, - { name: 'DIP Derivation', file: 'dip-derivation.test.mjs' }, - { name: 'DPNS Functions', file: 'dpns.test.mjs' }, - { name: 'Utility Functions', file: 'utilities-simple.test.mjs' }, - { name: 'Identity Queries', file: 'identity-queries.test.mjs' }, - { name: 'Document Queries', file: 'document-queries.test.mjs' }, - { name: 'Specialized Queries', file: 'specialized-queries.test.mjs' }, - { name: 'Voting & Contested Resources', file: 'voting-contested-resources.test.mjs' }, - { name: 'Token Queries', file: 'token-queries.test.mjs' }, - { name: 'Group Queries', file: 'group-queries.test.mjs' }, - { name: 'Epoch & Block Queries', file: 'epoch-block-queries.test.mjs' }, - { name: 'Protocol & Version Queries', file: 'protocol-version-queries.test.mjs' }, - { name: 'System & Utility Queries', file: 'system-utility-queries.test.mjs' }, - { name: 'State Transitions', file: 'state-transitions.test.mjs' }, - { name: 'Proof Verification', file: 'proof-verification.test.mjs' } -]; - -// Test results -const results = { - totalTests: 0, - totalPassed: 0, - totalFailed: 0, - suites: [], - startTime: Date.now() -}; - -// ANSI color codes -const colors = { - reset: '\x1b[0m', - bright: '\x1b[1m', - green: '\x1b[32m', - red: '\x1b[31m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - cyan: '\x1b[36m' -}; - -console.log(`${colors.bright}${colors.blue}========================================`); -console.log(` WASM SDK Comprehensive Tests `); -console.log(`========================================${colors.reset}\n`); - -// Function to run a single test file -async function runTest(name, file) { - return new Promise((resolve) => { - console.log(`${colors.cyan}Running ${name} tests...${colors.reset}`); - - const startTime = Date.now(); - const testPath = join(__dirname, file); - - let output = ''; - let errorOutput = ''; - - const child = spawn('node', [testPath], { - env: { ...process.env, NODE_NO_WARNINGS: '1' } - }); - - child.stdout.on('data', (data) => { - output += data.toString(); - }); - - child.stderr.on('data', (data) => { - errorOutput += data.toString(); - }); - - child.on('close', (code) => { - const duration = Date.now() - startTime; - - // Parse test results from output - const passedMatch = output.match(/(\d+) passed/); - const failedMatch = output.match(/(\d+) failed/); - const totalMatch = output.match(/(\d+) total/); - - const passed = passedMatch ? parseInt(passedMatch[1]) : 0; - const failed = failedMatch ? parseInt(failedMatch[1]) : 0; - const total = totalMatch ? parseInt(totalMatch[1]) : 0; - - // Extract individual test results - const testLines = output.split('\n').filter(line => - line.includes('βœ…') || line.includes('❌') - ); - - const suite = { - name, - file, - duration, - passed, - failed, - total, - exitCode: code, - tests: testLines.map(line => { - const isPassing = line.includes('βœ…'); - const testName = line.replace(/^.*[βœ…βŒ]\s*/, '').trim(); - return { name: testName, passed: isPassing }; - }), - errors: [] - }; - - // Extract error messages - const errorLines = output.split('\n').filter((line, index, arr) => { - return line.includes('❌') && index + 1 < arr.length; - }); - - errorLines.forEach((line, index) => { - const testName = line.replace(/^.*❌\s*/, '').trim(); - const nextLines = output.split('\n').slice( - output.split('\n').indexOf(line) + 1 - ); - const errorMessage = nextLines.find(l => l.trim().startsWith(''))?.trim() || ''; - if (errorMessage) { - suite.errors.push({ test: testName, error: errorMessage }); - } - }); - - // Check for panics or crashes - if (errorOutput.includes('panicked at') || output.includes('panicked at')) { - suite.hasPanic = true; - suite.panicMessage = 'Test suite panicked - see output for details'; - } - - results.suites.push(suite); - results.totalTests += total; - results.totalPassed += passed; - results.totalFailed += failed; - - // Print summary for this suite - if (code === 0 && failed === 0) { - console.log(`${colors.green}βœ“ ${name}: ${passed}/${total} tests passed${colors.reset} (${duration}ms)\n`); - } else { - console.log(`${colors.red}βœ— ${name}: ${passed}/${total} tests passed, ${failed} failed${colors.reset} (${duration}ms)`); - if (suite.hasPanic) { - console.log(` ${colors.yellow}⚠ Suite panicked during execution${colors.reset}`); - } - console.log(''); - } - - resolve(suite); - }); - }); -} - -// Run all tests sequentially -async function runAllTests() { - for (const { name, file } of testFiles) { - try { - await runTest(name, file); - } catch (error) { - console.error(`${colors.red}Error running ${name}: ${error.message}${colors.reset}`); - results.suites.push({ - name, - file, - error: error.message, - passed: 0, - failed: 1, - total: 1 - }); - results.totalFailed += 1; - results.totalTests += 1; - } - } - - results.endTime = Date.now(); - results.totalDuration = results.endTime - results.startTime; - - // Print overall summary - console.log(`${colors.bright}${colors.blue}========================================`); - console.log(` Test Summary `); - console.log(`========================================${colors.reset}\n`); - - console.log(`Total Test Suites: ${results.suites.length}`); - console.log(`Total Tests: ${results.totalTests}`); - console.log(`${colors.green}Passed: ${results.totalPassed}${colors.reset}`); - console.log(`${colors.red}Failed: ${results.totalFailed}${colors.reset}`); - console.log(`Time: ${(results.totalDuration / 1000).toFixed(2)}s\n`); - - // Show failed tests - if (results.totalFailed > 0) { - console.log(`${colors.red}${colors.bright}Failed Tests:${colors.reset}`); - results.suites.forEach(suite => { - if (suite.failed > 0 || suite.hasPanic) { - console.log(`\n${colors.yellow}${suite.name}:${colors.reset}`); - suite.tests.filter(t => !t.passed).forEach(test => { - console.log(` ${colors.red}βœ— ${test.name}${colors.reset}`); - }); - if (suite.hasPanic) { - console.log(` ${colors.yellow}⚠ ${suite.panicMessage}${colors.reset}`); - } - } - }); - } - - // Generate detailed report - generateReport(results); - - // Exit with appropriate code - process.exit(results.totalFailed > 0 ? 1 : 0); -} - -// Generate detailed HTML report -function generateReport(results) { - const reportPath = join(__dirname, 'test-report.html'); - - const html = ` - - - - WASM SDK Test Report - - - - -
    -

    WASM SDK Test Report

    -

    Generated: ${new Date().toLocaleString()}

    - -
    -
    -

    Total Tests

    -
    ${results.totalTests}
    -
    -
    -

    Passed

    -
    ${results.totalPassed}
    -
    -
    -

    Failed

    -
    ${results.totalFailed}
    -
    -
    - -

    Test Suites

    - ${results.suites.map((suite, index) => ` -
    -
    - ${suite.name} - ${suite.passed}/${suite.total} passed (${suite.duration}ms) - ${suite.hasPanic ? '⚠ Panic' : ''} -
    -
    - ${suite.hasPanic ? `
    ${suite.panicMessage}
    ` : ''} - ${suite.tests.map(test => ` -
    - ${test.name} -
    - `).join('')} -
    -
    - `).join('')} - -

    Execution Details

    -

    Total execution time: ${(results.totalDuration / 1000).toFixed(2)} seconds

    -

    Test files executed: ${results.suites.length}

    -
    - - - `; - - writeFileSync(reportPath, html); - console.log(`\n${colors.cyan}Detailed report saved to: ${reportPath}${colors.reset}`); -} - -// Run the tests -runAllTests().catch(error => { - console.error(`${colors.red}Fatal error: ${error.message}${colors.reset}`); - process.exit(1); -}); \ No newline at end of file diff --git a/packages/wasm-sdk/test/run-tests.mjs b/packages/wasm-sdk/test/run-tests.mjs deleted file mode 100644 index f00f4b626d7..00000000000 --- a/packages/wasm-sdk/test/run-tests.mjs +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env node -// run-tests.mjs - Node.js test runner for WASM SDK with ES modules support - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Test utilities -const tests = []; -let currentSuite = ''; - -function describe(name, fn) { - currentSuite = name; - console.log(`\n${name}`); - fn(); -} - -function test(name, fn) { - tests.push({ suite: currentSuite, name, fn }); -} - -function expect(value) { - return { - toBe(expected) { - if (value !== expected) { - throw new Error(`Expected ${value} to be ${expected}`); - } - }, - toBeDefined() { - if (value === undefined) { - throw new Error(`Expected value to be defined`); - } - }, - toMatch(pattern) { - if (!pattern.test(value)) { - throw new Error(`Expected ${value} to match ${pattern}`); - } - } - }; -} - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Define tests -const testSeed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - -describe('Key Derivation Tests', () => { - describe('derive_key_from_seed_with_path', () => { - test('should derive BIP44 mainnet key', async () => { - const path = "m/44'/5'/0'/0/0"; - const result = await wasmSdk.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - console.log(' BIP44 result:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - expect(result.private_key_wif).toBeDefined(); - expect(result.private_key_hex).toBeDefined(); - expect(result.public_key).toBeDefined(); - expect(result.address).toBeDefined(); - expect(result.network).toBe("mainnet"); - }); - - test('should derive DIP13 authentication key', async () => { - const path = "m/9'/5'/5'/0'/0'/0'/0'"; - const result = await wasmSdk.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - console.log(' DIP13 Authentication key result:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - expect(result.private_key_wif).toBeDefined(); - expect(result.address).toBeDefined(); - }); - - test('should derive DIP13 registration funding key', async () => { - const path = "m/9'/5'/5'/1'/0"; - const result = await wasmSdk.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - console.log(' DIP13 Registration funding key result:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - expect(result.private_key_wif).toBeDefined(); - }); - - test('should work with passphrase', async () => { - const path = "m/44'/5'/0'/0/0"; - const passphrase = "test passphrase"; - const result = await wasmSdk.derive_key_from_seed_with_path( - testSeed, - passphrase, - path, - "mainnet" - ); - - console.log(' With passphrase result:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.path).toBe(path); - - // Address should be different with passphrase - const withoutPassphrase = await wasmSdk.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "mainnet" - ); - - if (result.address === withoutPassphrase.address) { - throw new Error('Address should be different with passphrase'); - } - }); - - test('should work on testnet', async () => { - const path = "m/44'/1'/0'/0/0"; - const result = await wasmSdk.derive_key_from_seed_with_path( - testSeed, - undefined, - path, - "testnet" - ); - - console.log(' Testnet result:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.network).toBe("testnet"); - expect(result.address).toMatch(/^y/); // Testnet addresses start with 'y' - }); - }); - - describe('generate_mnemonic', () => { - test('should generate 12-word mnemonic', async () => { - const mnemonic = wasmSdk.generate_mnemonic(12); - const words = mnemonic.split(' '); - - console.log(' Generated 12-word mnemonic:', mnemonic); - - expect(words.length).toBe(12); - expect(wasmSdk.validate_mnemonic(mnemonic)).toBe(true); - }); - - test('should generate 24-word mnemonic', async () => { - const mnemonic = wasmSdk.generate_mnemonic(24); - const words = mnemonic.split(' '); - - console.log(' Generated 24-word mnemonic (first 10 words):', words.slice(0, 10).join(' ') + '...'); - - expect(words.length).toBe(24); - expect(wasmSdk.validate_mnemonic(mnemonic)).toBe(true); - }); - - test('should generate mnemonic in different languages', async () => { - const languages = ['en', 'es', 'fr', 'it', 'ja', 'ko', 'pt', 'cs']; - - for (const lang of languages) { - const mnemonic = wasmSdk.generate_mnemonic(12, lang); - console.log(` ${lang} mnemonic:`, mnemonic.substring(0, 40) + '...'); - expect(wasmSdk.validate_mnemonic(mnemonic, lang)).toBe(true); - } - }); - }); - - describe('DIP13 paths', () => { - test('should create correct DIP13 mainnet path info', () => { - const result = wasmSdk.derivation_path_dip13_mainnet(0); - - console.log(' DIP13 mainnet path:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.path).toBe("m/9'/5'/0'"); - expect(result.purpose).toBe(9); - expect(result.coin_type).toBe(5); - expect(result.account).toBe(0); - }); - - test('should create correct DIP13 testnet path info', () => { - const result = wasmSdk.derivation_path_dip13_testnet(0); - - console.log(' DIP13 testnet path:', JSON.stringify(result, null, 2)); - - expect(result).toBeDefined(); - expect(result.path).toBe("m/9'/1'/0'"); - expect(result.purpose).toBe(9); - expect(result.coin_type).toBe(1); - expect(result.account).toBe(0); - }); - }); -}); - -// Run tests -console.log('\nRunning tests...\n'); -let passed = 0; -let failed = 0; - -for (const { suite, name, fn } of tests) { - try { - await fn(); - console.log(` βœ… ${name}`); - passed++; - } catch (error) { - console.log(` ❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${tests.length} total`); -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/sample-query.test.mjs b/packages/wasm-sdk/test/sample-query.test.mjs deleted file mode 100644 index 1a0c50d58a0..00000000000 --- a/packages/wasm-sdk/test/sample-query.test.mjs +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env node -// sample-query.test.mjs - Sample tests for query functions (network-dependent) - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nSample Query Tests (Network-Dependent)\n'); - -// Initialize SDK -const builder = wasmSdk.WasmSdkBuilder.new_testnet(); -const sdk = await builder.build(); - -// Identity Query Tests -describe('Identity Queries (Expected to fail without network)'); - -await test('identity_fetch - requires valid identity ID', async () => { - try { - // This will fail without a valid identity ID - const result = await wasmSdk.identity_fetch(sdk, "invalididentityid"); - throw new Error('Should have failed with invalid ID'); - } catch (error) { - // Expected to fail - if (error.message.includes('Should have failed')) { - throw error; - } - console.log(' Expected error: ' + error.message.substring(0, 50) + '...'); - } -}); - -await test('get_identity_balance - requires valid identity', async () => { - try { - const result = await wasmSdk.get_identity_balance( - sdk, - "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" // Example testnet identity - ); - // If this succeeds, network is available - console.log(' Network available! Balance:', result); - } catch (error) { - // Expected to fail without network - console.log(' Expected network error'); - } -}); - -// Document Query Tests -describe('Document Queries'); - -await test('get_documents - DPNS contract', async () => { - try { - // DPNS contract ID on testnet - const dpnsContractId = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - - const result = await wasmSdk.get_documents( - sdk, - dpnsContractId, - "domain", - null, // no where clause - null, // no order by - 10, // limit - null, // no start after - null // no start at - ); - - console.log(' Network available! Found documents:', result?.length || 0); - } catch (error) { - console.log(' Expected network error'); - } -}); - -// Data Contract Query Tests -describe('Data Contract Queries'); - -await test('data_contract_fetch - DPNS contract', async () => { - try { - const dpnsContractId = "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"; - const result = await wasmSdk.data_contract_fetch(sdk, dpnsContractId); - - console.log(' Network available! Contract fetched'); - } catch (error) { - console.log(' Expected network error'); - } -}); - -// System Query Tests -describe('System Queries'); - -await test('get_status - platform status', async () => { - try { - const result = await wasmSdk.get_status(sdk); - console.log(' Network available! Status:', result); - } catch (error) { - console.log(' Expected network error'); - } -}); - -await test('get_current_epoch', async () => { - try { - const result = await wasmSdk.get_current_epoch(sdk); - console.log(' Network available! Current epoch:', result); - } catch (error) { - console.log(' Expected network error'); - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nNote: These tests require network connectivity to Dash Platform testnet.'); -console.log('Failures are expected when running offline or when testnet is unavailable.'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/sdk-init-simple.test.mjs b/packages/wasm-sdk/test/sdk-init-simple.test.mjs deleted file mode 100644 index cd495f849ca..00000000000 --- a/packages/wasm-sdk/test/sdk-init-simple.test.mjs +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env node -// sdk-init-simple.test.mjs - Simplified SDK initialization tests - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test results -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -console.log('\nSDK Initialization Tests\n'); - -// Test 1: Check WasmSdkBuilder exists -await test('WasmSdkBuilder class exists', () => { - if (!wasmSdk.WasmSdkBuilder) { - throw new Error('WasmSdkBuilder not found'); - } -}); - -// Test 2: Check static methods exist -await test('WasmSdkBuilder has static methods', () => { - if (typeof wasmSdk.WasmSdkBuilder.new_mainnet !== 'function') { - throw new Error('new_mainnet method not found'); - } - if (typeof wasmSdk.WasmSdkBuilder.new_testnet !== 'function') { - throw new Error('new_testnet method not found'); - } - if (typeof wasmSdk.WasmSdkBuilder.getLatestVersionNumber !== 'function') { - throw new Error('getLatestVersionNumber method not found'); - } -}); - -// Test 3: Get latest version -await test('getLatestVersionNumber returns a number', () => { - const version = wasmSdk.WasmSdkBuilder.getLatestVersionNumber(); - if (typeof version !== 'number') { - throw new Error(`Expected number, got ${typeof version}`); - } - console.log(` Latest version: ${version}`); -}); - -// Test 4: Create and test SDK instance -await test('Can create SDK instance', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - const sdk = await builder.build(); - - if (!sdk || !sdk.__wbg_ptr) { - throw new Error('Failed to create SDK instance'); - } - - const version = sdk.version(); - console.log(` SDK version: ${version}`); - - // Test that state transition methods exist - if (typeof sdk.tokenMint !== 'function') { - throw new Error('tokenMint method not found on SDK instance'); - } - if (typeof sdk.documentCreate !== 'function') { - throw new Error('documentCreate method not found on SDK instance'); - } -}); - -// Test 5: Check query functions -await test('Query functions exist as top-level exports', () => { - const queryFunctions = [ - 'identity_fetch', - 'get_documents', - 'data_contract_fetch', - 'get_status', - 'get_current_epoch' - ]; - - for (const fn of queryFunctions) { - if (typeof wasmSdk[fn] !== 'function') { - throw new Error(`${fn} not found`); - } - } -}); - -// Test 6: Check key generation functions -await test('Key generation functions exist', () => { - const keyFunctions = [ - 'generate_mnemonic', - 'validate_mnemonic', - 'mnemonic_to_seed', - 'derive_key_from_seed_with_path', - 'generate_key_pair', - 'pubkey_to_address', - 'validate_address' - ]; - - for (const fn of keyFunctions) { - if (typeof wasmSdk[fn] !== 'function') { - throw new Error(`${fn} not found`); - } - } -}); - -// Test 7: Check DPNS functions -await test('DPNS functions exist', () => { - const dpnsFunctions = [ - 'dpns_convert_to_homograph_safe', - 'dpns_is_valid_username', - 'dpns_is_contested_username', - 'dpns_register_name', - 'dpns_is_name_available', - 'dpns_resolve_name' - ]; - - for (const fn of dpnsFunctions) { - if (typeof wasmSdk[fn] !== 'function') { - throw new Error(`${fn} not found`); - } - } -}); - -// Test 8: Test mnemonic generation -await test('Can generate mnemonic', () => { - const mnemonic = wasmSdk.generate_mnemonic(12); - const words = mnemonic.split(' '); - - if (words.length !== 12) { - throw new Error(`Expected 12 words, got ${words.length}`); - } - - if (!wasmSdk.validate_mnemonic(mnemonic)) { - throw new Error('Generated mnemonic is invalid'); - } -}); - -// Test 9: Test key derivation -await test('Can derive keys from mnemonic', () => { - const testMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - const result = wasmSdk.derive_key_from_seed_with_path( - testMnemonic, - undefined, - "m/44'/5'/0'/0/0", - "mainnet" - ); - - if (!result.address) { - throw new Error('No address in result'); - } - if (!result.private_key_wif) { - throw new Error('No private key in result'); - } - if (!result.public_key) { - throw new Error('No public key in result'); - } -}); - -// Test 10: Test address validation -await test('Can validate addresses', () => { - // Use real valid Dash addresses - const mainnetAddress = "XoJA8qE3N2Y3jMLEtZ3vcN42qseZ8LvFf5"; // Real mainnet address - const testnetAddress = "yRd4FhXfVGHXpsuZXPNkMrfD9GVj46pnjt"; // Real testnet address - - if (!wasmSdk.validate_address(mainnetAddress, "mainnet")) { - throw new Error('Failed to validate mainnet address'); - } - - if (!wasmSdk.validate_address(testnetAddress, "testnet")) { - throw new Error('Failed to validate testnet address'); - } - - if (wasmSdk.validate_address(mainnetAddress, "testnet")) { - throw new Error('Mainnet address should not be valid on testnet'); - } -}); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/sdk-init.test.mjs b/packages/wasm-sdk/test/sdk-init.test.mjs deleted file mode 100644 index b377d2df6d2..00000000000 --- a/packages/wasm-sdk/test/sdk-init.test.mjs +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env node -// sdk-init.test.mjs - Tests for SDK initialization and configuration - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Test utilities -const tests = []; -let currentSuite = ''; - -function describe(name, fn) { - currentSuite = name; - console.log(`\n${name}`); - fn(); -} - -function test(name, fn) { - tests.push({ suite: currentSuite, name, fn }); -} - -function expect(value) { - return { - toBe(expected) { - if (value !== expected) { - throw new Error(`Expected ${value} to be ${expected}`); - } - }, - toBeDefined() { - if (value === undefined) { - throw new Error(`Expected value to be defined`); - } - }, - toBeInstanceOf(expectedClass) { - if (!(value instanceof expectedClass)) { - throw new Error(`Expected value to be instance of ${expectedClass.name}`); - } - }, - toContain(substring) { - if (!value.includes(substring)) { - throw new Error(`Expected ${value} to contain ${substring}`); - } - }, - toThrow() { - try { - value(); - throw new Error('Expected function to throw'); - } catch (e) { - // Expected - } - } - }; -} - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Define tests -describe('SDK Initialization Tests', () => { - describe('WasmSdkBuilder', () => { - test('should create mainnet SDK', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_mainnet(); - expect(builder).toBeDefined(); - expect(builder).toBeInstanceOf(wasmSdk.WasmSdkBuilder); - - const sdk = await builder.build(); - expect(sdk).toBeDefined(); - expect(sdk).toBeInstanceOf(wasmSdk.WasmSdk); - - // Free resources - sdk.free(); - builder.free(); - }); - - test('should create mainnet trusted SDK', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_mainnet_trusted(); - expect(builder).toBeDefined(); - expect(builder).toBeInstanceOf(wasmSdk.WasmSdkBuilder); - - const sdk = await builder.build(); - expect(sdk).toBeDefined(); - expect(sdk).toBeInstanceOf(wasmSdk.WasmSdk); - - sdk.free(); - builder.free(); - }); - - test('should create testnet SDK', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - expect(builder).toBeDefined(); - expect(builder).toBeInstanceOf(wasmSdk.WasmSdkBuilder); - - const sdk = await builder.build(); - expect(sdk).toBeDefined(); - expect(sdk).toBeInstanceOf(wasmSdk.WasmSdk); - - sdk.free(); - builder.free(); - }); - - test('should create testnet trusted SDK', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); - expect(builder).toBeDefined(); - expect(builder).toBeInstanceOf(wasmSdk.WasmSdkBuilder); - - const sdk = await builder.build(); - expect(sdk).toBeDefined(); - expect(sdk).toBeInstanceOf(wasmSdk.WasmSdk); - - sdk.free(); - builder.free(); - }); - - test('should set custom settings', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - - // Test with custom settings - const settings = { - request_timeout_seconds: 10, - connect_timeout_seconds: 5, - retries: 3 - }; - - builder.with_settings(JSON.stringify(settings)); - - const sdk = await builder.build(); - expect(sdk).toBeDefined(); - - sdk.free(); - builder.free(); - }); - - test('should set specific version', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - - // Test with version 1 - builder.with_version(1); - - const sdk = await builder.build(); - expect(sdk).toBeDefined(); - - sdk.free(); - builder.free(); - }); - - test('should handle invalid settings gracefully', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - - expect(() => { - builder.with_settings('invalid json'); - }).toThrow(); - - builder.free(); - }); - }); - - describe('getLatestVersionNumber', () => { - test('should return latest version number', () => { - const version = wasmSdk.WasmSdkBuilder.getLatestVersionNumber(); - expect(version).toBeDefined(); - expect(typeof version).toBe('number'); - expect(version).toBe(1); - console.log(` Latest version: ${version}`); - }); - }); - - describe('SDK Methods Availability', () => { - test('should have all query functions available as top-level exports', async () => { - const builder = wasmSdk.WasmSdkBuilder.new_testnet(); - const sdk = await builder.build(); - - // Identity queries - expect(typeof wasmSdk.identity_fetch).toBe('function'); - expect(typeof wasmSdk.identity_fetch_unproved).toBe('function'); - expect(typeof wasmSdk.get_identity_keys).toBe('function'); - expect(typeof wasmSdk.get_identity_nonce).toBe('function'); - expect(typeof wasmSdk.get_identity_contract_nonce).toBe('function'); - expect(typeof wasmSdk.get_identity_balance).toBe('function'); - expect(typeof wasmSdk.get_identities_balances).toBe('function'); - expect(typeof wasmSdk.get_identity_balance_and_revision).toBe('function'); - expect(typeof wasmSdk.get_identity_by_public_key_hash).toBe('function'); - expect(typeof wasmSdk.get_identity_by_non_unique_public_key_hash).toBe('function'); - expect(typeof wasmSdk.get_identities_contract_keys).toBe('function'); - expect(typeof wasmSdk.get_identity_token_balances).toBe('function'); - - // Document queries - expect(typeof wasmSdk.get_documents).toBe('function'); - expect(typeof wasmSdk.get_document).toBe('function'); - - // Data contract queries - expect(typeof wasmSdk.data_contract_fetch).toBe('function'); - expect(typeof wasmSdk.get_data_contract_history).toBe('function'); - expect(typeof wasmSdk.get_data_contracts).toBe('function'); - - // Token queries - expect(typeof wasmSdk.get_identities_token_balances).toBe('function'); - expect(typeof wasmSdk.get_identity_token_infos).toBe('function'); - expect(typeof wasmSdk.get_identities_token_infos).toBe('function'); - expect(typeof wasmSdk.get_token_statuses).toBe('function'); - expect(typeof wasmSdk.get_token_direct_purchase_prices).toBe('function'); - expect(typeof wasmSdk.get_token_contract_info).toBe('function'); - expect(typeof wasmSdk.get_token_perpetual_distribution_last_claim).toBe('function'); - expect(typeof wasmSdk.get_token_total_supply).toBe('function'); - - // Epoch queries - expect(typeof wasmSdk.get_epochs_info).toBe('function'); - expect(typeof wasmSdk.get_finalized_epoch_infos).toBe('function'); - expect(typeof wasmSdk.get_current_epoch).toBe('function'); - expect(typeof wasmSdk.get_evonodes_proposed_epoch_blocks_by_ids).toBe('function'); - expect(typeof wasmSdk.get_evonodes_proposed_epoch_blocks_by_range).toBe('function'); - - // Protocol/System queries - expect(typeof wasmSdk.get_protocol_version_upgrade_state).toBe('function'); - expect(typeof wasmSdk.get_protocol_version_upgrade_vote_status).toBe('function'); - expect(typeof wasmSdk.get_status).toBe('function'); - expect(typeof wasmSdk.get_current_quorums_info).toBe('function'); - expect(typeof wasmSdk.get_total_credits_in_platform).toBe('function'); - expect(typeof wasmSdk.get_prefunded_specialized_balance).toBe('function'); - expect(typeof wasmSdk.get_path_elements).toBe('function'); - - // Voting/Contested resources - expect(typeof wasmSdk.get_contested_resources).toBe('function'); - expect(typeof wasmSdk.get_contested_resource_vote_state).toBe('function'); - expect(typeof wasmSdk.get_contested_resource_voters_for_identity).toBe('function'); - expect(typeof wasmSdk.get_contested_resource_identity_votes).toBe('function'); - expect(typeof wasmSdk.get_vote_polls_by_end_date).toBe('function'); - - // Group queries - expect(typeof wasmSdk.get_group_info).toBe('function'); - expect(typeof wasmSdk.get_group_infos).toBe('function'); - expect(typeof wasmSdk.get_group_members).toBe('function'); - expect(typeof wasmSdk.get_identity_groups).toBe('function'); - expect(typeof wasmSdk.get_group_actions).toBe('function'); - expect(typeof wasmSdk.get_group_action_signers).toBe('function'); - expect(typeof wasmSdk.get_groups_data_contracts).toBe('function'); - - // DPNS queries - expect(typeof wasmSdk.dpns_register_name).toBe('function'); - expect(typeof wasmSdk.dpns_is_name_available).toBe('function'); - expect(typeof wasmSdk.dpns_resolve_name).toBe('function'); - expect(typeof wasmSdk.get_dpns_username_by_name).toBe('function'); - expect(typeof wasmSdk.get_dpns_usernames).toBe('function'); - expect(typeof wasmSdk.get_dpns_username).toBe('function'); - - // State transitions - check if they're methods on the SDK instance - expect(typeof sdk.tokenMint).toBe('function'); - expect(typeof sdk.tokenBurn).toBe('function'); - expect(typeof sdk.tokenTransfer).toBe('function'); - expect(typeof sdk.tokenFreeze).toBe('function'); - expect(typeof sdk.tokenUnfreeze).toBe('function'); - expect(typeof sdk.tokenDestroyFrozen).toBe('function'); - expect(typeof sdk.contractCreate).toBe('function'); - expect(typeof sdk.contractUpdate).toBe('function'); - expect(typeof sdk.documentCreate).toBe('function'); - expect(typeof sdk.documentReplace).toBe('function'); - expect(typeof sdk.documentDelete).toBe('function'); - expect(typeof sdk.documentTransfer).toBe('function'); - expect(typeof sdk.documentPurchase).toBe('function'); - expect(typeof sdk.documentSetPrice).toBe('function'); - expect(typeof sdk.identityCreditTransfer).toBe('function'); - expect(typeof sdk.identityCreditWithdrawal).toBe('function'); - expect(typeof sdk.identityUpdate).toBe('function'); - expect(typeof sdk.masternodeVote).toBe('function'); - - // Verification functions - expect(typeof wasmSdk.verify_identity_response).toBe('function'); - expect(typeof wasmSdk.verify_data_contract).toBe('function'); - expect(typeof wasmSdk.verify_documents).toBe('function'); - - // Utility functions - expect(typeof wasmSdk.wait_for_state_transition_result).toBe('function'); - - // Key generation functions - expect(typeof wasmSdk.generate_mnemonic).toBe('function'); - expect(typeof wasmSdk.validate_mnemonic).toBe('function'); - expect(typeof wasmSdk.mnemonic_to_seed).toBe('function'); - expect(typeof wasmSdk.derive_key_from_seed_phrase).toBe('function'); - expect(typeof wasmSdk.derive_key_from_seed_with_path).toBe('function'); - expect(typeof wasmSdk.generate_key_pair).toBe('function'); - expect(typeof wasmSdk.generate_key_pairs).toBe('function'); - expect(typeof wasmSdk.key_pair_from_wif).toBe('function'); - expect(typeof wasmSdk.key_pair_from_hex).toBe('function'); - expect(typeof wasmSdk.pubkey_to_address).toBe('function'); - expect(typeof wasmSdk.validate_address).toBe('function'); - expect(typeof wasmSdk.sign_message).toBe('function'); - - // DPNS utility functions - expect(typeof wasmSdk.dpns_convert_to_homograph_safe).toBe('function'); - expect(typeof wasmSdk.dpns_is_valid_username).toBe('function'); - expect(typeof wasmSdk.dpns_is_contested_username).toBe('function'); - - // Clean up - if (sdk && sdk.__wbg_ptr) { - sdk.free(); - } - if (builder && builder.__wbg_ptr) { - builder.free(); - } - }); - }); -}); - -// Run tests -console.log('\nRunning tests...\n'); -let passed = 0; -let failed = 0; - -for (const { suite, name, fn } of tests) { - try { - await fn(); - console.log(` βœ… ${name}`); - passed++; - } catch (error) { - console.log(` ❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${tests.length} total`); -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/specialized-queries.test.mjs b/packages/wasm-sdk/test/specialized-queries.test.mjs deleted file mode 100644 index f572bf2934d..00000000000 --- a/packages/wasm-sdk/test/specialized-queries.test.mjs +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env node -// specialized-queries.test.mjs - Tests for masternode, group, and other specialized queries - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nSpecialized Query Tests\n'); - -// Initialize SDK - use trusted builder for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Masternode Queries -describe('Masternode Queries'); - -await test('get_masternode_status - requires valid proTxHash', async () => { - try { - // This would need a real masternode proTxHash - const result = await wasmSdk.get_masternode_status( - sdk, - "0000000000000000000000000000000000000000000000000000000000000000" // 32-byte hex - ); - throw new Error('Should have failed with invalid proTxHash'); - } catch (error) { - if (error.message.includes('Should have failed')) { - throw error; - } - console.log(' Expected error with invalid proTxHash'); - } -}); - -await test('get_masternode_score - requires valid proTxHash', async () => { - try { - const result = await wasmSdk.get_masternode_score( - sdk, - "0000000000000000000000000000000000000000000000000000000000000000", - 1 // quorum count - ); - throw new Error('Should have failed with invalid proTxHash'); - } catch (error) { - if (error.message.includes('Should have failed')) { - throw error; - } - console.log(' Expected error with invalid proTxHash'); - } -}); - -// Specialized Document Queries -describe('Specialized Document Queries'); - -await test('get_prefunded_specialized_balance - requires valid document ID', async () => { - try { - const result = await wasmSdk.get_prefunded_specialized_balance( - sdk, - "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", // contract ID - "invalidDocumentId" - ); - throw new Error('Should have failed with invalid document ID'); - } catch (error) { - if (error.message.includes('Should have failed')) { - throw error; - } - console.log(' Expected error with invalid document ID'); - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- Only testing functions that actually exist in WASM SDK'); -console.log('- Network errors are expected when running offline'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/state-transitions.test.mjs b/packages/wasm-sdk/test/state-transitions.test.mjs deleted file mode 100644 index 9366a73cba4..00000000000 --- a/packages/wasm-sdk/test/state-transitions.test.mjs +++ /dev/null @@ -1,621 +0,0 @@ -#!/usr/bin/env node -// state-transitions.test.mjs - Tests for all state transition functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, { generate_key_pair } from '../pkg/wasm_sdk.js'; -import * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nState Transition Tests\n'); - -// Initialize SDK - use trusted builder for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// Test values -const TEST_MNEMONIC = "during develop before curtain hazard rare job language become verb message travel"; -const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; -const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; -const TOKEN_CONTRACT = 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv'; - -// Identity State Transitions -describe('Identity State Transitions'); - -await test('identity_create - requires funding', async () => { - try { - // Would need funding transaction - const result = await wasmSdk.identity_create( - sdk, - TEST_MNEMONIC, - null, // no alias - 0 // key index - ); - throw new Error('Should fail without funding'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without funding'); - } -}); - -await test('identity_create with all SECP256K1 keys (common scenario)', async () => { - try { - // Generate unique keys for testing (1 asset lock + 3 identity keys) - const assetLockKey = generate_key_pair("testnet"); - const secp256k1Key1 = generate_key_pair("testnet"); - const secp256k1Key2 = generate_key_pair("testnet"); - const secp256k1Key3 = generate_key_pair("testnet"); - - // Mock asset lock proof - const mockAssetLockProof = JSON.stringify({ - coreChainLockedHeight: 1000000, - outPoint: "0000000000000000000000000000000000000000000000000000000000000000:0" - }); - - // Create public keys array with all SECP256K1 keys - const publicKeys = JSON.stringify([ - { - keyType: "ECDSA_SECP256K1", - purpose: "AUTHENTICATION", - securityLevel: "MASTER", - privateKeyHex: secp256k1Key1.private_key_hex - }, - { - keyType: "ECDSA_SECP256K1", - purpose: "AUTHENTICATION", - securityLevel: "CRITICAL", - privateKeyHex: secp256k1Key2.private_key_hex - }, - { - keyType: "ECDSA_SECP256K1", - purpose: "ENCRYPTION", - securityLevel: "HIGH", - privateKeyHex: secp256k1Key3.private_key_hex - } - ]); - - const result = await sdk.identityCreate( - mockAssetLockProof, - assetLockKey.private_key_wif, - publicKeys - ); - throw new Error('Should fail with mock data'); - } catch (error) { - const errorMessage = error.message || error.toString() || 'Unknown error'; - if (errorMessage.includes('Should fail')) { - throw error; - } - // Check that it's NOT a signature verification error - if (errorMessage.includes('signature failed verification')) { - throw new Error('SIGNATURE VERIFICATION ERROR - SimpleSigner may not be working correctly'); - } - console.log(' Expected error with mock data (not signature error)'); - } -}); - -await test('identity_create with mixed key types (SECP256K1 and HASH160)', async () => { - try { - // Generate unique keys for testing (1 asset lock + 2 SECP256K1 + 1 HASH160) - const assetLockKey = generate_key_pair("testnet"); - const secp256k1Key1 = generate_key_pair("testnet"); - const secp256k1Key2 = generate_key_pair("testnet"); - const hash160Key = generate_key_pair("testnet"); - - // Mock asset lock proof - const mockAssetLockProof = JSON.stringify({ - coreChainLockedHeight: 1000000, - outPoint: "0000000000000000000000000000000000000000000000000000000000000001:0" - }); - - // Create mixed public keys array - const publicKeys = JSON.stringify([ - { - keyType: "ECDSA_SECP256K1", - purpose: "AUTHENTICATION", - securityLevel: "MASTER", - privateKeyHex: secp256k1Key1.private_key_hex - }, - { - keyType: "ECDSA_SECP256K1", - purpose: "AUTHENTICATION", - securityLevel: "CRITICAL", - privateKeyHex: secp256k1Key2.private_key_hex - }, - { - keyType: "ECDSA_HASH160", - purpose: "TRANSFER", - securityLevel: "HIGH", - privateKeyHex: hash160Key.private_key_hex - } - ]); - - const result = await sdk.identityCreate( - mockAssetLockProof, - assetLockKey.private_key_wif, - publicKeys - ); - throw new Error('Should fail with mock data'); - } catch (error) { - const errorMessage = error.message || error.toString() || 'Unknown error'; - if (errorMessage.includes('Should fail')) { - throw error; - } - // Check that it's NOT a signature verification error - if (errorMessage.includes('signature failed verification')) { - throw new Error('SIGNATURE VERIFICATION ERROR - SimpleSigner may not be working correctly'); - } - console.log(' Expected error with mock data (not signature error)'); - } -}); - -await test('identity_create with only HASH160 keys', async () => { - try { - // Generate unique keys for testing (1 asset lock + 3 HASH160) - const assetLockKey = generate_key_pair("testnet"); - const hash160Key1 = generate_key_pair("testnet"); - const hash160Key2 = generate_key_pair("testnet"); - const hash160Key3 = generate_key_pair("testnet"); - - // Mock asset lock proof - const mockAssetLockProof = JSON.stringify({ - coreChainLockedHeight: 1000000, - outPoint: "0000000000000000000000000000000000000000000000000000000000000002:0" - }); - - // Create public keys array with all HASH160 keys - const publicKeys = JSON.stringify([ - { - keyType: "ECDSA_HASH160", - purpose: "AUTHENTICATION", - securityLevel: "MASTER", - privateKeyHex: hash160Key1.private_key_hex - }, - { - keyType: "ECDSA_HASH160", - purpose: "AUTHENTICATION", - securityLevel: "CRITICAL", - privateKeyHex: hash160Key2.private_key_hex - }, - { - keyType: "ECDSA_HASH160", - purpose: "TRANSFER", - securityLevel: "HIGH", - privateKeyHex: hash160Key3.private_key_hex - } - ]); - - const result = await sdk.identityCreate( - mockAssetLockProof, - assetLockKey.private_key_wif, - publicKeys - ); - throw new Error('Should fail with mock data'); - } catch (error) { - const errorMessage = error.message || error.toString() || 'Unknown error'; - if (errorMessage.includes('Should fail')) { - throw error; - } - // Check that it's NOT a signature verification error - if (errorMessage.includes('signature failed verification')) { - throw new Error('SIGNATURE VERIFICATION ERROR - SimpleSigner may not be working correctly'); - } - console.log(' Expected error with mock data (not signature error)'); - } -}); - -await test('identity_update - requires existing identity', async () => { - try { - const updateData = JSON.stringify({ - add: [{ - purpose: 0, // authentication - securityLevel: 0, - keyType: 0, // ECDSA - data: "invalidpublickey" - }] - }); - - const result = await wasmSdk.identity_update( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - updateData, - 0 // key index - ); - throw new Error('Should fail with invalid key data'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with invalid key data'); - } -}); - -await test('identity_topup - requires funding', async () => { - try { - const result = await wasmSdk.identity_topup( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - 100000 // amount - ); - throw new Error('Should fail without funding'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without funding'); - } -}); - -await test('identity_withdraw - requires balance', async () => { - try { - const result = await wasmSdk.identity_withdraw( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - "yMTzqaUcb7e4QLiPT5f5hqNjgCXQq65pLm", // destination address - 100000, // amount - 0 // key index - ); - throw new Error('Should fail without balance'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without balance'); - } -}); - -// Document State Transitions -describe('Document State Transitions'); - -await test('document_create - DPNS document', async () => { - try { - const documentData = JSON.stringify({ - label: "testname", - normalizedLabel: "testname", - normalizedParentDomainName: "dash", - preorderSalt: "preordersalt", - records: { - identity: TEST_IDENTITY - }, - subdomainRules: { - allowSubdomains: false - } - }); - - const result = await wasmSdk.document_create( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - DPNS_CONTRACT, - "domain", - documentData, - 0 // key index - ); - throw new Error('Should fail without proper preorder'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without proper preorder'); - } -}); - -await test('document_update - requires existing document', async () => { - try { - const updateData = JSON.stringify({ - records: { - identity: TEST_IDENTITY, - dashAddress: "yMTzqaUcb7e4QLiPT5f5hqNjgCXQq65pLm" - } - }); - - const result = await wasmSdk.document_update( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - DPNS_CONTRACT, - "domain", - "nonexistentdocumentid", - updateData, - 0 // key index - ); - throw new Error('Should fail with nonexistent document'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with nonexistent document'); - } -}); - -await test('document_delete - requires existing document', async () => { - try { - const result = await wasmSdk.document_delete( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - DPNS_CONTRACT, - "domain", - "nonexistentdocumentid", - 0 // key index - ); - throw new Error('Should fail with nonexistent document'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with nonexistent document'); - } -}); - -// Token State Transitions -describe('Token State Transitions'); - -await test('token_create - create new token', async () => { - try { - const result = await wasmSdk.token_create( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - "TESTToken", // name - "TEST", // symbol - 8, // decimals - 1000000000, // total supply - null, // no additional metadata - 0 // key index - ); - throw new Error('Should fail without sufficient balance'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without sufficient balance'); - } -}); - -await test('token_mint - requires token ownership', async () => { - try { - const result = await wasmSdk.token_mint( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - TOKEN_CONTRACT, - 100000, // amount to mint - TEST_IDENTITY, // recipient - 0 // key index - ); - throw new Error('Should fail without token ownership'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without token ownership'); - } -}); - -await test('token_burn - requires token balance', async () => { - try { - const result = await wasmSdk.token_burn( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - TOKEN_CONTRACT, - 100000, // amount to burn - 0 // key index - ); - throw new Error('Should fail without token balance'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without token balance'); - } -}); - -await test('token_transfer - requires token balance', async () => { - try { - const result = await wasmSdk.token_transfer( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, // sender - TOKEN_CONTRACT, - "3mFKtDYspCMd8YmXNTB3qzKmbY3Azf4Kx3x8e36V8Gho", // recipient identity - 100000, // amount - 0 // key index - ); - throw new Error('Should fail without token balance'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without token balance'); - } -}); - -await test('token_update_metadata - requires token ownership', async () => { - try { - const metadata = JSON.stringify({ - description: "Updated token description", - website: "https://example.com" - }); - - const result = await wasmSdk.token_update_metadata( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - TOKEN_CONTRACT, - metadata, - 0 // key index - ); - throw new Error('Should fail without token ownership'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without token ownership'); - } -}); - -// Data Contract State Transitions -describe('Data Contract State Transitions'); - -await test('data_contract_create - requires balance', async () => { - try { - const contractDefinition = JSON.stringify({ - testDocument: { - type: "object", - properties: { - name: { - type: "string" - } - }, - required: ["name"], - additionalProperties: false - } - }); - - const result = await wasmSdk.data_contract_create( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - contractDefinition, - 0 // key index - ); - throw new Error('Should fail without sufficient balance'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without sufficient balance'); - } -}); - -await test('data_contract_update - requires ownership', async () => { - try { - const updateDefinition = JSON.stringify({ - testDocument: { - type: "object", - properties: { - name: { - type: "string" - }, - description: { - type: "string" - } - }, - required: ["name"], - additionalProperties: false - } - }); - - const result = await wasmSdk.data_contract_update( - sdk, - TEST_MNEMONIC, - TEST_IDENTITY, - DPNS_CONTRACT, // trying to update DPNS contract - updateDefinition, - 0 // key index - ); - throw new Error('Should fail without ownership'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error without ownership'); - } -}); - -// Broadcast Functions -describe('Broadcast Functions'); - -await test('broadcast_raw_transition - requires valid transition', async () => { - try { - const result = await wasmSdk.broadcast_raw_transition( - sdk, - "invalidtransitionhex" - ); - throw new Error('Should fail with invalid transition'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with invalid transition'); - } -}); - -await test('wait_for_state_transition_result - requires valid hash', async () => { - try { - const result = await wasmSdk.wait_for_state_transition_result( - sdk, - "invalidtransitionhash", - true, // prove - 60000 // timeout - ); - throw new Error('Should fail with invalid hash'); - } catch (error) { - if (error.message.includes('Should fail')) { - throw error; - } - console.log(' Expected error with invalid hash'); - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- State transitions require funded identities and proper credentials'); -console.log('- Most will fail without network connectivity and actual funds'); -console.log('- Token operations require token ownership or balance'); -console.log('- Data contract operations require contract ownership'); -console.log('- identity_put is known to panic in WASM environment'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/system-utility-queries.test.mjs b/packages/wasm-sdk/test/system-utility-queries.test.mjs deleted file mode 100644 index eff9d9b59d0..00000000000 --- a/packages/wasm-sdk/test/system-utility-queries.test.mjs +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env node -// system-utility-queries.test.mjs - Tests for system and utility query functions - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test utilities -let passed = 0; -let failed = 0; - -async function test(name, fn) { - try { - await fn(); - console.log(`βœ… ${name}`); - passed++; - } catch (error) { - console.log(`❌ ${name}`); - console.log(` ${error.message}`); - failed++; - } -} - -function describe(name) { - console.log(`\n${name}`); -} - -console.log('\nSystem & Utility Query Tests\n'); - -// Initialize SDK - prefetch quorums for trusted mode -console.log('Prefetching trusted quorums...'); -try { - await wasmSdk.prefetch_trusted_quorums_testnet(); - console.log('Quorums prefetched successfully'); -} catch (error) { - console.log('Warning: Could not prefetch quorums:', error.message); -} - -// Use trusted builder as required for WASM -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted(); -const sdk = await builder.build(); - -// System Information Queries -describe('System Information Queries'); - -await test('get_current_quorums_info - fetch current quorum information', async () => { - try { - const result = await wasmSdk.get_current_quorums_info(sdk); - console.log(` Current quorums info retrieved`); - if (result) { - console.log(` Number of quorums: ${result.length || 0}`); - if (result.length > 0) { - const firstQuorum = result[0]; - console.log(` First quorum type: ${firstQuorum.quorumType || 'N/A'}`); - console.log(` Member count: ${firstQuorum.memberCount || 'N/A'}`); - } - } - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -await test('get_total_credits_in_platform - fetch total platform credits', async () => { - try { - const result = await wasmSdk.get_total_credits_in_platform(sdk); - console.log(` Total credits in platform: ${result}`); - } catch (error) { - if (error.message.includes('network') || error.message.includes('connection')) { - console.log(' Expected network error (offline)'); - } else { - throw error; - } - } -}); - -// Clean up -sdk.free(); - -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`); - -console.log('\nπŸ“ Notes:'); -console.log('- Current quorums info shows active masternode quorums'); -console.log('- Total credits represents all credits in the platform'); -console.log('- Network errors are expected when running offline'); - -process.exit(failed > 0 ? 1 : 0); \ No newline at end of file diff --git a/packages/wasm-sdk/test/test-dip14-vectors.mjs b/packages/wasm-sdk/test/test-dip14-vectors.mjs deleted file mode 100755 index cd0bd99d4fe..00000000000 --- a/packages/wasm-sdk/test/test-dip14-vectors.mjs +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env node - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; - -async function runTests() { - console.log('Testing DIP14 Test Vectors...\n'); - - // Initialize WASM - const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm'); - const wasmBuffer = readFileSync(wasmPath); - await init(wasmBuffer); - - // Test helper - let passed = 0; - let failed = 0; - - const testVector = async (name, path, expectedXprv, expectedXpub) => { - try { - const seed = "b16d3782e714da7c55a397d5f19104cfed7ffa8036ac514509bbb50807f8ac598eeb26f0797bd8cc221a6cbff2168d90a5e9ee025a5bd977977b9eccd97894bb"; - const mnemonic = "birth kingdom trash renew flavor utility donkey gasp regular alert pave layer"; - - console.log(`\n=== ${name} ===`); - console.log(`Path: ${path}`); - - const result = await wasmSdk.derive_key_from_seed_with_extended_path( - mnemonic, - null, - path, - 'testnet' - ); - - console.log('\nResult:'); - console.log(`xprv: ${result.xprv}`); - console.log(`xpub: ${result.xpub}`); - - console.log('\nExpected:'); - console.log(`xprv: ${expectedXprv}`); - console.log(`xpub: ${expectedXpub}`); - - const xprvMatch = result.xprv === expectedXprv; - const xpubMatch = result.xpub === expectedXpub; - - console.log('\nComparison:'); - console.log(`xprv Match: ${xprvMatch ? 'βœ…' : '❌'}`); - console.log(`xpub Match: ${xpubMatch ? 'βœ…' : '❌'}`); - - if (xprvMatch && xpubMatch) { - console.log(`βœ… ${name} PASSED`); - passed++; - } else { - console.log(`❌ ${name} FAILED`); - failed++; - } - - } catch (error) { - console.error(`❌ ${name} ERROR: ${error.message}`); - failed++; - } - }; - - // Test Vector 1: Non-hardened / Hardened path example - await testVector( - "Test Vector 1", - "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b/0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'/0x4c4592ca670c983fc43397dfd21a6f427fac9b4ac53cb4dcdc6522ec51e81e79/0", - "tprv8iNr6Z8PgAHmYSgMKGbq42kMVAAQmwmzm5iTJdUXoxLf25zG3GeRCvnEdC6HKTHkU59nZkfjvcGk9VW2YHsFQMwsZrQLyNrGx9c37kgb368", - "tpubDF4tEyAdpXySRui9CvGRTSQU4BgLwGxuLPKEb9WqEE93raF2ffU1PRQ6oJHCgZ7dArzcMj9iKG8s8EFA1DdwgzWAXs61uFuRE1bQi8kAmLy" - ); - - // Test Vector 2: Multiple hardened derivations with final non-hardened index - await testVector( - "Test Vector 2", - "m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0", - "tprv8p9LqE2tA2b94gc3ciRNA525WVkFvzkcC9qjpKEcGaTqjb9u2pwTXj41KkZTj3c1a6fJUpyXRfcB4dimsYsLMjQjsTJwi5Ukx6tJ5BpmYpx", - "tpubDLqNye58JQGox9dqWN5xZUgC5XGC6KwWmTSX6qGugrGEa5QffDm3iDfsVtX7qyXuWoQsXA6YCSuckKshyjnwiGGoYWHonAv2X98HTU613UH" - ); - - // Test Vector 3: Non-hardened derivation (note: these use DIP14 extended format) - await testVector( - "Test Vector 3", - "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b", - "dpts1vgMVEs9mmv1YLwURCeoTn9CFMZ8JMVhyZuxQSKttNSETR3zydMFHMKTTNDQPf6nnupCCtcNnSu3nKZXAJhaguyoJWD4Ju5PE6PSkBqAKWci7HLz37qmFmZZU6GMkLvNLtST2iV8NmqqbX37c45", - "dptp1C5gGd8NzvAke5WNKyRfpDRyvV2UZ3jjrZVZU77qk9yZemMGSdZpkWp7y6wt3FzvFxAHSW8VMCaC1p6Ny5EqWuRm2sjvZLUUFMMwXhmW6eS69qjX958RYBH5R8bUCGZkCfUyQ8UVWcx9katkrRr" - ); - - // Test Vector 4: Hardened path with complex indices - await testVector( - "Test Vector 4", - "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b/0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'", - "dpts1vwRsaPMQfqwp59ELpx5UeuYtdaMCJyGTwiGtr8zgf6qWPMWnhPpg8R73hwR1xLibbdKVdh17zfwMxFEMxZzBKUgPwvuosUGDKW4ayZjs3AQB9EGRcVpDoFT8V6nkcc6KzksmZxvmDcd3MqiPEu", - "dptp1CLkexeadp6guoi8Fbiwq6CLZm3hT1DJLwHsxWvwYSeAhjenFhcQ9HumZSftfZEr4dyQjFD7gkM5bSn6Aj7F1Jve8KTn4JsMEaj9dFyJkYs4Ga5HSUqeajxGVmzaY1pEioDmvUtZL3J1NCDCmzQ" - ); - - // Summary - console.log('\n\n=== Test Summary ==='); - console.log(`Passed: ${passed}`); - console.log(`Failed: ${failed}`); - console.log(`Total: ${passed + failed}`); - - console.log('\n\n=== Notes ==='); - console.log('- Test vectors 1 & 2 use standard BIP32 format (tprv/tpub)'); - console.log('- Test vectors 3 & 4 use DIP14 extended format (dpts/dptp)'); - console.log('- The DIP14 format is used when any index >= 2^32'); - console.log('- Our implementation currently only supports standard BIP32 serialization'); - - process.exit(failed > 0 ? 1 : 0); -} - -runTests().catch(console.error); \ No newline at end of file diff --git a/packages/wasm-sdk/test/test-plan.md b/packages/wasm-sdk/test/test-plan.md deleted file mode 100644 index dc07bbac021..00000000000 --- a/packages/wasm-sdk/test/test-plan.md +++ /dev/null @@ -1,163 +0,0 @@ -# WASM SDK Comprehensive Test Plan - -## Test Structure - -### 1. SDK Initialization Tests (`sdk-init.test.mjs`) -- WasmSdkBuilder.new_mainnet() -- WasmSdkBuilder.new_mainnet_trusted() -- WasmSdkBuilder.new_testnet() -- WasmSdkBuilder.new_testnet_trusted() -- with_version() -- with_settings() -- build() -- getLatestVersionNumber() - -### 2. Wallet/Key Generation Tests (`key-generation.test.mjs`) -- generate_mnemonic (12, 15, 18, 21, 24 words, multiple languages) -- validate_mnemonic -- mnemonic_to_seed -- derive_key_from_seed_phrase -- derive_key_from_seed_with_path -- derivation_path_bip44_mainnet/testnet -- derivation_path_dip9_mainnet/testnet -- derivation_path_dip13_mainnet/testnet -- derive_child_public_key -- xprv_to_xpub -- generate_key_pair -- generate_key_pairs -- key_pair_from_wif -- key_pair_from_hex -- pubkey_to_address -- validate_address -- sign_message - -### 3. DPNS Tests (`dpns.test.mjs`) -- dpns_convert_to_homograph_safe -- dpns_is_valid_username -- dpns_is_contested_username -- dpns_register_name -- dpns_is_name_available -- dpns_resolve_name -- get_dpns_username_by_name -- get_dpns_usernames -- get_dpns_username - -### 4. Identity Query Tests (`identity-queries.test.mjs`) -- identity_fetch -- identity_fetch_unproved -- get_identity_keys -- get_identity_nonce -- get_identity_contract_nonce -- get_identity_balance -- get_identities_balances -- get_identity_balance_and_revision -- get_identity_by_public_key_hash -- get_identity_by_non_unique_public_key_hash -- get_identities_contract_keys -- get_identity_token_balances - -### 5. Document Query Tests (`document-queries.test.mjs`) -- get_documents -- get_document - -### 6. Data Contract Query Tests (`contract-queries.test.mjs`) -- data_contract_fetch -- get_data_contract_history -- get_data_contracts - -### 7. Token Query Tests (`token-queries.test.mjs`) -- get_identities_token_balances -- get_identity_token_infos -- get_identities_token_infos -- get_token_statuses -- get_token_direct_purchase_prices -- get_token_contract_info -- get_token_perpetual_distribution_last_claim -- get_token_total_supply - -### 8. Epoch Query Tests (`epoch-queries.test.mjs`) -- get_epochs_info -- get_finalized_epoch_infos -- get_current_epoch -- get_evonodes_proposed_epoch_blocks_by_ids -- get_evonodes_proposed_epoch_blocks_by_range - -### 9. Protocol/System Query Tests (`system-queries.test.mjs`) -- get_protocol_version_upgrade_state -- get_protocol_version_upgrade_vote_status -- get_status -- get_current_quorums_info -- get_total_credits_in_platform -- get_prefunded_specialized_balance -- get_path_elements - -### 10. Voting/Contested Resource Tests (`voting-queries.test.mjs`) -- get_contested_resources -- get_contested_resource_vote_state -- get_contested_resource_voters_for_identity -- get_contested_resource_identity_votes -- get_vote_polls_by_end_date - -### 11. Group Query Tests (`group-queries.test.mjs`) -- get_group_info -- get_group_infos -- get_group_members -- get_identity_groups -- get_group_actions -- get_group_action_signers -- get_groups_data_contracts - -### 12. State Transition Tests (`state-transitions.test.mjs`) -- Token operations: - - tokenMint - - tokenBurn - - tokenTransfer - - tokenFreeze - - tokenUnfreeze - - tokenDestroyFrozen -- Contract operations: - - contractCreate - - contractUpdate -- Document operations: - - documentCreate - - documentReplace - - documentDelete - - documentTransfer - - documentPurchase - - documentSetPrice -- Identity operations: - - identityCreditTransfer - - identityCreditWithdrawal - - identityUpdate -- Voting: - - masternodeVote - -### 13. Verification Tests (`verification.test.mjs`) -- verify_identity_response -- verify_data_contract -- verify_documents - -### 14. Utility Tests (`utilities.test.mjs`) -- wait_for_state_transition_result -- prefetch_trusted_quorums_mainnet -- prefetch_trusted_quorums_testnet -- testSerialization - -## Test Categories - -### Expected to Pass -- All wallet/key generation functions -- Basic utility functions -- DPNS validation functions - -### Expected to Fail (Need SDK Connection) -- All query functions (require network connection) -- All state transition functions (require funded identities) -- Verification functions (require proof data) - -### Test Data Requirements -- Valid identity IDs for testnet -- Valid data contract IDs -- Test private keys -- Test document IDs -- Test token contract information \ No newline at end of file diff --git a/packages/wasm-sdk/test/test-setup.js b/packages/wasm-sdk/test/test-setup.js deleted file mode 100644 index 65530f5dadf..00000000000 --- a/packages/wasm-sdk/test/test-setup.js +++ /dev/null @@ -1,52 +0,0 @@ -// test-setup.js - Setup for running WASM SDK tests in Node.js -const fs = require('fs'); -const path = require('path'); - -// Polyfill for TextEncoder/TextDecoder if needed -if (typeof global.TextEncoder === 'undefined') { - const { TextEncoder, TextDecoder } = require('util'); - global.TextEncoder = TextEncoder; - global.TextDecoder = TextDecoder; -} - -// Polyfill for crypto.getRandomValues -if (typeof global.crypto === 'undefined') { - global.crypto = require('crypto').webcrypto; -} - -// Since the WASM SDK is built for web target, we need to provide browser-like globals -global.self = global; - -// Load WASM module using eval to bypass module restrictions -async function loadWasmModule() { - // Read the JS file and modify it for Node.js - const jsPath = path.join(__dirname, '../pkg/wasm_sdk.js'); - let jsContent = fs.readFileSync(jsPath, 'utf8'); - - // Replace import.meta.url with a file URL - const fileUrl = `file://${jsPath}`; - jsContent = jsContent.replace(/import\.meta\.url/g, `'${fileUrl}'`); - - // Create a module wrapper - const moduleWrapper = ` - const exports = {}; - const module = { exports }; - ${jsContent} - return module.exports; - `; - - // Evaluate the module - const wasmModule = eval(`(async function() { ${moduleWrapper} })()`); - const loadedModule = await wasmModule; - - // Read and initialize WASM - const wasmPath = path.join(__dirname, '../pkg/wasm_sdk_bg.wasm'); - const wasmBuffer = fs.readFileSync(wasmPath); - - // Initialize the module - await loadedModule.default(wasmBuffer); - - return loadedModule; -} - -module.exports = { loadWasmModule }; \ No newline at end of file diff --git a/packages/wasm-sdk/test/test-summary.md b/packages/wasm-sdk/test/test-summary.md deleted file mode 100644 index 7d339875fd2..00000000000 --- a/packages/wasm-sdk/test/test-summary.md +++ /dev/null @@ -1,88 +0,0 @@ -# WASM SDK Test Summary - -## Test Execution Results - -### SDK Initialization Tests (`sdk-init-simple.test.mjs`) -- **Total**: 10 tests -- **Passed**: 9 -- **Failed**: 1 -- **Key Findings**: - - SDK initialization works properly - - All query and state transition functions are available - - Address validation test failed (need to use proper test addresses) - -### Key Generation Tests (`key-generation.test.mjs`) -- **Total**: 53 tests -- **Passed**: 49 -- **Failed**: 4 -- **Key Findings**: - - Mnemonic generation works for all word counts and languages - - Key derivation with paths works correctly - - BIP44/DIP9 path helper functions return different structure than expected - - Child key derivation functions are not yet implemented (expected) - -### DPNS Tests (`dpns.test.mjs`) -- **Total**: 34 tests -- **Passed**: 31 -- **Failed**: 3 -- **Key Findings**: - - Basic DPNS validation functions work - - Network operations properly return or handle errors when offline - - Homograph conversion doesn't remove special characters - - Unicode homograph protection not implemented - - Username validation accepts uppercase (might be by design) - -## Categories Summary - -### βœ… Working Without Network -1. **SDK Initialization** - All builder patterns work -2. **Key Generation** - Complete BIP39/BIP32/BIP44 implementation -3. **DPNS Validation** - Username validation and contested name detection -4. **Utility Functions** - Address validation, message signing - -### ⚠️ Requires Network Connection -1. **Identity Queries** - All identity fetch operations -2. **Document Queries** - Document retrieval operations -3. **Data Contract Queries** - Contract fetching -4. **Token Queries** - Token balance and info queries -5. **State Transitions** - All write operations -6. **DPNS Network Operations** - Name registration, resolution - -### 🚧 Not Implemented -1. **Child Key Derivation** - `derive_child_public_key` -2. **Extended Key Conversion** - `xprv_to_xpub` - -### Utility Tests (`utilities-simple.test.mjs`) -- **Total**: 14 tests -- **Passed**: 13 -- **Failed**: 1 -- **Key Findings**: - - SDK version checking works - - Trusted quorum prefetch successfully connects to network - - testSerialization method exists but returns undefined - - Error handling and type validation work correctly - - Some test functions cause panics: identity_put, epoch_testing, start (when called twice) - -## Summary of Known Issues - -### πŸ› Bugs/Issues Found -1. **testSerialization returns undefined** - Method exists but doesn't return expected data -2. **Panic in identity_put** - Invalid secret key error -3. **Panic in epoch_testing** - Connection pool configuration error -4. **Panic in start function** - Can't be called twice (trace dispatcher already set) -5. **BIP44/DIP9 path helpers** - Return different structure than expected (missing 'path' property) -6. **DPNS homograph conversion** - Doesn't remove special characters or handle unicode homographs -7. **Address validation** - Need proper test addresses - -### βœ… Working Features -1. All key generation and derivation functions -2. DPNS validation functions (except homograph handling) -3. Network connectivity (trusted quorum prefetch works) -4. Error handling and type validation -5. SDK initialization and version checking - -## Next Steps -1. Create query function tests (expected to fail without network) -2. Create state transition tests (expected to fail without identity/network) -3. Create comprehensive test runner with all tests -4. Document expected failures and categorize by reason \ No newline at end of file diff --git a/packages/wasm-sdk/test/test-token-pricing-complete.html b/packages/wasm-sdk/test/test-token-pricing-complete.html deleted file mode 100644 index fdb77077be4..00000000000 --- a/packages/wasm-sdk/test/test-token-pricing-complete.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - Token Pricing Test - - - -

    Token Pricing Query Test

    - -
    -

    Test Calculate Token ID

    - - - -
    
    -    
    - -
    -

    Test Get Token Price by Contract

    - - - -
    
    -    
    - -
    -

    Test Multiple Token Prices

    - -
    
    -    
    - -
    
    -
    -    
    -
    -
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test/token-pricing.test.mjs b/packages/wasm-sdk/test/token-pricing.test.mjs
    deleted file mode 100755
    index 1d16ba2d565..00000000000
    --- a/packages/wasm-sdk/test/token-pricing.test.mjs
    +++ /dev/null
    @@ -1,122 +0,0 @@
    -#!/usr/bin/env node
    -// token-pricing.test.mjs - Tests for token pricing query functions
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from '../pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test utilities
    -let passed = 0;
    -let failed = 0;
    -
    -async function test(name, fn) {
    -    try {
    -        await fn();
    -        console.log(`βœ… ${name}`);
    -        passed++;
    -    } catch (error) {
    -        console.log(`❌ ${name}`);
    -        console.log(`   ${error.message}`);
    -        failed++;
    -    }
    -}
    -
    -function describe(name) {
    -    console.log(`\n${name}`);
    -}
    -
    -console.log('\nToken Pricing Query Tests\n');
    -
    -// Test values
    -const TOKEN_CONTRACT_2 = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy';
    -
    -// Initialize SDK - prefetch quorums for trusted mode
    -console.log('Prefetching trusted quorums...');
    -try {
    -    await wasmSdk.prefetch_trusted_quorums_testnet();
    -    console.log('Quorums prefetched successfully');
    -} catch (error) {
    -    console.log('Warning: Could not prefetch quorums:', error.message);
    -}
    -
    -// Use trusted builder as required for WASM
    -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted();
    -const sdk = await builder.build();
    -
    -// Token Pricing Tests
    -describe('Token Pricing Helper Functions');
    -
    -await test('calculate_token_id_from_contract - calculate token ID from contract and position', async () => {
    -    const contractId = TOKEN_CONTRACT_2;
    -    const position = 0;
    -    
    -    const tokenId = wasmSdk.calculate_token_id_from_contract(contractId, position);
    -    console.log(`   Contract: ${contractId}`);
    -    console.log(`   Position: ${position}`);
    -    console.log(`   Calculated Token ID: ${tokenId}`);
    -    
    -    // Token ID should be a valid base58 string
    -    if (!tokenId || typeof tokenId !== 'string' || tokenId.length === 0) {
    -        throw new Error('Invalid token ID returned');
    -    }
    -});
    -
    -await test('get_token_price_by_contract - fetch token price using contract ID and position', async () => {
    -    try {
    -        const contractId = TOKEN_CONTRACT_2;
    -        const position = 0;
    -        
    -        const priceInfo = await wasmSdk.get_token_price_by_contract(sdk, contractId, position);
    -        console.log(`   Contract: ${contractId}`);
    -        console.log(`   Position: ${position}`);
    -        console.log(`   Price Info: ${JSON.stringify(priceInfo)}`);
    -        
    -        // Verify response structure
    -        if (!priceInfo || !priceInfo.tokenId) {
    -            throw new Error('Invalid price info structure');
    -        }
    -        
    -        console.log(`   Token ID: ${priceInfo.tokenId}`);
    -        console.log(`   Current Price: ${priceInfo.currentPrice}`);
    -        console.log(`   Base Price: ${priceInfo.basePrice}`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else if (error.message.includes('No pricing schedule found') || error.message.includes('Token not found')) {
    -            console.log('   Token pricing not set or token does not exist');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -// Summary
    -console.log('\n=== Test Summary ===');
    -console.log(`Passed: ${passed}`);
    -console.log(`Failed: ${failed}`);
    -console.log(`Total: ${passed + failed}`);
    -
    -process.exit(failed > 0 ? 1 : 0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test/token-queries.test.mjs b/packages/wasm-sdk/test/token-queries.test.mjs
    deleted file mode 100644
    index f6c0f637a29..00000000000
    --- a/packages/wasm-sdk/test/token-queries.test.mjs
    +++ /dev/null
    @@ -1,173 +0,0 @@
    -#!/usr/bin/env node
    -// token-queries.test.mjs - Tests for token query functions
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from '../pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test utilities
    -let passed = 0;
    -let failed = 0;
    -
    -async function test(name, fn) {
    -    try {
    -        await fn();
    -        console.log(`βœ… ${name}`);
    -        passed++;
    -    } catch (error) {
    -        console.log(`❌ ${name}`);
    -        console.log(`   ${error.message}`);
    -        failed++;
    -    }
    -}
    -
    -function describe(name) {
    -    console.log(`\n${name}`);
    -}
    -
    -console.log('\nToken Query Tests\n');
    -
    -// Test values from docs.html
    -const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk';
    -const TOKEN_CONTRACT_1 = 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv';
    -const TOKEN_CONTRACT_2 = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy';
    -const TOKEN_CONTRACT_3 = 'EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta';
    -
    -// Initialize SDK - prefetch quorums for trusted mode
    -console.log('Prefetching trusted quorums...');
    -try {
    -    await wasmSdk.prefetch_trusted_quorums_testnet();
    -    console.log('Quorums prefetched successfully');
    -} catch (error) {
    -    console.log('Warning: Could not prefetch quorums:', error.message);
    -}
    -
    -// Use trusted builder as required for WASM
    -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted();
    -const sdk = await builder.build();
    -
    -// Token Query Tests
    -describe('Token Status and Info Queries');
    -
    -await test('get_token_statuses - fetch status for multiple tokens', async () => {
    -    try {
    -        const result = await wasmSdk.get_token_statuses(
    -            sdk,
    -            [TOKEN_CONTRACT_1, TOKEN_CONTRACT_2]
    -        );
    -        console.log(`   Token statuses: ${JSON.stringify(result)}`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_token_direct_purchase_prices - get token purchase prices', async () => {
    -    try {
    -        const result = await wasmSdk.get_token_direct_purchase_prices(
    -            sdk,
    -            [TOKEN_CONTRACT_2]
    -        );
    -        console.log(`   Token prices: ${JSON.stringify(result)}`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_token_contract_info - get token contract information', async () => {
    -    try {
    -        const result = await wasmSdk.get_token_contract_info(
    -            sdk,
    -            TOKEN_CONTRACT_3
    -        );
    -        console.log(`   Token contract info retrieved`);
    -        if (result) {
    -            console.log(`   Token name: ${result.name || 'N/A'}`);
    -            console.log(`   Token symbol: ${result.symbol || 'N/A'}`);
    -        }
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -describe('Token Supply and Distribution Queries');
    -
    -await test('get_token_perpetual_distribution_last_claim - get last claim info', async () => {
    -    try {
    -        const result = await wasmSdk.get_token_perpetual_distribution_last_claim(
    -            sdk,
    -            TEST_IDENTITY,
    -            TOKEN_CONTRACT_3
    -        );
    -        console.log(`   Last claim info: ${JSON.stringify(result)}`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_token_total_supply - get token total supply', async () => {
    -    try {
    -        const result = await wasmSdk.get_token_total_supply(
    -            sdk,
    -            TOKEN_CONTRACT_1
    -        );
    -        console.log(`   Total supply: ${result}`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -// Clean up
    -sdk.free();
    -
    -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`);
    -
    -console.log('\nπŸ“ Notes:');
    -console.log('- Token contracts on testnet may have limited activity');
    -console.log('- Token statuses track issuance and burning states');
    -console.log('- Direct purchase prices are for tokens that can be bought directly');
    -console.log('- Network errors are expected when running offline');
    -
    -process.exit(failed > 0 ? 1 : 0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test/token-transitions.test.mjs b/packages/wasm-sdk/test/token-transitions.test.mjs
    deleted file mode 100644
    index 4d6d51ceaf9..00000000000
    --- a/packages/wasm-sdk/test/token-transitions.test.mjs
    +++ /dev/null
    @@ -1,354 +0,0 @@
    -#!/usr/bin/env node
    -// token-transitions.test.mjs - Tests for new token state transition functions
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from '../pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test utilities
    -let passed = 0;
    -let failed = 0;
    -
    -async function test(name, fn) {
    -    try {
    -        await fn();
    -        console.log(`βœ… ${name}`);
    -        passed++;
    -    } catch (error) {
    -        console.log(`❌ ${name}`);
    -        console.log(`   ${error.message}`);
    -        failed++;
    -    }
    -}
    -
    -function describe(name) {
    -    console.log(`\n${name}`);
    -}
    -
    -console.log('\nToken State Transition Tests\n');
    -
    -// Initialize SDK - use trusted builder for WASM
    -console.log('Prefetching trusted quorums...');
    -try {
    -    await wasmSdk.prefetch_trusted_quorums_testnet();
    -    console.log('Quorums prefetched successfully');
    -} catch (error) {
    -    console.log('Warning: Could not prefetch quorums:', error.message);
    -}
    -
    -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted();
    -const sdk = await builder.build();
    -
    -// Test values
    -const TEST_CONTRACT_ID = 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv';
    -const TEST_TOKEN_POSITION = 0;
    -const TEST_IDENTITY_ID = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk';
    -const TEST_RECIPIENT_ID = '3mFKtDYspCMd8YmXNTB3qzKmbY3Azf4Kx3x8e36V8Gho';
    -const TEST_PRIVATE_KEY = 'KycRvJNvCVapwvvpRLWz76qXFAbXFfAqhG9FouVjUmDVZ6UtZfGa'; // Dummy key for testing
    -
    -// Token Transfer Tests
    -describe('Token Transfer State Transition');
    -
    -await test('tokenTransfer - should validate parameters', async () => {
    -    try {
    -        // Test with invalid contract ID
    -        await sdk.tokenTransfer(
    -            'invalid-contract-id',
    -            TEST_TOKEN_POSITION,
    -            '1000',
    -            TEST_IDENTITY_ID,
    -            TEST_RECIPIENT_ID,
    -            TEST_PRIVATE_KEY,
    -            'Test transfer'
    -        );
    -        throw new Error('Should fail with invalid contract ID');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid contract ID');
    -    }
    -});
    -
    -await test('tokenTransfer - should validate amount', async () => {
    -    try {
    -        // Test with invalid amount
    -        await sdk.tokenTransfer(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            'invalid-amount',
    -            TEST_IDENTITY_ID,
    -            TEST_RECIPIENT_ID,
    -            TEST_PRIVATE_KEY,
    -            null
    -        );
    -        throw new Error('Should fail with invalid amount');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid amount');
    -    }
    -});
    -
    -await test('tokenTransfer - should require valid identity', async () => {
    -    try {
    -        // This will fail because the identity doesn't exist or we don't have the right key
    -        await sdk.tokenTransfer(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            '1000',
    -            TEST_IDENTITY_ID,
    -            TEST_RECIPIENT_ID,
    -            TEST_PRIVATE_KEY,
    -            'Test transfer'
    -        );
    -        throw new Error('Should fail without valid identity/key');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error without valid identity/key');
    -    }
    -});
    -
    -// Token Freeze Tests
    -describe('Token Freeze State Transition');
    -
    -await test('tokenFreeze - should validate parameters', async () => {
    -    try {
    -        // Test with invalid contract ID
    -        await sdk.tokenFreeze(
    -            'invalid-contract-id',
    -            TEST_TOKEN_POSITION,
    -            TEST_RECIPIENT_ID,
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            'Freezing tokens'
    -        );
    -        throw new Error('Should fail with invalid contract ID');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid contract ID');
    -    }
    -});
    -
    -await test('tokenFreeze - should validate identity to freeze', async () => {
    -    try {
    -        // Test with invalid identity ID
    -        await sdk.tokenFreeze(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            'invalid-identity',
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            null
    -        );
    -        throw new Error('Should fail with invalid identity to freeze');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid identity to freeze');
    -    }
    -});
    -
    -await test('tokenFreeze - should require freezer permissions', async () => {
    -    try {
    -        // This will fail because the identity doesn't have freeze permissions
    -        await sdk.tokenFreeze(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            TEST_RECIPIENT_ID,
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            'Test freeze'
    -        );
    -        throw new Error('Should fail without freeze permissions');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error without freeze permissions');
    -    }
    -});
    -
    -// Token Unfreeze Tests
    -describe('Token Unfreeze State Transition');
    -
    -await test('tokenUnfreeze - should validate parameters', async () => {
    -    try {
    -        // Test with invalid contract ID
    -        await sdk.tokenUnfreeze(
    -            'invalid-contract-id',
    -            TEST_TOKEN_POSITION,
    -            TEST_RECIPIENT_ID,
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            'Unfreezing tokens'
    -        );
    -        throw new Error('Should fail with invalid contract ID');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid contract ID');
    -    }
    -});
    -
    -await test('tokenUnfreeze - should validate identity to unfreeze', async () => {
    -    try {
    -        // Test with invalid identity ID
    -        await sdk.tokenUnfreeze(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            'invalid-identity',
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            null
    -        );
    -        throw new Error('Should fail with invalid identity to unfreeze');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid identity to unfreeze');
    -    }
    -});
    -
    -await test('tokenUnfreeze - should require unfreezer permissions', async () => {
    -    try {
    -        // This will fail because the identity doesn't have unfreeze permissions
    -        await sdk.tokenUnfreeze(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            TEST_RECIPIENT_ID,
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            'Test unfreeze'
    -        );
    -        throw new Error('Should fail without unfreeze permissions');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error without unfreeze permissions');
    -    }
    -});
    -
    -// Token Destroy Frozen Tests
    -describe('Token Destroy Frozen State Transition');
    -
    -await test('tokenDestroyFrozen - should validate parameters', async () => {
    -    try {
    -        // Test with invalid contract ID
    -        await sdk.tokenDestroyFrozen(
    -            'invalid-contract-id',
    -            TEST_TOKEN_POSITION,
    -            TEST_RECIPIENT_ID,
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            'Destroying frozen tokens'
    -        );
    -        throw new Error('Should fail with invalid contract ID');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid contract ID');
    -    }
    -});
    -
    -await test('tokenDestroyFrozen - should validate identity', async () => {
    -    try {
    -        // Test with invalid identity ID
    -        await sdk.tokenDestroyFrozen(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            'invalid-identity',
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            null
    -        );
    -        throw new Error('Should fail with invalid identity');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error with invalid identity');
    -    }
    -});
    -
    -await test('tokenDestroyFrozen - should require destroyer permissions', async () => {
    -    try {
    -        // This will fail because the identity doesn't have destroy permissions
    -        await sdk.tokenDestroyFrozen(
    -            TEST_CONTRACT_ID,
    -            TEST_TOKEN_POSITION,
    -            TEST_RECIPIENT_ID,
    -            TEST_IDENTITY_ID,
    -            TEST_PRIVATE_KEY,
    -            'Test destroy frozen'
    -        );
    -        throw new Error('Should fail without destroy permissions');
    -    } catch (error) {
    -        if (error && error.message && error.message.includes('Should fail')) {
    -            throw error;
    -        }
    -        console.log('   Expected error without destroy permissions');
    -    }
    -});
    -
    -// Method Availability Tests
    -describe('Token Transition Methods Availability');
    -
    -await test('All new token transition methods should be available on SDK', async () => {
    -    if (typeof sdk.tokenTransfer !== 'function') {
    -        throw new Error('tokenTransfer method not found on SDK instance');
    -    }
    -    if (typeof sdk.tokenFreeze !== 'function') {
    -        throw new Error('tokenFreeze method not found on SDK instance');
    -    }
    -    if (typeof sdk.tokenUnfreeze !== 'function') {
    -        throw new Error('tokenUnfreeze method not found on SDK instance');
    -    }
    -    if (typeof sdk.tokenDestroyFrozen !== 'function') {
    -        throw new Error('tokenDestroyFrozen method not found on SDK instance');
    -    }
    -    console.log('   All token transition methods are available');
    -});
    -
    -// Summary
    -console.log('\n=== Test Summary ===');
    -console.log(`Passed: ${passed}`);
    -console.log(`Failed: ${failed}`);
    -console.log('\nNote: Most tests are expected to fail with permission/identity errors');
    -console.log('This is normal as we are testing parameter validation without real funded identities.');
    -console.log('The important thing is that the methods are available and validate parameters correctly.\n');
    -
    -process.exit(failed > 0 ? 1 : 0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test/ui-automation/.env.example b/packages/wasm-sdk/test/ui-automation/.env.example
    deleted file mode 100644
    index ee0b0a1ea4e..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/.env.example
    +++ /dev/null
    @@ -1,12 +0,0 @@
    -# Test Credentials for WASM SDK UI Tests
    -# Copy this file to .env and fill with real values
    -
    -# Private keys for state transitions (DON'T STORE in production)
    -TEST_PRIVATE_KEY_IDENTITY_1=YOUR_IDENTITY_PRIVATE_KEY_HERE
    -TEST_PRIVATE_KEY_TRANSFER=YOUR_TRANSFER_PRIVATE_KEY_HERE
    -TEST_PRIVATE_KEY_CONTRACT=YOUR_CONTRACT_PRIVATE_KEY_HERE
    -# Secondary private key (used by some document/token transitions)
    -TEST_PRIVATE_KEY_SECONDARY=YOUR_TEST_PRIVATE_KEY_SECONDARY
    -
    -# Seed phrases for identity creation (not implemented yet)
    -TEST_SEED_PHRASE_1="your seed phrase here"
    diff --git a/packages/wasm-sdk/test/ui-automation/README.md b/packages/wasm-sdk/test/ui-automation/README.md
    deleted file mode 100644
    index e69ac93ce35..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/README.md
    +++ /dev/null
    @@ -1,257 +0,0 @@
    -# WASM SDK UI Automation Tests
    -
    -Automated testing suite for the WASM SDK web interface (`index.html`) using Playwright.
    -
    -## Features
    -
    -- **Cross-browser testing** (currently configured for Chromium, easily extensible)
    -- **Automated parameter injection** from existing test data
    -- **Page Object Model** for maintainable test code
    -- **Network switching** (testnet/mainnet) testing
    -- **Error handling** and validation testing
    -- **Comprehensive reporting** with screenshots and videos on failure
    -- **GitHub Actions integration** for automated testing
    -
    -## Quick Start
    -
    -### Prerequisites
    -
    -- Node.js 18+ installed
    -- Python 3 for serving the web interface
    -- Linux environment (Ubuntu/Debian recommended)
    -
    -### Installation
    -
    -```bash
    -cd /path/to/wasm-sdk/test/ui-automation
    -npm install
    -npx playwright install chromium
    -```
    -
    -### Running Tests
    -
    -The easiest way to run tests is using the provided shell script:
    -
    -```bash
    -# From any directory, using the test runner script:
    -./run-ui-tests.sh smoke          # Basic functionality tests
    -./run-ui-tests.sh queries        # Query execution tests  
    -./run-ui-tests.sh parameterized  # Comprehensive parameter testing
    -./run-ui-tests.sh all           # Run all tests (default)
    -
    -# Run in headed mode (see browser)
    -./run-ui-tests.sh headed
    -
    -# Debug mode with detailed output
    -DEBUG=true ./run-ui-tests.sh smoke
    -
    -# Pattern matching for specific tests
    -./run-ui-tests.sh --grep="should initialize SDK"
    -```
    -
    -**Alternative: Direct npm commands** (from ui-automation directory):
    -
    -```bash
    -npm test                    # Run all tests
    -npm run test:smoke          # Basic functionality tests
    -npm run test:queries        # Query execution tests
    -npm run test:parameterized  # Comprehensive parameter testing
    -npm run test:headed         # Run in headed mode
    -npm run test:debug          # Debug mode
    -npm run test:report         # View HTML report
    -```
    -
    -## Test Structure
    -
    -### Test Categories
    -
    -1. **Basic Smoke Tests** (`basic-smoke.spec.js`)
    -   - SDK initialization
    -   - UI component visibility
    -   - Network switching
    -   - Basic interaction flows
    -
    -2. **Query Execution Tests** (`query-execution.spec.js`)
    -   - Identity queries (getIdentity, getIdentityBalance, getIdentityKeys, etc.)
    -   - Data contract queries (getDataContract, getDataContracts, getDataContractHistory)
    -   - Document queries (getDocuments, getDocument)
    -   - System queries (getStatus, getCurrentEpoch, getTotalCreditsInPlatform)
    -   - Error handling scenarios
    -   - Proof support testing with automatic fallback
    -
    -3. **Parameterized Tests** (`parameterized-queries.spec.js`)
    -   - Multiple parameter sets per query type
    -   - Cross-network testing scenarios
    -   - Parameter validation testing
    -
    -### Architecture
    -
    -```text
    -ui-automation/
    -β”œβ”€β”€ tests/                  # Test specification files
    -β”‚   β”œβ”€β”€ basic-smoke.spec.js        # Basic functionality tests
    -β”‚   β”œβ”€β”€ query-execution.spec.js    # Comprehensive query testing
    -β”‚   └── parameterized-queries.spec.js # Multi-parameter testing
    -β”œβ”€β”€ utils/                  # Test utilities and page objects
    -β”‚   β”œβ”€β”€ base-test.js       # Base test functionality
    -β”‚   β”œβ”€β”€ wasm-sdk-page.js   # Page Object Model for index.html
    -β”‚   └── parameter-injector.js # Parameter injection system
    -β”œβ”€β”€ fixtures/              # Test data and fixtures
    -β”‚   └── test-data.js       # Centralized test parameters
    -β”œβ”€β”€ playwright.config.js   # Playwright configuration
    -β”œβ”€β”€ run-ui-tests.sh        # Comprehensive test runner script
    -└── package.json           # npm scripts and dependencies
    -```
    -
    -## Configuration
    -
    -### Playwright Configuration
    -
    -The `playwright.config.js` file is configured for:
    -
    -- **Base URL**: `http://localhost:8888` (server managed via Playwright config's `webServer`)
    -- **Browsers**: Chromium (headless by default)
    -- **Timeouts**: 30s for actions, 120s for tests
    -- **Reporters**: HTML, JSON, and console output
    -- **Screenshots/Videos**: On failure only
    -
    -### Test Data
    -
    -Test parameters are centralized in `fixtures/test-data.js` and include:
    -
    -- Known testnet identity IDs
    -- Data contract IDs (DPNS, DashPay, etc.)
    -- Document IDs and examples
    -- Token IDs for testing
    -- Parameter sets for each query type
    -
    -## Usage Examples
    -
    -### Running Specific Tests
    -
    -```bash
    -# Run only identity query tests
    -npx playwright test --grep "Identity Queries"
    -
    -# Run tests for a specific query
    -npx playwright test --grep "getIdentity"
    -
    -# Run tests on headed browser for debugging
    -npx playwright test --headed --grep "smoke"
    -```
    -
    -### Adding New Tests
    -
    -1. **Add test data** to `fixtures/test-data.js`
    -2. **Create test file** in `tests/` directory
    -3. **Use page object** for UI interactions
    -4. **Use parameter injector** for form filling
    -
    -Example:
    -
    -```javascript
    -const { test, expect } = require('@playwright/test');
    -const { WasmSdkPage } = require('../utils/wasm-sdk-page');
    -const { ParameterInjector } = require('../utils/parameter-injector');
    -
    -test('should execute my new query', async ({ page }) => {
    -  const wasmSdkPage = new WasmSdkPage(page);
    -  const parameterInjector = new ParameterInjector(wasmSdkPage);
    -  
    -  await wasmSdkPage.initialize('testnet');
    -  await wasmSdkPage.setupQuery('myCategory', 'myQueryType');
    -  
    -  const success = await parameterInjector.injectParameters('myCategory', 'myQueryType');
    -  expect(success).toBe(true);
    -  
    -  const result = await wasmSdkPage.executeQueryAndGetResult();
    -  expect(result.success || result.hasError).toBe(true);
    -});
    -```
    -
    -## CI/CD Integration
    -
    -### GitHub Actions Integration
    -
    -Tests run automatically in CI or can be triggered manually with different configurations:
    -
    -- Automatic execution after WASM SDK builds
    -- Manual execution with configurable test types and browsers
    -- Comprehensive reporting with HTML reports and test artifacts
    -
    -### Local CI Testing
    -
    -For continuous integration, use the test runner script which handles all setup automatically:
    -
    -```bash
    -# In CI environment - the script handles all prerequisites
    -./run-ui-tests.sh smoke     # Quick smoke tests for PR validation
    -./run-ui-tests.sh all       # Full test suite for releases
    -
    -# CI-friendly JSON output
    -DEBUG=false ./run-ui-tests.sh all
    -
    -# Results available in:
    -# - playwright-report/ (HTML)
    -# - test-results.json (JSON)
    -# - test-results/ (screenshots, videos)
    -```
    -
    -**Manual CI setup** (if not using the script):
    -
    -```bash
    -# Install system dependencies
    -sudo npx playwright install-deps
    -npx playwright install chromium
    -
    -# Run tests with CI-friendly output  
    -npm run test:ci
    -
    -# Results will be in test-results/ directory
    -```
    -
    -## Troubleshooting
    -
    -### Common Issues
    -
    -1. **Dependencies missing**: Run `sudo npx playwright install-deps`
    -2. **Port 8888 in use**: Playwright config's `webServer` starts the server automatically; ensure the port is free or update the config
    -3. **WASM build issues**: The script rebuilds WASM if needed
    -4. **Test timeouts**: Use `DEBUG=true ./run-ui-tests.sh` for details
    -
    -### Debug Mode
    -
    -```bash
    -# See detailed execution logs
    -DEBUG=true ./run-ui-tests.sh smoke
    -
    -# Run with visible browser
    -./run-ui-tests.sh headed
    -
    -# Interactive debugging
    -./run-ui-tests.sh debug
    -```
    -
    -## Extending Tests
    -
    -To add new tests:
    -
    -1. Add test data to `fixtures/test-data.js`
    -2. Create test cases using the page object model
    -3. Use `parameter-injector.js` for form filling
    -
    -## Known Issues
    -
    -- Some queries don't yet support proof information in the WASM SDK
    -- Tests automatically skip proof testing for unsupported queries
    -- All core functionality works correctly
    -
    -## Support
    -
    -For issues or questions:
    -
    -1. Use `DEBUG=true ./run-ui-tests.sh` to get detailed execution information
    -2. Check the HTML reports in `playwright-report/` for visual debugging
    -3. Review the implementation summary in `IMPLEMENTATION_SUMMARY.md`
    -4. Examine test screenshots and videos in `test-results/` for failed tests
    -5. Check GitHub Actions workflow runs for CI/CD issues
    diff --git a/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js b/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js
    deleted file mode 100644
    index 7ee00bd15c6..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/fixtures/test-data.js
    +++ /dev/null
    @@ -1,884 +0,0 @@
    -/**
    - * Test data extracted from existing WASM SDK test parameters
    - * Based on update_inputs.py and existing test files
    - */
    -
    -// Load environment variables for sensitive test data
    -require('dotenv').config({ path: require('path').join(__dirname, '../.env') });
    -
    -const testData = {
    -  // Known testnet identity IDs for testing (from WASM SDK docs and tests)
    -  identityIds: {
    -    testnet: [
    -      "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",  // Used in docs.html and multiple test files
    -      "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX"   // Used in docs.html
    -    ],
    -    mainnet: [
    -      // Add mainnet IDs when available
    -    ]
    -  },
    -
    -  // Data contract IDs (from WASM SDK files and update_inputs.py)
    -  dataContracts: {
    -    testnet: {
    -      dpns: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",  // Used in index.html as DPNS_CONTRACT_ID
    -      dashpay: "ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A",
    -      sample: "HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM",
    -      tokenPricing: "H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy",  // Used in test-token-pricing-complete.html
    -      tokenContract: "EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta", // Used in update_inputs.py
    -      postCreate: "9nzpvjVSStUrhkEs3eNHw2JYpcNoLh1MjmqW45QiyjSa"      // Used in test_post_create.html
    -    },
    -    mainnet: {
    -      // Add mainnet contract IDs when available
    -    }
    -  },
    -
    -  // Public key hashes for testing
    -  publicKeyHashes: {
    -    testnet: [
    -      "b7e904ce25ed97594e72f7af0e66f298031c1754",
    -      "518038dc858461bcee90478fd994bba8057b7531"
    -    ]
    -  },
    -
    -  // Token IDs for testing
    -  tokenIds: {
    -    testnet: [
    -      "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv",
    -      "HEv1AYWQfwCffXQgmuzmzyzUo9untRTmVr67n4e4PSWa", // Used in docs.html (last claim)
    -      "4tyvbA2ZGFLvjXLnJRCacSoMbFfpmBwGRrAZsVwnfYri", // Identity 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk frozen
    -    ]
    -  },
    -
    -  // ProTx hashes for epoch testing
    -  proTxHashes: {
    -    testnet: [
    -      "143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113"
    -    ]
    -  },
    -
    -  // Document IDs
    -  documentIds: {
    -    testnet: {
    -      dpnsDomain: "7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r"
    -    }
    -  },
    -
    -  // Specialized balance IDs
    -  specializedBalanceIds: {
    -    testnet: [
    -      "AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT"
    -    ]
    -  },
    -
    -  // Query test parameters organized by category
    -  queryParameters: {
    -    identity: {
    -      getIdentity: {
    -        testnet: [
    -          { id: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk" },
    -          { id: "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX" }
    -        ]
    -      },
    -      getIdentityKeys: {
    -        testnet: [
    -          { 
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            keyRequestType: "all"
    -          },
    -          {
    -            identityId: "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX",
    -            keyRequestType: "specific",
    -            specificKeyIds: ["1", "2"]
    -          }
    -        ]
    -      },
    -      getIdentityBalance: {
    -        testnet: [
    -          { id: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk" }
    -        ]
    -      },
    -      getIdentityByPublicKeyHash: {
    -        testnet: [
    -          { publicKeyHash: "b7e904ce25ed97594e72f7af0e66f298031c1754" }
    -        ]
    -      },
    -      getIdentitiesContractKeys: {
    -        testnet: [
    -          {
    -            identitiesIds: [
    -              "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -              "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX"
    -            ],
    -            contractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -            purposes: ["0", "3"] // Authentication and Transfer
    -          }
    -        ]
    -      },
    -      getIdentityNonce: {
    -        testnet: [
    -          { identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk" }
    -        ]
    -      },
    -      getIdentityContractNonce: {
    -        testnet: [
    -          { 
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            contractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec"
    -          }
    -        ]
    -      },
    -      getIdentitiesBalances: {
    -        testnet: [
    -          {
    -            identityIds: [
    -              "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -              "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX"
    -            ]
    -          }
    -        ]
    -      },
    -      getIdentityBalanceAndRevision: {
    -        testnet: [
    -          { id: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk" }
    -        ]
    -      },
    -      getIdentityByNonUniquePublicKeyHash: {
    -        testnet: [
    -          { publicKeyHash: "518038dc858461bcee90478fd994bba8057b7531" }
    -        ]
    -      },
    -      getIdentityTokenBalances: {
    -        testnet: [
    -          {
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            tokenIds: [
    -              "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv",
    -              "HEv1AYWQfwCffXQgmuzmzyzUo9untRTmVr67n4e4PSWa"
    -            ]
    -          }
    -        ]
    -      },
    -      getIdentitiesTokenBalances: {
    -        testnet: [
    -          {
    -            identityIds: [
    -              "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -              "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX"
    -            ],
    -            tokenId: "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv"
    -          }
    -        ]
    -      },
    -      getIdentityTokenInfos: {
    -        testnet: [
    -          {
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            tokenIds: [
    -              "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv",
    -              "4tyvbA2ZGFLvjXLnJRCacSoMbFfpmBwGRrAZsVwnfYri"
    -            ]
    -          }
    -        ]
    -      },
    -      getIdentitiesTokenInfos: {
    -        testnet: [
    -          {
    -            identityIds: [
    -              "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -              "5RG84o6KsTaZudDqS8ytbaRB8QP4YYQ2uwzb6Hj8cfjX"
    -            ],
    -            tokenId: "4tyvbA2ZGFLvjXLnJRCacSoMbFfpmBwGRrAZsVwnfYri"
    -          }
    -        ]
    -      }
    -    },
    -
    -    dataContract: {
    -      getDataContract: {
    -        testnet: [
    -          { id: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" },
    -          { id: "ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A" }
    -        ]
    -      },
    -      getDataContracts: {
    -        testnet: [
    -          { 
    -            ids: [
    -              "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -              "ALybvzfcCwMs7sinDwmtumw17NneuW7RgFtFHgjKmF3A"
    -            ]
    -          }
    -        ]
    -      },
    -      getDataContractHistory: {
    -        testnet: [
    -          { 
    -            id: "HLY575cNazmc5824FxqaEMEBuzFeE4a98GDRNKbyJqCM",
    -            limit: 10,
    -            offset: 0
    -          }
    -        ]
    -      }
    -    },
    -
    -    document: {
    -      getDocuments: {
    -        testnet: [
    -          {
    -            dataContractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -            documentType: "domain",
    -            limit: 10,
    -            where: '[["normalizedParentDomainName", "==", "dash"], ["normalizedLabel", "startsWith", "test"]]',
    -            orderBy: '[["normalizedLabel", "asc"]]'
    -          }
    -        ]
    -      },
    -      getDocument: {
    -        testnet: [
    -          {
    -            dataContractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -            documentType: "domain",
    -            documentId: "7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r"
    -          }
    -        ]
    -      }
    -    },
    -
    -    system: {
    -      getStatus: {
    -        testnet: [{}] // No parameters needed
    -      },
    -      getTotalCreditsInPlatform: {
    -        testnet: [{}]
    -      },
    -      getCurrentQuorumsInfo: {
    -        testnet: [{}] // No parameters needed
    -      },
    -      getPrefundedSpecializedBalance: {
    -        testnet: [
    -          { identityId: "AzaU7zqCT7X1kxh8yWxkT9PxAgNqWDu4Gz13emwcRyAT" }
    -        ]
    -      }
    -    },
    -
    -    protocol: {
    -      getProtocolVersionUpgradeState: {
    -        testnet: [{}] // No parameters needed
    -      },
    -      getProtocolVersionUpgradeVoteStatus: {
    -        testnet: [
    -          { 
    -            startProTxHash: "143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113",
    -            count: 100
    -          }
    -        ]
    -      }
    -    },
    -
    -    epoch: {
    -      getCurrentEpoch: {
    -        testnet: [{}]
    -      },
    -      getEpochsInfo: {
    -        testnet: [
    -          {
    -            startEpoch: 1000,
    -            count: 100,
    -            ascending: true
    -          }
    -        ]
    -      },
    -      getFinalizedEpochInfos: {
    -        testnet: [
    -          {
    -            startEpoch: 8635,
    -            count: 100,
    -            ascending: true
    -          }
    -        ]
    -      },
    -      getEvonodesProposedEpochBlocksByIds: {
    -        testnet: [
    -          {
    -            epoch: 8635,
    -            ids: ["143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113"]
    -          }
    -        ]
    -      },
    -      getEvonodesProposedEpochBlocksByRange: {
    -        testnet: [
    -          {
    -            epoch: 8635,
    -            limit: 10,
    -            startAfter: "143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113",
    -            orderAscending: true
    -          }
    -        ]
    -      }
    -    },
    -
    -    dpns: {
    -      getDpnsUsername: {
    -        testnet: [
    -          { 
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            limit: 10
    -          }
    -        ]
    -      },
    -      dpnsCheckAvailability: {
    -        testnet: [
    -          { label: "alice" },
    -          { label: "test-username" },
    -          { label: "available-name" }
    -        ]
    -      },
    -      dpnsResolve: {
    -        testnet: [
    -          { name: "therea1s11mshaddy5" },
    -          { name: "alice.dash" },
    -          { name: "test-name" }
    -        ]
    -      },
    -      dpnsSearch: {
    -        testnet: [
    -          { 
    -            prefix: "the",
    -            limit: 10
    -          },
    -          {
    -            prefix: "test",
    -            limit: 5
    -          }
    -        ]
    -      }
    -    },
    -
    -    token: {
    -      getTokenStatuses: {
    -        testnet: [
    -          {
    -            tokenIds: ["Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv", "H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy"]
    -          }
    -        ]
    -      },
    -      getTokenDirectPurchasePrices: {
    -        testnet: [
    -          {
    -            tokenIds: ["H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy"]
    -          }
    -        ]
    -      },
    -      getTokenContractInfo: {
    -        testnet: [
    -          {
    -            dataContractId: "H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy"
    -          }
    -        ]
    -      },
    -      getTokenPerpetualDistributionLastClaim: {
    -        testnet: [
    -          {
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            tokenId: "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv"
    -          }
    -        ]
    -      },
    -      getTokenTotalSupply: {
    -        testnet: [
    -          {
    -            tokenId: "Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv"
    -          }
    -        ]
    -      }
    -    },
    -
    -    voting: {
    -      getContestedResources: {
    -        testnet: [
    -          {
    -            documentTypeName: "domain",
    -            dataContractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -            indexName: "parentNameAndLabel",
    -            limit: 10,
    -            offset: 0,
    -            orderAscending: true
    -          }
    -        ]
    -      },
    -      getContestedResourceVoteState: {
    -        testnet: [
    -          {
    -            dataContractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -            documentTypeName: "domain",
    -            indexName: "parentNameAndLabel",
    -            indexValues: ["dash", "alice"],
    -            resultType: "contenders",
    -            allowIncludeLockedAndAbstainingVoteTally: false,
    -            count: 10,
    -            orderAscending: true
    -          }
    -        ]
    -      },
    -      getContestedResourceVotersForIdentity: {
    -        testnet: [
    -          {
    -            dataContractId: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec",
    -            documentTypeName: "domain",
    -            indexName: "parentNameAndLabel",
    -            indexValues: ["dash", "alice"],
    -            contestantId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            count: 10,
    -            orderAscending: true
    -          }
    -        ]
    -      },
    -      getContestedResourceIdentityVotes: {
    -        testnet: [
    -          {
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            limit: 10,
    -            offset: 0,
    -            orderAscending: true
    -          }
    -        ]
    -      },
    -      getVotePollsByEndDate: {
    -        testnet: [
    -          {
    -            limit: 10,
    -            offset: 0,
    -            orderAscending: true
    -          }
    -        ]
    -      }
    -    },
    -
    -    group: {
    -      getGroupInfo: {
    -        testnet: [
    -          {
    -            contractId: "49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N",
    -            groupContractPosition: 0
    -          }
    -        ]
    -      },
    -      getGroupInfos: {
    -        testnet: [
    -          {
    -            contractId: "49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N",
    -            count: 100
    -          }
    -        ]
    -      },
    -      getGroupActions: {
    -        testnet: [
    -          {
    -            contractId: "49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N",
    -            groupContractPosition: 0,
    -            status: "ACTIVE",
    -            count: 10
    -          }
    -        ]
    -      },
    -      getGroupActionSigners: {
    -        testnet: [
    -          {
    -            contractId: "49PJEnNx7ReCitzkLdkDNr4s6RScGsnNexcdSZJ1ph5N",
    -            groupContractPosition: 0,
    -            status: "ACTIVE",
    -            actionId: "6XJzL6Qb8Zhwxt4HFwh8NAn7q1u4dwdoUf8EmgzDudFZ"
    -          }
    -        ]
    -      }
    -    }
    -  },
    -
    -  // State transition test parameters organized by category
    -  stateTransitionParameters: {
    -    identity: {
    -      identityCreate: {
    -        testnet: [
    -          {
    -            seedPhrase: process.env.TEST_SEED_PHRASE_1 || "placeholder seed phrase",
    -            identityIndex: 0,
    -            keySelectionMode: "simple",
    -            assetLockProof: "a914b7e904ce25ed97594e72f7af0e66f298031c175487",
    -            privateKey: process.env.TEST_PRIVATE_KEY_IDENTITY_1 || "PLACEHOLDER_IDENTITY_KEY_1",
    -            expectedKeys: 2,
    -            description: "Test identity creation with standard seed phrase"
    -          }
    -        ]
    -      },
    -      identityTopUp: {
    -        testnet: [
    -          {
    -            identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -            assetLockProof: "a914b7e904ce25ed97594e72f7af0e66f298031c175487",
    -            privateKey: process.env.TEST_PRIVATE_KEY_IDENTITY_1 || "PLACEHOLDER_IDENTITY_KEY_1",
    -            description: "Top up existing identity with credits"
    -          }
    -        ]
    -      },
    -      identityCreditTransfer: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            recipientId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
    -            amount: 100000, // 0.000001 DASH in credits
    -            privateKey: process.env.TEST_PRIVATE_KEY_TRANSFER || "PLACEHOLDER_TRANSFER_KEY", // Transfer key
    -            description: "Transfer credits between identities"
    -          }
    -        ]
    -      },
    -      identityCreditWithdrawal: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            toAddress: "yQW6TmUFef5CDyhEYwjoN8aUTMmKLYYNDm",
    -            amount: 190000, // 0.0000019 DASH in credits (minimum withdrawal amount)
    -            privateKey: process.env.TEST_PRIVATE_KEY_TRANSFER || "PLACEHOLDER_TRANSFER_KEY",
    -            description: "Withdraw credits to Dash address"
    -          }
    -        ]
    -      }
    -    },
    -    dataContract: {
    -      dataContractCreate: {
    -        testnet: [
    -          {
    -            canBeDeleted: false,
    -            readonly: false,
    -            keepsHistory: false,
    -            documentSchemas: '{"note": {"type": "object", "properties": {"message": {"type": "string", "position": 0}}, "additionalProperties": false}}',
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            description: "Create simple test data contract with document schema"
    -          }
    -        ]
    -      },
    -      dataContractUpdate: {
    -        testnet: [
    -          {
    -            dataContractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Sample contract ID
    -            newDocumentSchemas: '{"note": {"type": "object", "properties": {"message": {"type": "string", "position": 0}, "author": {"type": "string", "position": 1}}, "additionalProperties": false}}',
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            description: "Update existing note document schema to add author field"
    -          }
    -        ]
    -      }
    -    },
    -    document: {
    -      documentCreate: {
    -        testnet: [
    -          {
    -            contractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Use simple note contract (will be created by dataContractCreate test)
    -            documentType: "note",
    -            documentFields: {
    -              message: "Document created for WASM-SDK UI testing"
    -            },
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            description: "Create test note document with simple schema"
    -          }
    -        ]
    -      },
    -      documentReplace: {
    -        testnet: [
    -          {
    -            contractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Use simple note contract
    -            documentType: "note",
    -            documentId: "Dy19ZeYPpqbEDcpsPcLwkviY5GZqT7yJL2EY4YfxTYjn", // Persistent testnet document
    -            documentFields: {
    -              message: "Updated document message for automation testing"
    -            },
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            description: "Replace existing note document"
    -          }
    -        ]
    -      },
    -      documentDelete: {
    -        testnet: [
    -          {
    -            contractId: "5kMgvQ9foEQ9TzDhz5jvbJ9Lhv5qqBpUeYEezHNEa6Ti", // Use simple note contract
    -            documentType: "note",
    -            documentId: "PLACEHOLDER_DOCUMENT_ID", // Will be set dynamically
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            description: "Delete existing note document"
    -          }
    -        ]
    -      },
    -      documentTransfer: {
    -        testnet: [
    -          {
    -            identityId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG", // Current owner
    -            privateKey: process.env.TEST_PRIVATE_KEY_SECONDARY || "PLACEHOLDER_CONTRACT_KEY",
    -            contractId: "HdRFTcxgwPSVgzdy6MTYutDLJdbpfLMXwuBaYLYKMVHv", // Use NFT contract
    -            documentType: "card",
    -            documentId: "EypPkQLgT6Jijht7NYs4jmK5TGzkNd1Z4WrQdH1hND59", // Existing trading card document
    -            recipientId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC", // Transfer recipient
    -            description: "Transfer trading card ownership to secondary identity"
    -          }
    -        ]
    -      },
    -      documentPurchase: {
    -        testnet: [
    -          {
    -            identityId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG", // Buyer identity
    -            privateKey: process.env.TEST_PRIVATE_KEY_SECONDARY || "PLACEHOLDER_SECONDARY_KEY",
    -            contractId: "HdRFTcxgwPSVgzdy6MTYutDLJdbpfLMXwuBaYLYKMVHv", // Use NFT contract
    -            documentType: "card",
    -            documentId: "EypPkQLgT6Jijht7NYs4jmK5TGzkNd1Z4WrQdH1hND59", // Existing trading card document
    -            price: 1000, // Price in credits
    -            description: "Purchase a priced trading card with secondary identity"
    -          }
    -        ]
    -      },
    -      documentSetPrice: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC", // Primary identity owns card after creation
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            contractId: "HdRFTcxgwPSVgzdy6MTYutDLJdbpfLMXwuBaYLYKMVHv", // Use NFT contract
    -            documentType: "card",
    -            documentId: "EypPkQLgT6Jijht7NYs4jmK5TGzkNd1Z4WrQdH1hND59", // Existing trading card document
    -            price: 1000, // Price in credits
    -            description: "Set price for a trading card"
    -          }
    -        ]
    -      }
    -    },
    -    token: {
    -      tokenMint: {
    -        testnet: [
    -          {
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            amount: "2",
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            // issuedToIdentityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            publicNote: "Token mint test",
    -            description: "Mint new tokens (may fail without permissions)"
    -          }
    -        ]
    -      },      
    -      tokenTransfer: {
    -        testnet: [
    -          {
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            amount: "1",
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            recipientId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",            
    -            publicNote: "Token transfer test",
    -            description: "Transfer tokens between identities"
    -          }
    -        ]
    -      },
    -      tokenBurn: {
    -        testnet: [
    -          {
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            amount: "1",
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            publicNote: "Token burn test",
    -            description: "Burn tokens from identity balance"
    -          }
    -        ]
    -      },
    -      tokenFreeze: {
    -        testnet: [
    -          {
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            identityToFreeze: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            publicNote: "Token freeze test",
    -            description: "Freeze tokens for an identity"
    -          }
    -        ]
    -      },
    -      tokenDestroyFrozen: {
    -        testnet: [
    -          {
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            frozenIdentityId: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            publicNote: "Destroy frozen tokens test",
    -            description: "Destroy frozen tokens from an identity"
    -          }
    -        ]
    -      },
    -      tokenUnfreeze: {
    -        testnet: [
    -          {
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            identityToUnfreeze: "HJDxtN6FJF3U3T9TMLWCqudfJ5VRkaUrxTsRp36djXAG",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            publicNote: "Token unfreeze test",
    -            description: "Unfreeze tokens for an identity"
    -          }
    -        ]
    -      },
    -      tokenClaim: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            distributionType: "perpetual",
    -            publicNote: "Token claim test",
    -            description: "Claim tokens from distribution"
    -          }
    -        ]
    -      },
    -      tokenSetPriceForDirectPurchase: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            priceType: "single",
    -            priceData: "10",
    -            publicNote: "Set token price test",
    -            description: "Set price for direct token purchases"
    -          }
    -        ]
    -      },
    -      tokenDirectPurchase: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            amount: "1",
    -            totalAgreedPrice: "10",
    -            description: "Direct purchase of tokens at configured price"
    -          }
    -        ]
    -      },
    -      tokenConfigUpdate: {
    -        testnet: [
    -          {
    -            identityId: "7XcruVSsGQVSgTcmPewaE4tXLutnW1F6PXxwMbo8GYQC",
    -            privateKey: process.env.TEST_PRIVATE_KEY_CONTRACT || "PLACEHOLDER_CONTRACT_KEY",
    -            contractId: "Afk9QSj9UDE14K1y9y3iSx6kUSm5LLmhbdAvPvWL4P2i",
    -            tokenPosition: 0,
    -            configItemType: "max_supply",
    -            configValue: "1000000",
    -            publicNote: "Update max supply test",
    -            description: "Update token configuration max supply"
    -          }
    -        ]
    -      },
    -    }
    -  },
    -
    -  // Common where clauses for document queries
    -  whereClausesExamples: {
    -    dpnsDomain: [
    -      [["normalizedParentDomainName", "==", "dash"]],
    -      [["normalizedParentDomainName", "==", "dash"], ["normalizedLabel", "startsWith", "test"]]
    -    ]
    -  },
    -
    -  // Order by examples
    -  orderByExamples: {
    -    createdAtDesc: [["$createdAt", "desc"]],
    -    createdAtAsc: [["$createdAt", "asc"]]
    -  }
    -};
    -
    -/**
    - * Get test parameters for a specific query
    - */
    -function getTestParameters(category, queryType, network = 'testnet') {
    -  const categoryData = testData.queryParameters[category];
    -  if (!categoryData) {
    -    throw new Error(`No test data found for category: ${category}`);
    -  }
    -
    -  const queryData = categoryData[queryType];
    -  if (!queryData) {
    -    throw new Error(`No test data found for query: ${category}.${queryType}`);
    -  }
    -
    -  const networkData = queryData[network];
    -  if (!networkData || networkData.length === 0) {
    -    throw new Error(`No test data found for ${category}.${queryType} on ${network}`);
    -  }
    -
    -  return networkData[0]; // Return first test case
    -}
    -
    -/**
    - * Get all test parameters for a query (for parameterized testing)
    - */
    -function getAllTestParameters(category, queryType, network = 'testnet') {
    -  const categoryData = testData.queryParameters[category];
    -  if (!categoryData) return [];
    -
    -  const queryData = categoryData[queryType];
    -  if (!queryData) return [];
    -
    -  return queryData[network] || [];
    -}
    -
    -/**
    - * Get test parameters for a specific state transition
    - */
    -function getStateTransitionParameters(category, transitionType, network = 'testnet') {
    -  const categoryData = testData.stateTransitionParameters[category];
    -  if (!categoryData) {
    -    throw new Error(`No state transition test data found for category: ${category}`);
    -  }
    -
    -  const transitionData = categoryData[transitionType];
    -  if (!transitionData) {
    -    throw new Error(`No state transition test data found for transition: ${category}.${transitionType}`);
    -  }
    -
    -  const networkData = transitionData[network];
    -  if (!networkData || networkData.length === 0) {
    -    throw new Error(`No state transition test data found for ${category}.${transitionType} on ${network}`);
    -  }
    -
    -  return networkData[0]; // Return first test case
    -}
    -
    -/**
    - * Get all test parameters for a state transition (for parameterized testing)
    - */
    -function getAllStateTransitionParameters(category, transitionType, network = 'testnet') {
    -  const categoryData = testData.stateTransitionParameters[category];
    -  if (!categoryData) return [];
    -
    -  const transitionData = categoryData[transitionType];
    -  if (!transitionData) return [];
    -
    -  return transitionData[network] || [];
    -}
    -
    -module.exports = {
    -  testData,
    -  getTestParameters,
    -  getAllTestParameters,
    -  getStateTransitionParameters,
    -  getAllStateTransitionParameters
    -};
    diff --git a/packages/wasm-sdk/test/ui-automation/package-lock.json b/packages/wasm-sdk/test/ui-automation/package-lock.json
    deleted file mode 100644
    index 86e0d8971d3..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/package-lock.json
    +++ /dev/null
    @@ -1,96 +0,0 @@
    -{
    -  "name": "wasm-sdk-ui-automation",
    -  "version": "1.0.0",
    -  "lockfileVersion": 3,
    -  "requires": true,
    -  "packages": {
    -    "": {
    -      "name": "wasm-sdk-ui-automation",
    -      "version": "1.0.0",
    -      "dependencies": {
    -        "dotenv": "^17.2.1"
    -      },
    -      "devDependencies": {
    -        "@playwright/test": "^1.54.1"
    -      },
    -      "engines": {
    -        "node": ">=18"
    -      }
    -    },
    -    "node_modules/@playwright/test": {
    -      "version": "1.54.1",
    -      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz",
    -      "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==",
    -      "dev": true,
    -      "license": "Apache-2.0",
    -      "dependencies": {
    -        "playwright": "1.54.1"
    -      },
    -      "bin": {
    -        "playwright": "cli.js"
    -      },
    -      "engines": {
    -        "node": ">=18"
    -      }
    -    },
    -    "node_modules/dotenv": {
    -      "version": "17.2.1",
    -      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
    -      "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
    -      "license": "BSD-2-Clause",
    -      "engines": {
    -        "node": ">=12"
    -      },
    -      "funding": {
    -        "url": "https://dotenvx.com"
    -      }
    -    },
    -    "node_modules/fsevents": {
    -      "version": "2.3.2",
    -      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
    -      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
    -      "dev": true,
    -      "hasInstallScript": true,
    -      "license": "MIT",
    -      "optional": true,
    -      "os": [
    -        "darwin"
    -      ],
    -      "engines": {
    -        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
    -      }
    -    },
    -    "node_modules/playwright": {
    -      "version": "1.54.1",
    -      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz",
    -      "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==",
    -      "dev": true,
    -      "license": "Apache-2.0",
    -      "dependencies": {
    -        "playwright-core": "1.54.1"
    -      },
    -      "bin": {
    -        "playwright": "cli.js"
    -      },
    -      "engines": {
    -        "node": ">=18"
    -      },
    -      "optionalDependencies": {
    -        "fsevents": "2.3.2"
    -      }
    -    },
    -    "node_modules/playwright-core": {
    -      "version": "1.54.1",
    -      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz",
    -      "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==",
    -      "dev": true,
    -      "license": "Apache-2.0",
    -      "bin": {
    -        "playwright-core": "cli.js"
    -      },
    -      "engines": {
    -        "node": ">=18"
    -      }
    -    }
    -  }
    -}
    diff --git a/packages/wasm-sdk/test/ui-automation/package.json b/packages/wasm-sdk/test/ui-automation/package.json
    deleted file mode 100644
    index 78bcefca2f5..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/package.json
    +++ /dev/null
    @@ -1,31 +0,0 @@
    -{
    -  "name": "wasm-sdk-ui-automation",
    -  "version": "1.0.0",
    -  "description": "UI automation tests for WASM SDK web interface",
    -  "private": true,
    -  "scripts": {
    -    "test": "playwright test",
    -    "test:headed": "playwright test --headed",
    -    "test:debug": "playwright test --debug",
    -    "test:ui": "playwright test --ui",
    -    "test:report": "playwright show-report",
    -    "test:smoke": "playwright test tests/basic-smoke.spec.js",
    -    "test:queries": "playwright test tests/query-execution.spec.js",
    -    "test:parameterized": "playwright test tests/parameterized-queries.spec.js",
    -    "test:all": "playwright test --reporter=html,json,list",
    -    "test:ci": "playwright test --reporter=json --output-dir=test-results",
    -    "install-browsers": "playwright install",
    -    "serve": "cd ../../ && python3 -m http.server 8888",
    -    "pretest": "echo 'Starting web server for tests...'",
    -    "posttest": "echo 'Tests completed. Check playwright-report/ for results.'"
    -  },
    -  "devDependencies": {
    -    "@playwright/test": "^1.54.1"
    -  },
    -  "engines": {
    -    "node": ">=18"
    -  },
    -  "dependencies": {
    -    "dotenv": "^17.2.1"
    -  }
    -}
    diff --git a/packages/wasm-sdk/test/ui-automation/playwright.config.js b/packages/wasm-sdk/test/ui-automation/playwright.config.js
    deleted file mode 100644
    index 5119dde4d90..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/playwright.config.js
    +++ /dev/null
    @@ -1,88 +0,0 @@
    -// @ts-check
    -const { defineConfig, devices } = require('@playwright/test');
    -
    -/**
    - * @see https://playwright.dev/docs/test-configuration
    - */
    -module.exports = defineConfig({
    -  testDir: './tests',
    -  /* Fail the build on CI if you accidentally left test.only in the source code. */
    -  forbidOnly: !!process.env.CI,
    -  /* Retry on CI only */
    -  retries: process.env.CI ? 2 : 0,
    -  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
    -  reporter: [
    -    ['html', { outputFolder: 'playwright-report' }],
    -    ['json', { outputFile: 'test-results.json' }],
    -    ['list']
    -  ],
    -  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
    -  use: {
    -    /* Base URL to use in actions like `await page.goto('/')`. */
    -    baseURL: 'http://localhost:8888',
    -    
    -    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
    -    trace: 'on-first-retry',
    -    
    -    /* Take screenshot on failure */
    -    screenshot: 'only-on-failure',
    -    
    -    /* Record video on failure */
    -    video: 'retain-on-failure',
    -    
    -    /* Global timeout for each action (e.g. click, fill, etc.) */
    -    actionTimeout: 30000,
    -    
    -    /* Global timeout for each navigation action */
    -    navigationTimeout: 30000,
    -  },
    -
    -  /* Configure projects for different test execution modes */
    -  projects: [
    -    {
    -      name: 'parallel-tests',
    -      testMatch: ['basic-smoke.spec.js', 'query-execution.spec.js', 'parameterized-queries.spec.js'],
    -      fullyParallel: true,
    -      workers: process.env.CI ? 1 : undefined,
    -      use: { 
    -        ...devices['Desktop Chrome'],
    -        // Enable headless mode by default
    -        headless: true,
    -        // Use a larger viewport for better testing
    -        viewport: { width: 1920, height: 1080 }
    -      },
    -    },
    -    // Skip state transitions tests in CI environments
    -    // These are very slow-running due to https://github.com/dashpay/platform/issues/2736
    -    ...(process.env.CI ? [] : [{
    -      name: 'sequential-tests',
    -      testMatch: ['state-transitions.spec.js'],
    -      fullyParallel: false, // Tests in file run in order
    -      workers: 1, // Single worker for sequential execution
    -      use: { 
    -        ...devices['Desktop Chrome'],
    -        // Enable headless mode by default
    -        headless: true,
    -        // Use a larger viewport for better testing
    -        viewport: { width: 1920, height: 1080 }
    -      },
    -    }]),
    -  ],
    -
    -  /* Run your local dev server before starting the tests */
    -  webServer: {
    -    command: 'python3 -m http.server 8888',
    -    url: 'http://localhost:8888',
    -    cwd: '../../', // Run from wasm-sdk directory to serve index.html
    -    reuseExistingServer: !process.env.CI,
    -    timeout: 60000,
    -  },
    -
    -  /* Global test timeout */
    -  timeout: 120000,
    -  
    -  /* Expect timeout for assertions */
    -  expect: {
    -    timeout: 10000,
    -  },
    -});
    diff --git a/packages/wasm-sdk/test/ui-automation/run-ui-tests.sh b/packages/wasm-sdk/test/ui-automation/run-ui-tests.sh
    deleted file mode 100755
    index 808a3f1050c..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/run-ui-tests.sh
    +++ /dev/null
    @@ -1,288 +0,0 @@
    -#!/bin/bash
    -
    -# WASM SDK UI Automation Test Runner
    -# This script sets up and runs the UI automation tests
    -
    -set -e
    -
    -# Colors for output
    -RED='\033[0;31m'
    -GREEN='\033[0;32m'
    -YELLOW='\033[1;33m'
    -NC='\033[0m' # No Color
    -
    -# Configuration
    -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 
    -WASM_SDK_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
    -UI_TEST_DIR="$SCRIPT_DIR"
    -
    -# Debug mode flag
    -DEBUG=${DEBUG:-false}
    -
    -# Function to print colored output
    -print_status() {
    -    echo -e "${GREEN}[INFO]${NC} $1"
    -}
    -
    -print_warning() {
    -    echo -e "${YELLOW}[WARN]${NC} $1"
    -}
    -
    -print_error() {
    -    echo -e "${RED}[ERROR]${NC} $1"
    -}
    -
    -print_debug() {
    -    if [ "$DEBUG" = "true" ]; then
    -        echo -e "${YELLOW}[DEBUG]${NC} $1"
    -    fi
    -}
    -
    -
    -# Function to validate paths
    -validate_paths() {
    -    print_debug "Validating directory paths..."
    -    print_debug "SCRIPT_DIR: $SCRIPT_DIR"
    -    print_debug "WASM_SDK_DIR: $WASM_SDK_DIR"
    -    print_debug "UI_TEST_DIR: $UI_TEST_DIR"
    -    
    -    # Check if UI test directory exists and contains expected files
    -    if [ ! -d "$UI_TEST_DIR" ]; then
    -        print_error "UI test directory not found: $UI_TEST_DIR"
    -        exit 1
    -    fi
    -    
    -    if [ ! -f "$UI_TEST_DIR/package.json" ]; then
    -        print_error "package.json not found in UI test directory: $UI_TEST_DIR"
    -        exit 1
    -    fi
    -    
    -    if [ ! -f "$UI_TEST_DIR/playwright.config.js" ]; then
    -        print_error "playwright.config.js not found in UI test directory: $UI_TEST_DIR"
    -        exit 1
    -    fi
    -    
    -    # Check if WASM SDK directory exists
    -    if [ ! -d "$WASM_SDK_DIR" ]; then
    -        print_error "WASM SDK directory not found: $WASM_SDK_DIR"
    -        exit 1
    -    fi
    -    
    -    if [ ! -f "$WASM_SDK_DIR/index.html" ]; then
    -        print_error "index.html not found in WASM SDK directory: $WASM_SDK_DIR"
    -        exit 1
    -    fi
    -    
    -    print_debug "Path validation passed βœ“"
    -}
    -
    -# Function to check prerequisites
    -check_prerequisites() {
    -    print_status "Checking prerequisites..."
    -    
    -    # Validate paths first
    -    validate_paths
    -    
    -    # Check Node.js
    -    if ! command -v node &> /dev/null; then
    -        print_error "Node.js is not installed. Please install Node.js 18+ and try again."
    -        exit 1
    -    fi
    -    
    -    NODE_VERSION=$(node --version | sed 's/v//')
    -    NODE_MAJOR=$(echo $NODE_VERSION | cut -d. -f1)
    -    if [ "$NODE_MAJOR" -lt 18 ]; then
    -        print_error "Node.js version $NODE_VERSION is too old. Please install Node.js 18+ and try again."
    -        exit 1
    -    fi
    -    print_debug "Node.js version: $NODE_VERSION βœ“"
    -    
    -    # Check Python
    -    if ! command -v python3 &> /dev/null; then
    -        print_error "Python 3 is not installed. Please install Python 3 and try again."
    -        exit 1
    -    fi
    -    PYTHON_VERSION=$(python3 --version 2>&1 | cut -d' ' -f2)
    -    print_debug "Python version: $PYTHON_VERSION βœ“"
    -    
    -    # Check if WASM SDK is built
    -    if [ ! -f "$WASM_SDK_DIR/pkg/wasm_sdk.js" ]; then
    -        print_warning "WASM SDK not found. Building..."
    -        cd "$WASM_SDK_DIR"
    -        if ! ./build.sh; then
    -            print_error "Failed to build WASM SDK"
    -            exit 1
    -        fi
    -    fi
    -    print_debug "WASM SDK found βœ“"
    -    
    -    print_status "Prerequisites check passed βœ“"
    -}
    -
    -# Function to install dependencies
    -install_dependencies() {
    -    print_status "Installing test dependencies..."
    -    
    -    cd "$UI_TEST_DIR" || {
    -        print_error "Failed to change to UI test directory: $UI_TEST_DIR"
    -        exit 1
    -    }
    -    print_debug "Changed to directory: $(pwd)"
    -    
    -    if [ ! -d "node_modules" ]; then
    -        print_status "Installing npm dependencies..."
    -        if ! npm install; then
    -            print_error "Failed to install npm dependencies"
    -            exit 1
    -        fi
    -    fi
    -    
    -    # Check if browsers are installed
    -    if ! npx playwright --version &> /dev/null; then
    -        print_error "Playwright not found. Installing..."
    -        if ! npm install; then
    -            print_error "Failed to install Playwright"
    -            exit 1
    -        fi
    -    fi
    -    
    -    # Install browsers if needed
    -    if ! find "$HOME/.cache/ms-playwright" -maxdepth 1 -name "chromium-*" -type d -print -quit 2>/dev/null | grep -q .; then
    -        print_status "Installing Playwright browsers..."
    -        if ! npx playwright install chromium; then
    -            print_error "Failed to install Playwright browsers"
    -            exit 1
    -        fi
    -    fi
    -    
    -    print_status "Dependencies installed βœ“"
    -}
    -
    -
    -# Function to run tests
    -run_tests() {
    -    print_status "Running UI automation tests..."
    -    
    -    cd "$UI_TEST_DIR" || {
    -        print_error "Failed to change to UI test directory: $UI_TEST_DIR"
    -        exit 1
    -    }
    -    print_debug "Running tests from directory: $(pwd)"
    -    
    -    # Verify npm scripts exist
    -    if [ ! -f "package.json" ]; then
    -        print_error "package.json not found in test directory"
    -        exit 1
    -    fi
    -    
    -    # Show available npm scripts for debugging
    -    print_debug "Available npm scripts:"
    -    if [ "$DEBUG" = "true" ]; then
    -        npm run 2>/dev/null | grep -E "^\s*(test:|build:|start)" || true
    -    fi
    -    
    -    # Determine test type from arguments
    -    case "${1:-all}" in
    -        "smoke")
    -            print_status "Running smoke tests..."
    -            npm run test:smoke
    -            ;;
    -        "queries")
    -            print_status "Running query execution tests..."
    -            npm run test:queries
    -            ;;
    -        "parameterized")
    -            print_status "Running parameterized tests..."
    -            npm run test:parameterized
    -            ;;
    -        "headed")
    -            print_status "Running tests in headed mode..."
    -            npm run test:headed
    -            ;;
    -        "debug")
    -            print_status "Running tests in debug mode..."
    -            npm run test:debug
    -            ;;
    -        "ui")
    -            print_status "Running tests in UI mode..."
    -            npm run test:ui
    -            ;;
    -        "all")
    -            print_status "Running all tests..."
    -            npm run test:all
    -            ;;
    -        *)
    -            # Pass through any other arguments to playwright
    -            print_status "Running custom playwright command: $*"
    -            npx playwright test "$@"
    -            ;;
    -    esac
    -}
    -
    -# Function to show results
    -show_results() {
    -    print_status "Test execution completed!"
    -    
    -    if [ -d "$UI_TEST_DIR/playwright-report" ]; then
    -        print_status "HTML report available at: $UI_TEST_DIR/playwright-report/index.html"
    -        print_status "To view report: npm run test:report"
    -    fi
    -    
    -    if [ -f "$UI_TEST_DIR/test-results.json" ]; then
    -        print_status "JSON results available at: $UI_TEST_DIR/test-results.json"
    -    fi
    -}
    -
    -# Function to print usage
    -print_usage() {
    -    echo "Usage: $0 [test_type]"
    -    echo ""
    -    echo "Test types:"
    -    echo "  smoke          - Run basic smoke tests"
    -    echo "  queries        - Run query execution tests"
    -    echo "  parameterized  - Run parameterized tests"
    -    echo "  headed         - Run tests in headed mode (visible browser)"
    -    echo "  debug          - Run tests in debug mode"
    -    echo "  ui             - Run tests in UI mode (interactive)"
    -    echo "  all            - Run all tests (default)"
    -    echo ""
    -    echo "Environment variables:"
    -    echo "  DEBUG=true     - Enable debug output"
    -    echo ""
    -    echo "Examples:"
    -    echo "  $0                    # Run all tests"
    -    echo "  $0 smoke              # Run smoke tests only"
    -    echo "  $0 headed             # Run tests with visible browser"
    -    echo "  DEBUG=true $0 smoke   # Run smoke tests with debug output"
    -    echo "  $0 --grep=\"Identity\"  # Run tests matching pattern"
    -}
    -
    -# Main execution
    -main() {
    -    # Handle help flag
    -    if [[ "$1" == "-h" || "$1" == "--help" ]]; then
    -        print_usage
    -        exit 0
    -    fi
    -    
    -    print_status "Starting WASM SDK UI Automation Tests..."
    -    print_status "Working directory: $WASM_SDK_DIR"
    -    print_status "Test directory: $UI_TEST_DIR"
    -    
    -    check_prerequisites
    -    install_dependencies
    -    
    -    # Run tests and capture exit code
    -    if run_tests "$@"; then
    -        print_status "All tests completed successfully! βœ…"
    -        show_results
    -        exit 0
    -    else
    -        print_error "Some tests failed! ❌"
    -        show_results
    -        exit 1
    -    fi
    -}
    -
    -# Run main function with all arguments
    -main "$@"
    diff --git a/packages/wasm-sdk/test/ui-automation/tests/basic-smoke.spec.js b/packages/wasm-sdk/test/ui-automation/tests/basic-smoke.spec.js
    deleted file mode 100644
    index 576900d5751..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/tests/basic-smoke.spec.js
    +++ /dev/null
    @@ -1,182 +0,0 @@
    -const { test, expect } = require('@playwright/test');
    -const { WasmSdkPage } = require('../utils/wasm-sdk-page');
    -
    -test.describe('WASM SDK Basic Smoke Tests', () => {
    -  let wasmSdkPage;
    -
    -  test.beforeEach(async ({ page }) => {
    -    wasmSdkPage = new WasmSdkPage(page);
    -    await wasmSdkPage.initialize('testnet');
    -  });
    -
    -  test('should initialize SDK successfully', async () => {
    -    // Wait for SDK to be fully ready (with retry logic)
    -    let statusState;
    -    let attempts = 0;
    -    const maxAttempts = 3;
    -    
    -    while (attempts < maxAttempts) {
    -      statusState = await wasmSdkPage.getStatusBannerState();
    -      
    -      if (statusState === 'success') {
    -        break;
    -      }
    -      
    -      if (statusState === 'loading') {
    -        // Wait for loading to complete
    -        await wasmSdkPage.waitForSdkReady();
    -        statusState = await wasmSdkPage.getStatusBannerState();
    -        
    -        if (statusState === 'success') {
    -          break;
    -        }
    -      }
    -      
    -      attempts++;
    -      if (attempts < maxAttempts) {
    -        await wasmSdkPage.page.waitForTimeout(2000);
    -      }
    -    }
    -    
    -    // Final check
    -    expect(statusState).toBe('success');
    -    
    -    // Verify network is set to testnet
    -    const networkIndicator = wasmSdkPage.page.locator('#networkIndicator');
    -    await expect(networkIndicator).toContainText('TESTNET');
    -  });
    -
    -  test('should load query categories', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    
    -    const categories = await wasmSdkPage.getAvailableQueryCategories();
    -    
    -    // Check that we have the expected categories
    -    const expectedCategories = [
    -      'Identity Queries',
    -      'Data Contract Queries', 
    -      'Document Queries',
    -      'DPNS Queries',
    -      'Voting & Contested Resources',
    -      'Protocol & Version',
    -      'Epoch & Block',
    -      'Token Queries',
    -      'Group Queries',
    -      'System & Utility'
    -    ];
    -    
    -    for (const category of expectedCategories) {
    -      expect(categories).toContain(category);
    -    }
    -  });
    -
    -  test('should switch between networks', async () => {
    -    // Test switching to mainnet
    -    await wasmSdkPage.setNetwork('mainnet');
    -    const mainnetIndicator = wasmSdkPage.page.locator('#networkIndicator');
    -    await expect(mainnetIndicator).toContainText('MAINNET');
    -    
    -    // Switch back to testnet
    -    await wasmSdkPage.setNetwork('testnet');
    -    const testnetIndicator = wasmSdkPage.page.locator('#networkIndicator');
    -    await expect(testnetIndicator).toContainText('TESTNET');
    -  });
    -
    -  test('should show query types when category is selected', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    await wasmSdkPage.setQueryCategory('identity');
    -    
    -    const queryTypes = await wasmSdkPage.getAvailableQueryTypes();
    -    
    -    // Should have some identity query types
    -    expect(queryTypes.length).toBeGreaterThan(0);
    -    expect(queryTypes).toContain('Get Identity');
    -  });
    -
    -  test('should show input fields when query type is selected', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    await wasmSdkPage.setQueryCategory('identity');
    -    await wasmSdkPage.setQueryType('getIdentity');
    -    
    -    // Should show query inputs container
    -    const queryInputs = wasmSdkPage.page.locator('#queryInputs');
    -    await expect(queryInputs).toBeVisible();
    -    
    -    // Should show execute button
    -    const executeButton = wasmSdkPage.page.locator('#executeQuery');
    -    await expect(executeButton).toBeVisible();
    -  });
    -
    -  test('should enable/disable execute button based on form completion', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    await wasmSdkPage.setQueryCategory('identity');
    -    await wasmSdkPage.setQueryType('getIdentity');
    -    
    -    const executeButton = wasmSdkPage.page.locator('#executeQuery');
    -    
    -    // Button should be enabled (even without required params for this test)
    -    await expect(executeButton).toBeVisible();
    -  });
    -
    -  test('should clear results when clear button is clicked', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    await wasmSdkPage.setQueryCategory('system');
    -    await wasmSdkPage.setQueryType('getStatus');
    -    
    -    // Execute a simple query first
    -    await wasmSdkPage.executeQuery();
    -    
    -    // Clear results
    -    await wasmSdkPage.clearResults();
    -    
    -    // Verify results are cleared
    -    const resultContent = wasmSdkPage.page.locator('#identityInfo');
    -    await expect(resultContent).toHaveClass(/empty/);
    -  });
    -
    -  test('should toggle proof information', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    await wasmSdkPage.setQueryCategory('identity');
    -    await wasmSdkPage.setQueryType('getIdentity');
    -    
    -    // Wait a moment for UI to fully load
    -    await wasmSdkPage.page.waitForTimeout(1000);
    -    
    -    // Check if proof toggle is available
    -    const proofContainer = wasmSdkPage.page.locator('#proofToggleContainer');
    -    
    -    try {
    -      // Wait for container to potentially appear
    -      await proofContainer.waitFor({ state: 'visible', timeout: 5000 });
    -      
    -      // Test enabling proof info
    -      const enableSuccess = await wasmSdkPage.enableProofInfo();
    -      if (enableSuccess) {
    -        const proofToggle = wasmSdkPage.page.locator('#proofToggle');
    -        await expect(proofToggle).toBeChecked();
    -        
    -        // Test disabling proof info
    -        const disableSuccess = await wasmSdkPage.disableProofInfo();
    -        if (disableSuccess) {
    -          await expect(proofToggle).not.toBeChecked();
    -        }
    -        
    -      } else {
    -      }
    -    } catch (error) {
    -      // Proof toggle not available for this query type - that's OK
    -    }
    -  });
    -
    -  test('should show query description when available', async () => {
    -    await wasmSdkPage.setOperationType('queries');
    -    await wasmSdkPage.setQueryCategory('identity');
    -    await wasmSdkPage.setQueryType('getIdentity');
    -    
    -    const description = await wasmSdkPage.getQueryDescription();
    -    
    -    if (description) {
    -      expect(description.length).toBeGreaterThan(0);
    -    }
    -  });
    -});
    diff --git a/packages/wasm-sdk/test/ui-automation/tests/parameterized-queries.spec.js b/packages/wasm-sdk/test/ui-automation/tests/parameterized-queries.spec.js
    deleted file mode 100644
    index f1f2ee07717..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/tests/parameterized-queries.spec.js
    +++ /dev/null
    @@ -1,238 +0,0 @@
    -const { test, expect } = require('@playwright/test');
    -const { WasmSdkPage } = require('../utils/wasm-sdk-page');
    -const { ParameterInjector } = require('../utils/parameter-injector');
    -
    -test.describe('WASM SDK Parameterized Query Tests', () => {
    -  let wasmSdkPage;
    -  let parameterInjector;
    -
    -  test.beforeEach(async ({ page }) => {
    -    wasmSdkPage = new WasmSdkPage(page);
    -    parameterInjector = new ParameterInjector(wasmSdkPage);
    -    await wasmSdkPage.initialize('testnet');
    -  });
    -
    -  // Generate parameterized tests for each query category
    -  const queryCategories = [
    -    { category: 'identity', queries: ['getIdentity', 'getIdentityBalance', 'getIdentityKeys'] },
    -    { category: 'dataContract', queries: ['getDataContract', 'getDataContracts'] },
    -    { category: 'document', queries: ['getDocuments', 'getDocument'] },
    -    { category: 'system', queries: ['getStatus', 'getCurrentEpoch', 'getTotalCreditsInPlatform'] }
    -  ];
    -
    -  for (const { category, queries } of queryCategories) {
    -    test.describe(`${category.toUpperCase()} Category Tests`, () => {
    -      
    -      for (const queryType of queries) {
    -        test(`should execute ${queryType} with all available parameter sets`, async () => {
    -          const parameterSets = parameterInjector.createParameterizedTests(category, queryType, 'testnet');
    -          
    -          if (parameterSets.length === 0) {
    -            test.skip(`No parameter sets available for ${category}.${queryType}`);
    -            return;
    -          }
    -
    -          let successCount = 0;
    -          let errorCount = 0;
    -          const results = [];
    -
    -          for (const paramSet of parameterSets) {
    -            try {
    -              console.log(`\nπŸ§ͺ Testing ${paramSet.testName}`);
    -              
    -              await wasmSdkPage.setupQuery(category, queryType);
    -              
    -              // Inject parameters
    -              const injectionSuccess = await parameterInjector.injectParameters(
    -                category, 
    -                queryType, 
    -                'testnet', 
    -                paramSet.index
    -              );
    -              
    -              if (!injectionSuccess) {
    -                console.warn(`⚠️  Could not inject parameters for ${paramSet.testName}`);
    -                continue;
    -              }
    -
    -              // Execute query
    -              const result = await wasmSdkPage.executeQueryAndGetResult();
    -              results.push({
    -                testName: paramSet.testName,
    -                parameters: paramSet.parameters,
    -                success: result.success,
    -                hasError: result.hasError,
    -                resultLength: result.result?.length || 0,
    -                statusText: result.statusText
    -              });
    -
    -              if (result.success) {
    -                successCount++;
    -                console.log(`βœ… ${paramSet.testName} - SUCCESS`);
    -              } else {
    -                errorCount++;
    -                console.log(`❌ ${paramSet.testName} - ERROR: ${result.statusText}`);
    -              }
    -
    -              // Brief pause between executions
    -              await wasmSdkPage.page.waitForTimeout(1000);
    -              await wasmSdkPage.clearResults();
    -              
    -            } catch (error) {
    -              errorCount++;
    -              console.error(`πŸ’₯ ${paramSet.testName} - EXCEPTION:`, error.message);
    -              results.push({
    -                testName: paramSet.testName,
    -                parameters: paramSet.parameters,
    -                success: false,
    -                hasError: true,
    -                error: error.message
    -              });
    -            }
    -          }
    -
    -          // Summary assertions
    -          console.log(`\nπŸ“Š ${category}.${queryType} Summary:`);
    -          console.log(`   Total tests: ${parameterSets.length}`);
    -          console.log(`   Successful: ${successCount}`);
    -          console.log(`   Errors: ${errorCount}`);
    -
    -          // At least one test should complete (success or graceful error)
    -          expect(successCount + errorCount).toBeGreaterThan(0);
    -          
    -          // Store results for reporting
    -          test.info().attach('test-results', {
    -            body: JSON.stringify(results, null, 2),
    -            contentType: 'application/json'
    -          });
    -        });
    -      }
    -    });
    -  }
    -
    -  test.describe('Cross-Network Parameter Tests', () => {
    -    const networks = ['testnet', 'mainnet'];
    -    
    -    for (const network of networks) {
    -      test(`should execute system queries on ${network}`, async () => {
    -        await wasmSdkPage.setNetwork(network);
    -        
    -        const systemQueries = ['getStatus', 'getCurrentEpoch'];
    -        const results = [];
    -        
    -        for (const queryType of systemQueries) {
    -          try {
    -            await wasmSdkPage.setupQuery('system', queryType);
    -            const result = await wasmSdkPage.executeQueryAndGetResult();
    -            
    -            results.push({
    -              network,
    -              queryType,
    -              success: result.success,
    -              hasError: result.hasError,
    -              resultLength: result.result?.length || 0
    -            });
    -            
    -            await wasmSdkPage.clearResults();
    -            await wasmSdkPage.page.waitForTimeout(500);
    -            
    -          } catch (error) {
    -            results.push({
    -              network,
    -              queryType,
    -              success: false,
    -              error: error.message
    -            });
    -          }
    -        }
    -        
    -        // At least one query should work
    -        const successfulQueries = results.filter(r => r.success);
    -        expect(successfulQueries.length).toBeGreaterThan(0);
    -        
    -        console.log(`${network} system queries:`, results);
    -      });
    -    }
    -  });
    -
    -  test.describe('Parameter Validation Tests', () => {
    -    test('should validate parameters before injection', async () => {
    -      const testCases = [
    -        {
    -          category: 'identity',
    -          queryType: 'getIdentity',
    -          parameters: { id: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec' },
    -          expectedValid: true
    -        },
    -        {
    -          category: 'identity', 
    -          queryType: 'getIdentity',
    -          parameters: { id: '' },
    -          expectedValid: false
    -        },
    -        {
    -          category: 'identity',
    -          queryType: 'getIdentity', 
    -          parameters: { id: '1234567890' }, // Too short for base58 ID
    -          expectedValid: false
    -        },
    -        {
    -          category: 'identity',
    -          queryType: 'getIdentity', 
    -          parameters: { id: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S0Il' }, // Contains invalid base58 chars
    -          expectedValid: false
    -        }
    -      ];
    -
    -      for (const testCase of testCases) {
    -        const validation = parameterInjector.validateParameters(testCase.parameters);
    -        
    -        if (testCase.expectedValid) {
    -          expect(validation.errors.length).toBe(0);
    -        } else {
    -          expect(validation.errors.length).toBeGreaterThan(0);
    -        }
    -        
    -        console.log(`Validation for ${JSON.stringify(testCase.parameters)}:`, validation);
    -      }
    -    });
    -  });
    -
    -  test.describe('Random Parameter Stress Tests', () => {
    -    test('should handle random parameter generation gracefully', async () => {
    -      const testQueries = [
    -        { category: 'identity', queryType: 'getIdentity' },
    -        { category: 'system', queryType: 'getStatus' }
    -      ];
    -
    -      for (const { category, queryType } of testQueries) {
    -        try {
    -          const randomParams = parameterInjector.generateRandomParameters(category, queryType);
    -          
    -          if (Object.keys(randomParams).length > 0) {
    -            await wasmSdkPage.setupQuery(category, queryType);
    -            await wasmSdkPage.fillQueryParameters(randomParams);
    -            
    -            const result = await wasmSdkPage.executeQueryAndGetResult(false);
    -            
    -            // Should complete without crashing
    -            expect(result).toBeDefined();
    -            expect(typeof result.success).toBe('boolean');
    -            
    -            console.log(`Random test ${category}.${queryType}:`, {
    -              parameters: randomParams,
    -              success: result.success,
    -              hasError: result.hasError
    -            });
    -          }
    -          
    -          await wasmSdkPage.clearResults();
    -          
    -        } catch (error) {
    -          // Random parameters might cause errors - that's OK as long as UI doesn't crash
    -          console.log(`Random test error (acceptable): ${error.message}`);
    -        }
    -      }
    -    });
    -  });
    -});
    diff --git a/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js b/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js
    deleted file mode 100644
    index b375d8f5dde..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/tests/query-execution.spec.js
    +++ /dev/null
    @@ -1,1352 +0,0 @@
    -const { test, expect } = require('@playwright/test');
    -const { WasmSdkPage } = require('../utils/wasm-sdk-page');
    -const { ParameterInjector } = require('../utils/parameter-injector');
    -
    -/**
    - * Helper function to execute a query with proof toggle enabled
    - * @param {WasmSdkPage} wasmSdkPage - The page object instance
    - * @param {ParameterInjector} parameterInjector - The parameter injector instance
    - * @param {string} category - Query category (e.g., 'identity', 'documents')
    - * @param {string} queryName - Query name (e.g., 'getIdentity')
    - * @param {string} network - Network to use ('testnet' or 'mainnet')
    - * @returns {Promise} - The query result object
    - */
    -async function executeQueryWithProof(wasmSdkPage, parameterInjector, category, queryName, network = 'testnet') {
    -  await wasmSdkPage.setupQuery(category, queryName);
    -  
    -  // Enable proof info if available
    -  const proofEnabled = await wasmSdkPage.enableProofInfo();
    -  
    -  // If proof was enabled, wait for the toggle to be actually checked
    -  if (proofEnabled) {
    -    const proofToggle = wasmSdkPage.page.locator('#proofToggle');
    -    await expect(proofToggle).toBeChecked();
    -    console.log('Proof toggle confirmed as checked');
    -  }
    -  
    -  const success = await parameterInjector.injectParameters(category, queryName, network);
    -  expect(success).toBe(true);
    -  
    -  const result = await wasmSdkPage.executeQueryAndGetResult();
    -  
    -  return { result, proofEnabled };
    -}
    -
    -/**
    - * Helper function to parse balance/nonce responses that may contain large numbers
    - * @param {string} resultStr - The raw result string from the query
    - * @param {string} propertyName - The property name to extract (e.g., 'balance', 'nonce')
    - * @returns {number} - The parsed number value
    - */
    -function parseNumericResult(resultStr, propertyName = 'balance') {
    -  const trimmedStr = resultStr.trim();
    -  
    -  // Try to parse as JSON first (in case it's a JSON response)
    -  let numericValue;
    -  try {
    -    const parsed = JSON.parse(trimmedStr);
    -    
    -    // Check if it's a JSON object with the expected property
    -    if (typeof parsed === 'object' && parsed[propertyName] !== undefined) {
    -      numericValue = Number(parsed[propertyName]);
    -    } else if (typeof parsed === 'number') {
    -      numericValue = parsed;
    -    } else {
    -      numericValue = Number(parsed);
    -    }
    -  } catch {
    -    // If not JSON, try parsing directly as number
    -    numericValue = Number(trimmedStr);
    -    
    -    // If Number() fails, log the issue
    -    if (isNaN(numericValue)) {
    -      console.error(`Failed to parse ${propertyName}:`, trimmedStr, 'type:', typeof trimmedStr);
    -    }
    -  }
    -  
    -  return numericValue;
    -}
    -
    -/**
    - * Helper function to validate basic query result properties
    - * @param {Object} result - The query result object
    - */
    -function validateBasicQueryResult(result) {
    -  expect(result.success).toBe(true);
    -  expect(result.result).toBeDefined();
    -  expect(result.hasError).toBe(false);
    -  expect(result.result).not.toContain('Error executing query');
    -  expect(result.result).not.toContain('not found');
    -  expect(result.result).not.toContain('invalid');
    -}
    -
    -/**
    - * Helper function to validate basic query result properties for DPNS queries
    - * (allows "not found" as valid response)
    - * @param {Object} result - The query result object
    - */
    -function validateBasicDpnsQueryResult(result) {
    -  expect(result.success).toBe(true);
    -  expect(result.result).toBeDefined();
    -  expect(result.hasError).toBe(false);
    -  expect(result.result).not.toContain('Error executing query');
    -  expect(result.result).not.toContain('invalid');
    -}
    -
    -/**
    - * Helper function to validate proof content contains expected fields
    - * @param {string} proofContent - The proof content string
    - */
    -function validateProofContent(proofContent) {
    -  expect(proofContent).toBeDefined();
    -  expect(proofContent).not.toBe('');
    -  expect(proofContent).toContain('metadata');
    -  expect(proofContent).toContain('proof');
    -  expect(proofContent).toContain('grovedbProof');
    -  expect(proofContent).toContain('quorumHash');
    -  expect(proofContent).toContain('signature');
    -}
    -
    -/**
    - * Helper function to validate split view (proof mode) result
    - * @param {Object} result - The query result object
    - */
    -function validateSplitView(result) {
    -  expect(result.inSplitView).toBe(true);
    -  expect(result.proofContent).toBeDefined();
    -  expect(result.proofContent).not.toBe('');
    -  validateProofContent(result.proofContent);
    -}
    -
    -/**
    - * Helper function to validate single view (non-proof mode) result
    - * @param {Object} result - The query result object
    - */
    -function validateSingleView(result) {
    -  expect(result.inSplitView).toBe(false);
    -  expect(result.proofContent).toBeNull();
    -}
    -
    -/**
    - * Helper function to validate data contract result
    - * @param {string} resultStr - The raw result string containing contract data
    - */
    -function validateContractResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const contractData = JSON.parse(resultStr);
    -  expect(contractData).toBeDefined();
    -  expect(contractData).toHaveProperty('id');
    -  expect(contractData).toHaveProperty('config');
    -}
    -
    -/**
    - * Helper function to validate document result
    - * @param {string} resultStr - The raw result string containing document data
    - */
    -function validateDocumentResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const documentData = JSON.parse(resultStr);
    -  expect(documentData).toBeDefined();
    -  // Documents can be arrays or single objects
    -  if (Array.isArray(documentData)) {
    -    expect(documentData.length).toBeGreaterThanOrEqual(0);
    -    // Validate each document in the array has ownerId
    -    documentData.forEach(document => {
    -      expect(document).toHaveProperty('ownerId');
    -    });
    -  } else {
    -    expect(documentData).toBeInstanceOf(Object);
    -    // Validate single document has ownerId
    -    expect(documentData).toHaveProperty('ownerId');
    -  }
    -}
    -
    -/**
    - * Helper function to validate numeric results and ensure they're valid
    - * @param {string} resultStr - The raw result string
    - * @param {string} propertyName - The property name to extract
    - * @returns {number} - The validated numeric value
    - */
    -function validateNumericResult(resultStr, propertyName = 'balance') {
    -  const numericValue = parseNumericResult(resultStr, propertyName);
    -  expect(numericValue).not.toBeNaN();
    -  expect(numericValue).toBeGreaterThanOrEqual(0);
    -  return numericValue;
    -}
    -
    -/**
    - * Specific validation functions for parameterized tests
    - */
    -function validateIdentityResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const identityData = JSON.parse(resultStr);
    -  expect(identityData).toHaveProperty('id');
    -  expect(identityData).toHaveProperty('publicKeys');
    -  expect(identityData).toHaveProperty('balance');
    -}
    -
    -function validateKeysResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const keysData = JSON.parse(resultStr);
    -  expect(keysData).toBeDefined();
    -  keysData.forEach(key => {
    -      expect(key).toHaveProperty('keyId')
    -      expect(key).toHaveProperty('purpose')
    -    });
    -}
    -
    -function validateIdentitiesContractKeysResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const contractKeysData = JSON.parse(resultStr);
    -  expect(contractKeysData).toBeDefined();
    -  expect(Array.isArray(contractKeysData)).toBe(true);
    -  
    -  contractKeysData.forEach(identityResult => {
    -    expect(identityResult).toHaveProperty('identityId');
    -    expect(identityResult).toHaveProperty('keys');
    -    expect(Array.isArray(identityResult.keys)).toBe(true);
    -    
    -    identityResult.keys.forEach(key => {
    -      expect(key).toHaveProperty('keyId');
    -      expect(key).toHaveProperty('purpose');
    -      expect(key).toHaveProperty('keyType');
    -      expect(key).toHaveProperty('publicKeyData');
    -      expect(key).toHaveProperty('securityLevel');
    -    });
    -  });
    -}
    -
    -function validateIdentitiesResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const identitiesData = JSON.parse(resultStr);
    -  expect(identitiesData).toBeDefined();
    -  
    -  if (Array.isArray(identitiesData)) {
    -    expect(identitiesData.length).toBeGreaterThanOrEqual(0);
    -    // Validate each identity using the single identity validator
    -    identitiesData.forEach(identity => {
    -      validateIdentityResult(JSON.stringify(identity));
    -    });
    -  } else {
    -    // Single identity - use the existing validator
    -    validateIdentityResult(JSON.stringify(identitiesData));
    -  }
    -}
    -
    -function validateBalancesResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const balancesData = JSON.parse(resultStr);
    -  expect(balancesData).toBeDefined();
    -  if (Array.isArray(balancesData)) {
    -    expect(balancesData.length).toBeGreaterThanOrEqual(0);
    -    // Validate each balance object in the array
    -    balancesData.forEach(balanceObj => {
    -      expect(balanceObj).toHaveProperty('balance');
    -    });
    -  }
    -}
    -
    -function validateBalanceAndRevisionResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const data = JSON.parse(resultStr);
    -  expect(data).toBeDefined();
    -  expect(data).toBeInstanceOf(Object);
    -  expect(data).toHaveProperty('balance');
    -  expect(data).toHaveProperty('revision');
    -}
    -
    -function validateTokenBalanceResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const tokenData = JSON.parse(resultStr);
    -  expect(tokenData).toBeDefined();
    -  tokenData.forEach(token => {
    -    expect(token).toHaveProperty('balance');
    -  });
    -}
    -
    -function validateTokenInfoResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const tokenInfoData = JSON.parse(resultStr);
    -  expect(tokenInfoData).toBeDefined();
    -  tokenInfoData.forEach(token => {
    -    expect(token).toHaveProperty('isFrozen');
    -  });
    -}
    -
    -test.describe('WASM SDK Query Execution Tests', () => {
    -  let wasmSdkPage;
    -  let parameterInjector;
    -
    -  test.beforeEach(async ({ page }) => {
    -    wasmSdkPage = new WasmSdkPage(page);
    -    parameterInjector = new ParameterInjector(wasmSdkPage);
    -    await wasmSdkPage.initialize('testnet');
    -  });
    -
    -  test.describe('Data Contract Queries', () => {
    -    test('should execute getDataContract query', async () => {
    -      await wasmSdkPage.setupQuery('dataContract', 'getDataContract');
    -      
    -      const success = await parameterInjector.injectParameters('dataContract', 'getDataContract', 'testnet');
    -      expect(success).toBe(true);
    -      
    -      const result = await wasmSdkPage.executeQueryAndGetResult();
    -      
    -      // Use helper functions for validation
    -      validateBasicQueryResult(result);
    -      validateSingleView(result);
    -      validateContractResult(result.result);
    -      
    -      console.log('βœ… getDataContract single view without proof confirmed');
    -    });
    -
    -    test('should execute getDataContracts query for multiple contracts', async () => {
    -      await wasmSdkPage.setupQuery('dataContract', 'getDataContracts');
    -      
    -      const success = await parameterInjector.injectParameters('dataContract', 'getDataContracts', 'testnet');
    -      expect(success).toBe(true);
    -      
    -      const result = await wasmSdkPage.executeQueryAndGetResult();
    -      
    -      // Use helper functions for validation
    -      validateBasicQueryResult(result);
    -      validateSingleView(result);
    -      
    -      // Multiple contracts result should be valid JSON
    -      expect(() => JSON.parse(result.result)).not.toThrow();
    -      const contractsData = JSON.parse(result.result);
    -      expect(contractsData).toBeDefined();
    -      expect(contractsData).toHaveProperty('dataContracts');
    -      expect(typeof contractsData.dataContracts).toBe('object');
    -      
    -      // Validate each contract using validateContractResult
    -      Object.values(contractsData.dataContracts).forEach(contract => {
    -        validateContractResult(JSON.stringify(contract));
    -      });
    -      
    -      console.log('βœ… getDataContracts single view without proof confirmed');
    -    });
    -
    -    test('should execute getDataContractHistory query', async () => {
    -      await wasmSdkPage.setupQuery('dataContract', 'getDataContractHistory');
    -      
    -      const success = await parameterInjector.injectParameters('dataContract', 'getDataContractHistory', 'testnet');
    -      expect(success).toBe(true);
    -      
    -      const result = await wasmSdkPage.executeQueryAndGetResult();
    -      
    -      // Use helper functions for validation
    -      validateBasicQueryResult(result);
    -      validateSingleView(result);
    -      
    -      // Contract history should be valid JSON (array of contract versions)
    -      expect(() => JSON.parse(result.result)).not.toThrow();
    -      const historyData = JSON.parse(result.result);
    -      expect(historyData).toBeDefined();
    -      expect(Array.isArray(historyData) || typeof historyData === 'object').toBe(true);
    -      
    -      console.log('βœ… getDataContractHistory single view without proof confirmed');
    -    });
    -
    -    test('should execute getDataContract query with proof info', async () => {
    -      const { result, proofEnabled } = await executeQueryWithProof(
    -        wasmSdkPage, 
    -        parameterInjector, 
    -        'dataContract', 
    -        'getDataContract',
    -        'testnet'
    -      );
    -      
    -      // Validate basic result
    -      validateBasicQueryResult(result);
    -      validateContractResult(result.result);
    -      
    -      // If proof was enabled, verify split view
    -      if (proofEnabled) {
    -        validateSplitView(result);
    -        console.log('βœ… getDataContract split view with proof confirmed');
    -      } else {
    -        console.log('⚠️ Proof was not enabled for getDataContract query');
    -      }
    -    });
    -
    -    test('should execute getDataContracts query with proof info', async () => {
    -      const { result, proofEnabled } = await executeQueryWithProof(
    -        wasmSdkPage, 
    -        parameterInjector, 
    -        'dataContract', 
    -        'getDataContracts',
    -        'testnet'
    -      );
    -      
    -      // Validate basic result
    -      validateBasicQueryResult(result);
    -      
    -      // Multiple contracts result should be valid JSON
    -      expect(() => JSON.parse(result.result)).not.toThrow();
    -      const contractsData = JSON.parse(result.result);
    -      expect(contractsData).toBeDefined();
    -      expect(contractsData).toHaveProperty('dataContracts');
    -      expect(typeof contractsData.dataContracts).toBe('object');
    -      
    -      // Validate each contract using validateContractResult
    -      Object.values(contractsData.dataContracts).forEach(contract => {
    -        validateContractResult(JSON.stringify(contract));
    -      });
    -      
    -      // If proof was enabled, verify split view
    -      if (proofEnabled) {
    -        validateSplitView(result);
    -        console.log('βœ… getDataContracts split view with proof confirmed');
    -      } else {
    -        console.log('⚠️ Proof was not enabled for getDataContracts query');
    -      }
    -    });
    -
    -    test('should execute getDataContractHistory query with proof info', async () => {
    -      const { result, proofEnabled } = await executeQueryWithProof(
    -        wasmSdkPage, 
    -        parameterInjector, 
    -        'dataContract', 
    -        'getDataContractHistory',
    -        'testnet'
    -      );
    -      
    -      // Validate basic result
    -      validateBasicQueryResult(result);
    -      
    -      // Contract history should be valid JSON
    -      expect(() => JSON.parse(result.result)).not.toThrow();
    -      const historyData = JSON.parse(result.result);
    -      expect(historyData).toBeDefined();
    -      expect(Array.isArray(historyData) || typeof historyData === 'object').toBe(true);
    -      
    -      // If proof was enabled, verify split view
    -      if (proofEnabled) {
    -        validateSplitView(result);
    -        console.log('βœ… getDataContractHistory split view with proof confirmed');
    -      } else {
    -        console.log('⚠️ Proof was not enabled for getDataContractHistory query');
    -      }
    -    });
    -  });
    -
    -  test.describe('Document Queries', () => {
    -    test('should execute getDocuments query', async () => {
    -      await wasmSdkPage.setupQuery('document', 'getDocuments');
    -      
    -      const success = await parameterInjector.injectParameters('document', 'getDocuments', 'testnet');
    -      expect(success).toBe(true);
    -      
    -      const result = await wasmSdkPage.executeQueryAndGetResult();
    -      
    -      // Use helper functions for validation
    -      validateBasicQueryResult(result);
    -      validateSingleView(result);
    -      validateDocumentResult(result.result);
    -      
    -      console.log('βœ… getDocuments single view without proof confirmed');
    -    });
    -
    -    test('should execute getDocument query for specific document', async () => {
    -      await wasmSdkPage.setupQuery('document', 'getDocument');
    -      
    -      const success = await parameterInjector.injectParameters('document', 'getDocument', 'testnet');
    -      expect(success).toBe(true);
    -      
    -      const result = await wasmSdkPage.executeQueryAndGetResult();
    -      
    -      // Use helper functions for validation
    -      validateBasicQueryResult(result);
    -      validateSingleView(result);
    -      validateDocumentResult(result.result);
    -      
    -      console.log('βœ… getDocument single view without proof confirmed');
    -    });
    -
    -    test('should execute getDocuments query with proof info', async () => {
    -      const { result, proofEnabled } = await executeQueryWithProof(
    -        wasmSdkPage, 
    -        parameterInjector, 
    -        'document', 
    -        'getDocuments',
    -        'testnet'
    -      );
    -      
    -      // Validate basic result
    -      validateBasicQueryResult(result);
    -      validateDocumentResult(result.result);
    -      
    -      // If proof was enabled, verify split view
    -      if (proofEnabled) {
    -        validateSplitView(result);
    -        console.log('βœ… getDocuments split view with proof confirmed');
    -      } else {
    -        console.log('⚠️ Proof was not enabled for getDocuments query');
    -      }
    -    });
    -
    -    test('should execute getDocument query with proof info', async () => {
    -      const { result, proofEnabled } = await executeQueryWithProof(
    -        wasmSdkPage, 
    -        parameterInjector, 
    -        'document', 
    -        'getDocument',
    -        'testnet'
    -      );
    -      
    -      // Validate basic result
    -      validateBasicQueryResult(result);
    -      validateDocumentResult(result.result);
    -      
    -      // If proof was enabled, verify split view
    -      if (proofEnabled) {
    -        validateSplitView(result);
    -        console.log('βœ… getDocument split view with proof confirmed');
    -      } else {
    -        console.log('⚠️ Proof was not enabled for getDocument query');
    -      }
    -    });
    -  });
    -
    -  test.describe('System Queries', () => {
    -    const systemQueries = [
    -      { 
    -        name: 'getStatus', 
    -        hasProofSupport: false, // No proof function in WASM-SDK
    -        needsParameters: false,
    -        validateFn: (result) => {
    -          expect(result).toBeDefined();
    -          expect(Object.keys(JSON.parse(result))).toEqual(expect.arrayContaining([
    -            'version', 'node', 'chain', 'network', 'stateSync', 'time'
    -          ]));
    -        }
    -      },
    -      { 
    -        name: 'getTotalCreditsInPlatform', 
    -        hasProofSupport: true, 
    -        needsParameters: false,
    -        validateFn: (result) => {
    -          expect(result).toBeDefined();
    -          expect(result).toMatch(/\d+|credits|balance/i);
    -        }
    -      },
    -      { 
    -        name: 'getCurrentQuorumsInfo', 
    -        hasProofSupport: false, // No proof function in WASM-SDK 
    -        needsParameters: false,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const quorumsData = JSON.parse(result);
    -          expect(quorumsData).toBeDefined();
    -          expect(quorumsData).toHaveProperty('quorums');
    -          expect(Array.isArray(quorumsData.quorums)).toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getPrefundedSpecializedBalance', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const balanceData = JSON.parse(result);
    -          expect(balanceData).toBeDefined();
    -          expect(balanceData).toHaveProperty('identityId');
    -          expect(balanceData).toHaveProperty('balance');
    -        }
    -      }
    -    ];
    -
    -    systemQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('system', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('system', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'system', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  test.describe('Epoch & Block Queries', () => {
    -    const epochQueries = [
    -      { 
    -        name: 'getCurrentEpoch', 
    -        hasProofSupport: true, 
    -        needsParameters: false,
    -        validateFn: (result) => {
    -          expect(result).toBeDefined();
    -          expect(result).toMatch(/\d+|epoch/i);
    -        }
    -      },
    -      { 
    -        name: 'getEpochsInfo', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const epochData = JSON.parse(result);
    -          expect(epochData).toBeDefined();
    -          expect(typeof epochData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getFinalizedEpochInfos', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const epochData = JSON.parse(result);
    -          expect(epochData).toBeDefined();
    -          expect(typeof epochData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getEvonodesProposedEpochBlocksByIds', 
    -        hasProofSupport: false, // Proof support not yet implemented in WASM-SDK
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const blockData = JSON.parse(result);
    -          expect(blockData).toBeDefined();
    -          expect(typeof blockData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getEvonodesProposedEpochBlocksByRange', 
    -        hasProofSupport: false, // Proof support not yet implemented in WASM-SDK
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const blockData = JSON.parse(result);
    -          expect(blockData).toBeDefined();
    -          expect(typeof blockData === 'object').toBe(true);
    -        }
    -      }
    -    ];
    -
    -    epochQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('epoch', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('epoch', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'epoch', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  test.describe('Token Queries', () => {
    -    const tokenQueries = [
    -      { 
    -        name: 'getTokenStatuses', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const tokenStatuses = JSON.parse(result);
    -          expect(tokenStatuses).toBeDefined();
    -          expect(Array.isArray(tokenStatuses)).toBe(true);
    -          tokenStatuses.forEach(token => {
    -            expect(token).toHaveProperty('isPaused');
    -            expect(typeof token.isPaused).toBe('boolean');
    -          });
    -        }
    -      },
    -      { 
    -        name: 'getTokenDirectPurchasePrices', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const priceData = JSON.parse(result);
    -          expect(priceData).toBeDefined();
    -          expect(Array.isArray(priceData)).toBe(true);
    -          priceData.forEach(token => {
    -            expect(token).toHaveProperty('basePrice');
    -          });
    -        }
    -      },
    -      { 
    -        name: 'getTokenContractInfo', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const contractInfo = JSON.parse(result);
    -          expect(contractInfo).toBeDefined();
    -          expect(typeof contractInfo === 'object').toBe(true);
    -          expect(contractInfo).toHaveProperty('contractId');
    -        }
    -      },
    -      { 
    -        name: 'getTokenPerpetualDistributionLastClaim', 
    -        hasProofSupport: false, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const claimData = JSON.parse(result);
    -          expect(claimData).toBeDefined();
    -          expect(typeof claimData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getTokenTotalSupply', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const supplyData = JSON.parse(result);
    -          expect(supplyData).toBeDefined();
    -          expect(typeof supplyData === 'object').toBe(true);
    -          expect(supplyData).toHaveProperty('totalSupply');
    -        }
    -      }
    -    ];
    -
    -    tokenQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('token', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('token', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'token', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  test.describe('Voting & Contested Resources Queries', () => {
    -    const votingQueries = [
    -      { 
    -        name: 'getContestedResources', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const contestedData = JSON.parse(result);
    -          expect(contestedData).toBeDefined();
    -          expect(typeof contestedData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getContestedResourceVoteState', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const voteStateData = JSON.parse(result);
    -          expect(voteStateData).toBeDefined();
    -          expect(typeof voteStateData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getContestedResourceVotersForIdentity', 
    -        hasProofSupport: true,
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const votersData = JSON.parse(result);
    -          expect(votersData).toBeDefined();
    -          expect(typeof votersData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getContestedResourceIdentityVotes', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const identityVotesData = JSON.parse(result);
    -          expect(identityVotesData).toBeDefined();
    -          expect(typeof identityVotesData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getVotePollsByEndDate', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const pollsData = JSON.parse(result);
    -          expect(pollsData).toBeDefined();
    -          expect(typeof pollsData === 'object').toBe(true);
    -        }
    -      }
    -    ];
    -
    -    votingQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('voting', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('voting', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'voting', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  test.describe('Group Queries', () => {
    -    const groupQueries = [
    -      { 
    -        name: 'getGroupInfo', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const groupInfo = JSON.parse(result);
    -          expect(groupInfo).toBeDefined();
    -          expect(typeof groupInfo === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getGroupInfos', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const groupInfos = JSON.parse(result);
    -          expect(groupInfos).toBeDefined();
    -          expect(typeof groupInfos === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getGroupActions', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const groupActions = JSON.parse(result);
    -          expect(groupActions).toBeDefined();
    -          expect(typeof groupActions === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'getGroupActionSigners', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const actionSigners = JSON.parse(result);
    -          expect(actionSigners).toBeDefined();
    -          expect(typeof actionSigners === 'object').toBe(true);
    -        }
    -      }
    -    ];
    -
    -    groupQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('group', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('group', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'group', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  test.describe('Error Handling', () => {
    -    test('should handle invalid identity ID gracefully', async () => {
    -      await wasmSdkPage.setupQuery('identity', 'getIdentity');
    -      
    -      // Fill with invalid ID (contains invalid base58 characters '0', 'O', 'I', 'l')
    -      await wasmSdkPage.fillQueryParameters({ id: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4SOIl0' });
    -      
    -      // Click execute button directly
    -      const executeButton = wasmSdkPage.page.locator('#executeQuery');
    -      await executeButton.click();
    -      
    -      // Wait a bit for the error to appear
    -      await wasmSdkPage.page.waitForTimeout(1000);
    -      
    -      // Check for error status
    -      const statusBanner = wasmSdkPage.page.locator('#statusBanner');
    -      const statusClass = await statusBanner.getAttribute('class');
    -      const statusText = await wasmSdkPage.getStatusBannerText();
    -      
    -      // Should show error
    -      expect(statusClass).toContain('error');
    -      expect(statusText).toBeTruthy();
    -      
    -      console.log('Error handling result:', statusText);
    -    });
    -
    -    test('should handle empty required fields', async () => {
    -      await wasmSdkPage.setupQuery('identity', 'getIdentity');
    -      
    -      // Don't fill any parameters, try to execute
    -      const executeButton = wasmSdkPage.page.locator('#executeQuery');
    -      await executeButton.click();
    -      
    -      // Wait a bit for the error to appear
    -      await wasmSdkPage.page.waitForTimeout(1000);
    -      
    -      // Check for error status
    -      const statusBanner = wasmSdkPage.page.locator('#statusBanner');
    -      const statusClass = await statusBanner.getAttribute('class');
    -      const statusText = await wasmSdkPage.getStatusBannerText();
    -      
    -      // Should show error or validation message
    -      expect(statusClass).toContain('error');
    -      expect(statusText).toContain('required');
    -      
    -      console.log('Empty fields handling:', statusText);
    -    });
    -  });
    -
    -
    -  test.describe('Network Switching', () => {
    -    test('should execute queries on mainnet', async () => {
    -      // Switch to mainnet
    -      await wasmSdkPage.setNetwork('mainnet');
    -      
    -      await wasmSdkPage.setupQuery('system', 'getStatus');
    -      
    -      const result = await wasmSdkPage.executeQueryAndGetResult();
    -      
    -      // Verify query executed successfully
    -      expect(result.success).toBe(true);
    -      expect(result.result).toBeDefined();
    -      
    -      // Verify the result is not an error message
    -      expect(result.hasError).toBe(false);
    -      expect(result.result).not.toContain('Error executing query');
    -      expect(result.result).not.toContain('not found');
    -      
    -      // Should contain status data with version info
    -      expect(result.result).toContain('version');
    -      
    -    });
    -  });
    -
    -  test.describe('Protocol & Version Queries', () => {
    -    const protocolQueries = [
    -      { 
    -        name: 'getProtocolVersionUpgradeState', 
    -        hasProofSupport: true, 
    -        needsParameters: false,
    -        validateFn: (result) => {
    -          expect(result).toBeDefined();
    -          expect(result).toContain('currentProtocolVersion');
    -        }
    -      },
    -      { 
    -        name: 'getProtocolVersionUpgradeVoteStatus', 
    -        hasProofSupport: false, // Proof support not yet implemented in WASM-SDK
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const voteData = JSON.parse(result);
    -          expect(voteData).toBeDefined();
    -          expect(typeof voteData === 'object').toBe(true);
    -        }
    -      }
    -    ];
    -
    -    protocolQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('protocol', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('protocol', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'protocol', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  test.describe('DPNS Queries', () => {
    -    const dpnsQueries = [
    -      { 
    -        name: 'getDpnsUsername', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const usernameData = JSON.parse(result);
    -          expect(usernameData).toBeDefined();
    -          if (Array.isArray(usernameData)) {
    -            expect(usernameData.length).toBeGreaterThanOrEqual(1);
    -          }
    -        }
    -      },
    -      { 
    -        name: 'dpnsCheckAvailability', 
    -        hasProofSupport: false,  // Proof support not yet implemented in WASM-SDK
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const availabilityData = JSON.parse(result);
    -          expect(availabilityData).toBeDefined();
    -          expect(typeof availabilityData === 'boolean' || typeof availabilityData === 'object').toBe(true);
    -        }
    -      },
    -      { 
    -        name: 'dpnsResolve', 
    -        hasProofSupport: false,  // Proof support not yet implemented in WASM-SDK
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const resolveData = JSON.parse(result);
    -          // Check for either successful resolution (has name) or error response
    -          if (resolveData && typeof resolveData === 'object') {
    -            // Valid response structure - may or may not have 'name' depending on resolution success
    -            expect(resolveData).toBeDefined();
    -          }
    -        }
    -      },
    -      { 
    -        name: 'dpnsSearch', 
    -        hasProofSupport: true, 
    -        needsParameters: true,
    -        validateFn: (result) => {
    -          expect(() => JSON.parse(result)).not.toThrow();
    -          const searchData = JSON.parse(result);
    -          expect(searchData).toBeDefined();
    -          if (Array.isArray(searchData)) {
    -            expect(searchData.length).toBeGreaterThanOrEqual(0);
    -            searchData.forEach(result => {
    -              expect(result).toHaveProperty('username');
    -            });
    -          }
    -        }
    -      }
    -    ];
    -
    -    dpnsQueries.forEach(({ name, hasProofSupport, needsParameters, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('dpns', name);
    -          
    -          if (needsParameters) {
    -            const success = await parameterInjector.injectParameters('dpns', name, 'testnet');
    -            expect(success).toBe(true);
    -          }
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicDpnsQueryResult(result);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} single view without proof confirmed`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'dpns', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicDpnsQueryResult(result);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              console.log(`βœ… ${name} split view with proof confirmed`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -            }
    -            
    -            validateFn(result.result);
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -
    -  // Test Identity Queries
    -  test.describe('Identity Queries', () => {
    -    // Complete set of all available identity queries with correct proof support
    -    const testQueries = [
    -      { name: 'getIdentity', hasProofSupport: true, validateFn: validateIdentityResult },
    -      { name: 'getIdentityBalance', hasProofSupport: true, validateFn: (result) => validateNumericResult(result, 'balance') },
    -      { name: 'getIdentityKeys', hasProofSupport: true, validateFn: validateKeysResult },
    -      { name: 'getIdentityNonce', hasProofSupport: true, validateFn: (result) => validateNumericResult(result, 'nonce') },
    -      { name: 'getIdentityContractNonce', hasProofSupport: true, validateFn: (result) => validateNumericResult(result, 'nonce') },
    -      { name: 'getIdentityByPublicKeyHash', hasProofSupport: true, validateFn: validateIdentityResult },
    -      { name: 'getIdentitiesContractKeys', hasProofSupport: true, validateFn: validateIdentitiesContractKeysResult },
    -      { name: 'getIdentitiesBalances', hasProofSupport: true, validateFn: validateBalancesResult },
    -      { name: 'getIdentityBalanceAndRevision', hasProofSupport: true, validateFn: validateBalanceAndRevisionResult },
    -      { name: 'getIdentityByNonUniquePublicKeyHash', hasProofSupport: true, validateFn: validateIdentitiesResult },
    -      { name: 'getIdentityTokenBalances', hasProofSupport: true, validateFn: validateTokenBalanceResult },
    -      { name: 'getIdentitiesTokenBalances', hasProofSupport: true, validateFn: validateTokenBalanceResult },
    -      { name: 'getIdentityTokenInfos', hasProofSupport: true, validateFn: validateTokenInfoResult },
    -      { name: 'getIdentitiesTokenInfos', hasProofSupport: true, validateFn: validateTokenInfoResult }
    -    ];
    -
    -    testQueries.forEach(({ name, hasProofSupport, validateFn }) => {
    -      test.describe(`${name} query (parameterized)`, () => {
    -        test('without proof info', async () => {
    -          await wasmSdkPage.setupQuery('identity', name);
    -          await wasmSdkPage.disableProofInfo();
    -          
    -          const success = await parameterInjector.injectParameters('identity', name, 'testnet');
    -          expect(success).toBe(true);
    -          
    -          const result = await wasmSdkPage.executeQueryAndGetResult();
    -          validateBasicQueryResult(result);
    -          expect(result.result.length).toBeGreaterThan(0);
    -          validateSingleView(result);
    -          validateFn(result.result);
    -          
    -          console.log(`βœ… ${name} without proof - PASSED`);
    -        });
    -
    -        if (hasProofSupport) {
    -          test('with proof info', async () => {
    -            const { result, proofEnabled } = await executeQueryWithProof(
    -              wasmSdkPage, 
    -              parameterInjector, 
    -              'identity', 
    -              name,
    -              'testnet'
    -            );
    -            
    -            validateBasicQueryResult(result);
    -            expect(result.result.length).toBeGreaterThan(0);
    -            
    -            if (proofEnabled) {
    -              validateSplitView(result);
    -              validateFn(result.result);
    -              console.log(`βœ… ${name} with proof - PASSED`);
    -            } else {
    -              console.log(`⚠️ Proof was not enabled for ${name} query`);
    -              validateFn(result.result);
    -            }
    -          });
    -        } else {
    -          test.skip('with proof info', async () => {
    -            // Proof support not yet implemented in WASM SDK for this query
    -          });
    -        }
    -      });
    -    });
    -  });
    -});
    diff --git a/packages/wasm-sdk/test/ui-automation/tests/state-transitions.spec.js b/packages/wasm-sdk/test/ui-automation/tests/state-transitions.spec.js
    deleted file mode 100644
    index f0f80a0841a..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/tests/state-transitions.spec.js
    +++ /dev/null
    @@ -1,1474 +0,0 @@
    -const { test, expect } = require('@playwright/test');
    -const { WasmSdkPage } = require('../utils/wasm-sdk-page');
    -const { ParameterInjector } = require('../utils/parameter-injector');
    -
    -/**
    - * Helper function to execute a state transition
    - * @param {WasmSdkPage} wasmSdkPage - The page object instance
    - * @param {ParameterInjector} parameterInjector - The parameter injector instance
    - * @param {string} category - State transition category (e.g., 'identity', 'dataContract')
    - * @param {string} transitionType - Transition type (e.g., 'identityCreate')
    - * @param {string} network - Network to use ('testnet' or 'mainnet')
    - * @returns {Promise} - The transition result object
    - */
    -async function executeStateTransition(wasmSdkPage, parameterInjector, category, transitionType, network = 'testnet') {
    -  await wasmSdkPage.setupStateTransition(category, transitionType);
    -
    -  const success = await parameterInjector.injectStateTransitionParameters(category, transitionType, network);
    -  expect(success).toBe(true);
    -
    -  const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -  return result;
    -}
    -
    -/**
    - * Helper function to validate basic state transition result properties
    - * @param {Object} result - The state transition result object
    - */
    -function validateBasicStateTransitionResult(result) {
    -  // Check for withdrawal-specific minimum amount error
    -  if (!result.success && result.result && result.result.includes('Missing response message')) {
    -    console.error('⚠️  Withdrawal may have failed due to insufficient amount. Minimum withdrawal is ~190,000 credits.');
    -    console.error('Full error:', result.result);
    -  }
    -
    -  expect(result.success).toBe(true);
    -  expect(result.result).toBeDefined();
    -  expect(result.hasError).toBe(false);
    -  expect(result.result).not.toContain('Error executing');
    -  expect(result.result).not.toContain('invalid');
    -  expect(result.result).not.toContain('failed');
    -}
    -
    -/**
    - * Filter out placeholder options from dropdown arrays
    - * @param {string[]} options - Array of dropdown options
    - * @returns {string[]} - Filtered array without placeholders
    - */
    -function filterPlaceholderOptions(options) {
    -  return options.filter(option =>
    -    !option.toLowerCase().includes('select') &&
    -    option.trim() !== ''
    -  );
    -}
    -
    -/**
    - * Parse and validate JSON response structure
    - * @param {string} resultStr - The raw result string
    - * @returns {Object} - The parsed contract data
    - */
    -function parseContractResponse(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const contractData = JSON.parse(resultStr);
    -  expect(contractData).toBeDefined();
    -  expect(contractData).toBeInstanceOf(Object);
    -  expect(contractData.status).toBe('success');
    -  expect(contractData.contractId).toBeDefined();
    -  expect(contractData.version).toBeDefined();
    -  expect(typeof contractData.version).toBe('number');
    -  expect(contractData.message).toBeDefined();
    -  return contractData;
    -}
    -
    -/**
    - * Helper function to validate data contract result (both create and update)
    - * @param {string} resultStr - The raw result string from data contract operation
    - * @param {boolean} isUpdate - Whether this is an update operation (default: false for create)
    - * @returns {Object} - The parsed contract data for further use
    - */
    -function validateDataContractResult(resultStr, isUpdate = false) {
    -  const contractData = parseContractResponse(resultStr);
    -
    -  // Conditional validations based on operation type
    -  if (isUpdate) {
    -    // Update: only has version and message specifics
    -    expect(contractData.version).toBeGreaterThan(1); // Updates should increment version
    -    expect(contractData.message).toContain('updated successfully');
    -  } else {
    -    // Create: has additional fields that updates don't have
    -    expect(contractData.ownerId).toBeDefined();
    -    expect(contractData.documentTypes).toBeDefined();
    -    expect(Array.isArray(contractData.documentTypes)).toBe(true);
    -    expect(contractData.version).toBe(1); // Creates start at version 1
    -    expect(contractData.message).toContain('created successfully');
    -  }
    -
    -  return contractData;
    -}
    -
    -/**
    - * Helper function to validate document creation result
    - * @param {string} resultStr - The raw result string from document creation
    - */
    -function validateDocumentCreateResult(resultStr) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const documentResponse = JSON.parse(resultStr);
    -  expect(documentResponse).toBeDefined();
    -  expect(documentResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for document creation
    -  expect(documentResponse.type).toBe('DocumentCreated');
    -  expect(documentResponse.documentId).toBeDefined();
    -  expect(typeof documentResponse.documentId).toBe('string');
    -  expect(documentResponse.documentId.length).toBeGreaterThan(0);
    -
    -  // Validate the document object
    -  expect(documentResponse.document).toBeDefined();
    -  expect(documentResponse.document.id).toBe(documentResponse.documentId);
    -  expect(documentResponse.document.ownerId).toBeDefined();
    -  expect(documentResponse.document.dataContractId).toBeDefined();
    -  expect(documentResponse.document.documentType).toBeDefined();
    -  expect(documentResponse.document.revision).toBe(1); // New documents start at revision 1
    -  expect(documentResponse.document.data).toBeDefined();
    -  expect(typeof documentResponse.document.data).toBe('object');
    -
    -  return documentResponse;
    -}
    -
    -/**
    - * Helper function to validate document replace result
    - * @param {string} resultStr - The raw result string from document replacement
    - * @param {string} expectedDocumentId - Expected document ID to validate against
    - * @param {number} expectedMinRevision - Minimum expected revision (should be > 1)
    - */
    -function validateDocumentReplaceResult(resultStr, expectedDocumentId, expectedMinRevision = 2) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const replaceResponse = JSON.parse(resultStr);
    -  expect(replaceResponse).toBeDefined();
    -  expect(replaceResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for document replacement
    -  expect(replaceResponse.type).toBe('DocumentReplaced');
    -  expect(replaceResponse.documentId).toBe(expectedDocumentId);
    -  expect(replaceResponse.document).toBeDefined();
    -
    -  // Validate the document object matches the expected structure
    -  expect(replaceResponse.document.id).toBe(expectedDocumentId);
    -  expect(replaceResponse.document.ownerId).toBeDefined();
    -  expect(replaceResponse.document.dataContractId).toBeDefined();
    -  expect(replaceResponse.document.documentType).toBeDefined();
    -  expect(replaceResponse.document.revision).toBeGreaterThanOrEqual(expectedMinRevision);
    -  expect(replaceResponse.document.data).toBeDefined();
    -  expect(typeof replaceResponse.document.data).toBe('object');
    -
    -  console.log(`βœ… Confirmed replacement of document: ${expectedDocumentId} (revision: ${replaceResponse.document.revision})`);
    -
    -  return replaceResponse;
    -}
    -
    -/**
    - * Helper function to validate document deletion result
    - * @param {string} resultStr - The raw result string from document deletion
    - * @param {string} expectedDocumentId - Optional expected document ID to validate against
    - */
    -function validateDocumentDeleteResult(resultStr, expectedDocumentId = null) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const deleteResponse = JSON.parse(resultStr);
    -  expect(deleteResponse).toBeDefined();
    -  expect(deleteResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for document deletion
    -  expect(deleteResponse.type).toBe('DocumentDeleted');
    -  expect(deleteResponse.documentId).toBeDefined();
    -  expect(typeof deleteResponse.documentId).toBe('string');
    -  expect(deleteResponse.documentId.length).toBeGreaterThan(0);
    -  expect(deleteResponse.deleted).toBe(true);
    -
    -  // If expectedDocumentId is provided, verify it matches the response
    -  if (expectedDocumentId) {
    -    expect(deleteResponse.documentId).toBe(expectedDocumentId);
    -    console.log(`Confirmed deletion of correct document: ${expectedDocumentId}`);
    -  }
    -
    -  return deleteResponse;
    -}
    -
    -/**
    - * Helper function to validate identity credit transfer result
    - * @param {string} resultStr - The raw result string from identity credit transfer
    - * @param {string} expectedSenderId - Expected sender identity ID
    - * @param {string} expectedRecipientId - Expected recipient identity ID
    - * @param {number} expectedAmount - Expected transfer amount
    - */
    -function validateIdentityCreditTransferResult(resultStr, expectedSenderId, expectedRecipientId, expectedAmount) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const transferResponse = JSON.parse(resultStr);
    -  expect(transferResponse).toBeDefined();
    -  expect(transferResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for identity credit transfer
    -  expect(transferResponse.status).toBe('success');
    -  expect(transferResponse.senderId).toBe(expectedSenderId);
    -  expect(transferResponse.recipientId).toBe(expectedRecipientId);
    -  expect(transferResponse.amount).toBe(expectedAmount);
    -  expect(transferResponse.message).toBeDefined();
    -
    -  console.log(`βœ… Confirmed credit transfer: ${expectedAmount} credits from ${expectedSenderId} to ${expectedRecipientId}`);
    -
    -  return transferResponse;
    -}
    -
    -/**
    - * Helper function to validate identity credit withdrawal result
    - * @param {string} resultStr - The raw result string from identity credit withdrawal
    - * @param {string} expectedIdentityId - Expected identity ID
    - * @param {string} expectedWithdrawalAddress - Expected withdrawal address
    - * @param {number} expectedAmount - Expected withdrawal amount
    - */
    -function validateIdentityCreditWithdrawalResult(resultStr, expectedIdentityId, expectedWithdrawalAddress, expectedAmount) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const withdrawalResponse = JSON.parse(resultStr);
    -  expect(withdrawalResponse).toBeDefined();
    -  expect(withdrawalResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for identity credit withdrawal
    -  expect(withdrawalResponse.status).toBe('success');
    -  expect(withdrawalResponse.identityId).toBe(expectedIdentityId);
    -  expect(withdrawalResponse.toAddress).toBe(expectedWithdrawalAddress);
    -  expect(withdrawalResponse.amount).toBeDefined(); // Amount might be different due to fees
    -  expect(withdrawalResponse.remainingBalance).toBeDefined();
    -  expect(withdrawalResponse.message).toContain('withdrawn successfully');
    -
    -  console.log(`βœ… Confirmed credit withdrawal: ${withdrawalResponse.amount} credits from ${expectedIdentityId} to ${expectedWithdrawalAddress}`);
    -
    -  return withdrawalResponse;
    -}
    -
    -/**
    - * Helper function to validate token mint result
    - * @param {string} resultStr - The raw result string from token mint
    - * @param {string} expectedIdentityId - Expected identity ID
    - * @param {string} expectedAmount - Expected mint amount
    - */
    -function validateTokenMintResult(resultStr, expectedIdentityId, expectedAmount) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const mintResponse = JSON.parse(resultStr);
    -  expect(mintResponse).toBeDefined();
    -  expect(mintResponse).toBeInstanceOf(Object);
    -
    -  // Token mint returns an empty object {} on success
    -  // This indicates the transaction was submitted successfully
    -  console.log(`βœ… Token mint transaction submitted successfully: ${expectedAmount} tokens to ${expectedIdentityId}`);
    -
    -  return mintResponse;
    -}
    -
    -/**
    - * Helper function to validate token transfer result
    - * @param {string} resultStr - The raw result string from token transfer
    - * @param {string} expectedSenderId - Expected sender identity ID
    - * @param {string} expectedRecipientId - Expected recipient identity ID
    - * @param {string} expectedAmount - Expected transfer amount
    - */
    -function validateTokenTransferResult(resultStr, expectedSenderId, expectedRecipientId, expectedAmount) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const transferResponse = JSON.parse(resultStr);
    -  expect(transferResponse).toBeDefined();
    -  expect(transferResponse).toBeInstanceOf(Object);
    -
    -  // Token transfer returns an empty object {} on success
    -  // This indicates the transaction was submitted successfully
    -  console.log(`βœ… Token transfer transaction submitted successfully: ${expectedAmount} tokens from ${expectedSenderId} to ${expectedRecipientId}`);
    -
    -  return transferResponse;
    -}
    -
    -/**
    - * Helper function to validate token burn result
    - * @param {string} resultStr - The raw result string from token burn
    - * @param {string} expectedIdentityId - Expected identity ID
    - * @param {string} expectedAmount - Expected burn amount
    - */
    -function validateTokenBurnResult(resultStr, expectedIdentityId, expectedAmount) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const burnResponse = JSON.parse(resultStr);
    -  expect(burnResponse).toBeDefined();
    -  expect(burnResponse).toBeInstanceOf(Object);
    -
    -  // Token burn returns an empty object {} on success
    -  // This indicates the transaction was submitted successfully
    -  console.log(`βœ… Token burn transaction submitted successfully: ${expectedAmount} tokens burned from ${expectedIdentityId}`);
    -
    -  return burnResponse;
    -}
    -
    -/**
    - * Helper function to validate token freeze result
    - * @param {string} resultStr - The raw result string from token freeze
    - * @param {string} expectedIdentityId - Expected identity ID to freeze
    - */
    -function validateTokenFreezeResult(resultStr, expectedIdentityId) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const freezeResponse = JSON.parse(resultStr);
    -  expect(freezeResponse).toBeDefined();
    -  expect(freezeResponse).toBeInstanceOf(Object);
    -
    -  // Token freeze returns an empty object {} on success
    -  console.log(`βœ… Token freeze transaction submitted successfully for identity: ${expectedIdentityId}`);
    -
    -  return freezeResponse;
    -}
    -
    -/**
    - * Helper function to validate token destroy frozen result
    - * @param {string} resultStr - The raw result string from token destroy frozen
    - * @param {string} expectedIdentityId - Expected identity ID with frozen tokens
    - */
    -function validateTokenDestroyFrozenResult(resultStr, expectedIdentityId) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const destroyResponse = JSON.parse(resultStr);
    -  expect(destroyResponse).toBeDefined();
    -  expect(destroyResponse).toBeInstanceOf(Object);
    -
    -  // Token destroy frozen returns an empty object {} on success
    -  console.log(`βœ… Token destroy frozen transaction submitted successfully: destroyed all frozen tokens from ${expectedIdentityId}`);
    -
    -  return destroyResponse;
    -}
    -
    -/**
    - * Helper function to validate token unfreeze result
    - * @param {string} resultStr - The raw result string from token unfreeze
    - * @param {string} expectedIdentityId - Expected identity ID to unfreeze
    - */
    -function validateTokenUnfreezeResult(resultStr, expectedIdentityId) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const unfreezeResponse = JSON.parse(resultStr);
    -  expect(unfreezeResponse).toBeDefined();
    -  expect(unfreezeResponse).toBeInstanceOf(Object);
    -
    -  // Token unfreeze returns an empty object {} on success
    -  console.log(`βœ… Token unfreeze transaction submitted successfully for identity: ${expectedIdentityId}`);
    -
    -  return unfreezeResponse;
    -}
    -
    -function validateTokenClaimResult(resultStr, expectedDistributionType) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const claimResponse = JSON.parse(resultStr);
    -  expect(claimResponse).toBeDefined();
    -  expect(claimResponse).toBeInstanceOf(Object);
    -
    -  // Token claim returns an empty object {} on success
    -  console.log(`βœ… Token claim transaction submitted successfully for distribution type: ${expectedDistributionType}`);
    -
    -  return claimResponse;
    -}
    -
    -function validateTokenSetPriceResult(resultStr, expectedPriceType, expectedPriceData) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const setPriceResponse = JSON.parse(resultStr);
    -  expect(setPriceResponse).toBeDefined();
    -  expect(setPriceResponse).toBeInstanceOf(Object);
    -
    -  // Token set price returns an empty object {} on success
    -  console.log(`βœ… Token set price transaction submitted successfully - Type: ${expectedPriceType}, Price: ${expectedPriceData}`);
    -
    -  return setPriceResponse;
    -}
    -
    -function validateTokenDirectPurchaseResult(resultStr, expectedAmount, expectedTotalPrice) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const purchaseResponse = JSON.parse(resultStr);
    -  expect(purchaseResponse).toBeDefined();
    -  expect(purchaseResponse).toBeInstanceOf(Object);
    -
    -  // Token direct purchase returns an empty object {} on success
    -  console.log(`βœ… Token direct purchase transaction submitted successfully - Amount: ${expectedAmount} tokens, Total price: ${expectedTotalPrice} credits`);
    -
    -  return purchaseResponse;
    -}
    -
    -function validateTokenConfigUpdateResult(resultStr, expectedConfigType, expectedConfigValue) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const configUpdateResponse = JSON.parse(resultStr);
    -  expect(configUpdateResponse).toBeDefined();
    -  expect(configUpdateResponse).toBeInstanceOf(Object);
    -
    -  // Token config update returns an empty object {} on success
    -  console.log(`βœ… Token config update transaction submitted successfully - Type: ${expectedConfigType}, Value: ${expectedConfigValue}`);
    -
    -  return configUpdateResponse;
    -}
    -
    -/**
    - * Helper function to validate document transfer result
    - * @param {string} resultStr - The raw result string from document transfer
    - * @param {string} expectedDocumentId - Expected document ID to validate against
    - * @param {string} expectedRecipientId - Expected recipient identity ID
    - */
    -function validateDocumentTransferResult(resultStr, expectedDocumentId, expectedRecipientId) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const transferResponse = JSON.parse(resultStr);
    -  expect(transferResponse).toBeDefined();
    -  expect(transferResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for document transfer
    -  expect(transferResponse.type).toBe('DocumentTransferred');
    -  expect(transferResponse.documentId).toBe(expectedDocumentId);
    -  expect(transferResponse.newOwnerId).toBe(expectedRecipientId);
    -  expect(transferResponse.transferred).toBe(true);
    -
    -  console.log(`βœ… Confirmed transfer of document: ${expectedDocumentId} to ${expectedRecipientId}`);
    -
    -  return transferResponse;
    -}
    -
    -/**
    - * Helper function to validate document set price result
    - * @param {string} resultStr - The raw result string from document set price
    - * @param {string} expectedDocumentId - Expected document ID to validate against
    - * @param {number} expectedPrice - Expected price that was set
    - */
    -function validateDocumentSetPriceResult(resultStr, expectedDocumentId, expectedPrice) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const setPriceResponse = JSON.parse(resultStr);
    -  expect(setPriceResponse).toBeDefined();
    -  expect(setPriceResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for document set price
    -  expect(setPriceResponse.type).toBe('DocumentPriceSet');
    -  expect(setPriceResponse.documentId).toBe(expectedDocumentId);
    -  expect(setPriceResponse.price).toBe(expectedPrice);
    -  expect(setPriceResponse.priceSet).toBe(true);
    -
    -  console.log(`βœ… Confirmed price set for document: ${expectedDocumentId} at ${expectedPrice} credits`);
    -
    -  return setPriceResponse;
    -}
    -
    -/**
    - * Helper function to validate document purchase result
    - * @param {string} resultStr - The raw result string from document purchase
    - * @param {string} expectedDocumentId - Expected document ID to validate against
    - * @param {string} expectedBuyerId - Expected buyer identity ID
    - * @param {number} expectedPrice - Expected purchase price
    - */
    -function validateDocumentPurchaseResult(resultStr, expectedDocumentId, expectedBuyerId, expectedPrice) {
    -  expect(() => JSON.parse(resultStr)).not.toThrow();
    -  const purchaseResponse = JSON.parse(resultStr);
    -  expect(purchaseResponse).toBeDefined();
    -  expect(purchaseResponse).toBeInstanceOf(Object);
    -
    -  // Validate the response structure for document purchase
    -  expect(purchaseResponse.type).toBe('DocumentPurchased');
    -  expect(purchaseResponse.documentId).toBe(expectedDocumentId);
    -  expect(purchaseResponse.status).toBe('success');
    -  expect(purchaseResponse.newOwnerId).toBe(expectedBuyerId);
    -  expect(purchaseResponse.pricePaid).toBe(expectedPrice);
    -  expect(purchaseResponse.message).toBe('Document purchased successfully');
    -  expect(purchaseResponse.documentUpdated).toBe(true);
    -  expect(purchaseResponse.revision).toBeDefined();
    -  expect(typeof purchaseResponse.revision).toBe('number');
    -
    -  console.log(`βœ… Confirmed purchase of document: ${expectedDocumentId} by ${expectedBuyerId} for ${expectedPrice} credits`);
    -
    -  return purchaseResponse;
    -}
    -
    -/**
    - * Execute a state transition with custom parameters
    - * @param {WasmSdkPage} wasmSdkPage - The page object instance
    - * @param {ParameterInjector} parameterInjector - The parameter injector instance
    - * @param {string} category - State transition category
    - * @param {string} transitionType - Transition type
    - * @param {string} network - Network to use
    - * @param {Object} customParams - Custom parameters to override test data
    - * @returns {Promise} - The transition result object
    - */
    -async function executeStateTransitionWithCustomParams(wasmSdkPage, parameterInjector, category, transitionType, network = 'testnet', customParams = {}) {
    -  await wasmSdkPage.setupStateTransition(category, transitionType);
    -
    -  const success = await parameterInjector.injectStateTransitionParameters(category, transitionType, network, customParams);
    -  expect(success).toBe(true);
    -
    -  const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -  return result;
    -}
    -
    -test.describe('WASM SDK State Transition Tests', () => {
    -  let wasmSdkPage;
    -  let parameterInjector;
    -
    -  test.beforeEach(async ({ page }) => {
    -    wasmSdkPage = new WasmSdkPage(page);
    -    parameterInjector = new ParameterInjector(wasmSdkPage);
    -    await wasmSdkPage.initialize('testnet');
    -  });
    -
    -  test.describe('Data Contract State Transitions', () => {
    -    test.skip('should execute data contract create transition', async () => {
    -      // Execute the data contract create transition
    -      const result = await executeStateTransition(
    -        wasmSdkPage,
    -        parameterInjector,
    -        'dataContract',
    -        'dataContractCreate',
    -        'testnet'
    -      );
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Validate data contract creation specific result
    -      validateDataContractResult(result.result, false);
    -
    -      console.log('βœ… Data contract create state transition completed successfully');
    -    });
    -
    -    test.skip('should execute data contract update transition', async () => {
    -      // Execute the data contract update transition
    -      const result = await executeStateTransition(
    -        wasmSdkPage,
    -        parameterInjector,
    -        'dataContract',
    -        'dataContractUpdate',
    -        'testnet'
    -      );
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Validate data contract update specific result
    -      validateDataContractResult(result.result, true);
    -
    -      console.log('βœ… Data contract update state transition completed successfully');
    -    });
    -
    -    test('should create data contract and then update it with author field', async () => {
    -      // Set extended timeout for combined create+update operation
    -      test.setTimeout(180000);
    -
    -      let contractId;
    -
    -      // Step 1: Create contract (reported separately)
    -      await test.step('Create data contract', async () => {
    -        console.log('Creating new data contract...');
    -        const createResult = await executeStateTransition(
    -          wasmSdkPage,
    -          parameterInjector,
    -          'dataContract',
    -          'dataContractCreate',
    -          'testnet'
    -        );
    -
    -        // Validate create result
    -        validateBasicStateTransitionResult(createResult);
    -        validateDataContractResult(createResult.result, false);
    -
    -        // Get the contract ID from create result
    -        contractId = JSON.parse(createResult.result).contractId;
    -        console.log('βœ… Data contract created with ID:', contractId);
    -      });
    -
    -      // Step 2: Update contract (reported separately)
    -      // This test is now flaky for some reason and frequently fails
    -      await test.step('Update data contract with author field', async () => {
    -        console.log('πŸ”„ Updating data contract to add author field...');
    -        const updateResult = await executeStateTransitionWithCustomParams(
    -          wasmSdkPage,
    -          parameterInjector,
    -          'dataContract',
    -          'dataContractUpdate',
    -          'testnet',
    -          { dataContractId: contractId } // Override with dynamic contract ID
    -        );
    -
    -        // Validate update result
    -        validateBasicStateTransitionResult(updateResult);
    -        validateDataContractResult(updateResult.result, true);
    -
    -        console.log('βœ… Data contract updated successfully with author field');
    -      });
    -    });
    -
    -    test('should show authentication inputs for data contract transitions', async () => {
    -      await wasmSdkPage.setupStateTransition('dataContract', 'dataContractCreate');
    -
    -      // Check that authentication inputs are visible
    -      const hasAuthInputs = await wasmSdkPage.hasAuthenticationInputs();
    -      expect(hasAuthInputs).toBe(true);
    -
    -      console.log('βœ… Data contract state transition authentication inputs are visible');
    -    });
    -  });
    -
    -  test.describe('Document State Transitions', () => {
    -    test('should execute document create transition', async () => {
    -      // Set up the document create transition manually due to special schema handling
    -      await wasmSdkPage.setupStateTransition('document', 'documentCreate');
    -
    -      // Inject basic parameters (contractId, documentType, identityId, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('document', 'documentCreate', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Step 1: Fetch document schema to generate dynamic fields
    -      await test.step('Fetch document schema', async () => {
    -        await wasmSdkPage.fetchDocumentSchema();
    -        console.log('βœ… Document schema fetched and fields generated');
    -      });
    -
    -      // Step 2: Fill document fields
    -      await test.step('Fill document fields', async () => {
    -        // Get document fields from test data
    -        const testParams = parameterInjector.testData.stateTransitionParameters.document.documentCreate.testnet[0];
    -        await wasmSdkPage.fillDocumentFields(testParams.documentFields);
    -        console.log('βœ… Document fields filled');
    -      });
    -
    -      // Step 3: Execute the transition
    -      await test.step('Execute document create', async () => {
    -        const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -        // Validate basic result structure
    -        validateBasicStateTransitionResult(result);
    -
    -        // Validate document creation specific result
    -        validateDocumentCreateResult(result.result);
    -
    -        console.log('βœ… Document create state transition completed successfully');
    -      });
    -    });
    -
    -    test('should execute document replace transition', async () => {
    -      // Set up the document replace transition
    -      await wasmSdkPage.setupStateTransition('document', 'documentReplace');
    -
    -      // Inject basic parameters (contractId, documentType, documentId, identityId, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('document', 'documentReplace', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Load the existing document to get revision and populate fields
    -      await wasmSdkPage.loadExistingDocument();
    -
    -      // Create updated message with timestamp
    -      const testParams = parameterInjector.testData.stateTransitionParameters.document.documentReplace.testnet[0];
    -      const baseMessage = testParams.documentFields.message;
    -      const timestamp = new Date().toISOString();
    -      const updatedFields = {
    -        message: `${baseMessage} - Updated at ${timestamp}`
    -      };
    -
    -      // Fill updated document fields
    -      await wasmSdkPage.fillDocumentFields(updatedFields);
    -
    -      // Execute the replace transition
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Validate document replace specific result with expected document ID
    -      const expectedDocumentId = testParams.documentId;
    -      validateDocumentReplaceResult(result.result, expectedDocumentId);
    -
    -      console.log('βœ… Document replace state transition completed successfully');
    -    });
    -
    -    test('should set price, purchase, and transfer a trading card document', async () => {
    -      // Set extended timeout for complete marketplace workflow
    -      test.setTimeout(275000);
    -
    -      let documentId;
    -      // Step 1: Set price on the card (by owner - primary identity)
    -      await test.step('Set price on trading card', async () => {
    -        console.log('Setting price on trading card...');
    -
    -        // Get the configured price from test data
    -        const setPriceParams = parameterInjector.testData.stateTransitionParameters.document.documentSetPrice.testnet[0];
    -        const configuredPrice = setPriceParams.price;
    -        
    -        // Execute the set price transition
    -        const setPriceResult = await executeStateTransitionWithCustomParams(
    -          wasmSdkPage,
    -          parameterInjector,
    -          'document',
    -          'documentSetPrice',
    -          'testnet',
    -          {}
    -        );
    -
    -        // Validate basic result structure
    -        validateBasicStateTransitionResult(setPriceResult);
    -
    -        // Get document ID from test data for validation
    -        documentId = setPriceParams.documentId;
    -        
    -        // Validate document set price specific result
    -        validateDocumentSetPriceResult(
    -          setPriceResult.result,
    -          documentId,
    -          configuredPrice
    -        );
    -
    -        console.log('βœ… Card price set successfully');
    -      });
    -
    -      // Step 2: Purchase the card with secondary identity (tests purchase flow)
    -      await test.step('Purchase trading card with secondary identity', async () => {
    -        console.log('Purchasing trading card with secondary identity...');
    -
    -        // Get the configured price from test data
    -        const purchaseParams = parameterInjector.testData.stateTransitionParameters.document.documentPurchase.testnet[0];
    -        const purchaseConfiguredPrice = purchaseParams.price;
    -        
    -        // Log if the purchase price differs from what was set
    -        const setPriceParams = parameterInjector.testData.stateTransitionParameters.document.documentSetPrice.testnet[0];
    -        if (purchaseConfiguredPrice !== setPriceParams.price) {
    -          console.log(`⚠️ Note: documentPurchase uses price ${purchaseConfiguredPrice}, but documentSetPrice set it to ${setPriceParams.price}`);
    -        }
    -
    -        // Execute the purchase transition
    -        const purchaseResult = await executeStateTransitionWithCustomParams(
    -          wasmSdkPage,
    -          parameterInjector,
    -          'document',
    -          'documentPurchase',
    -          'testnet',
    -          {}
    -        );
    -
    -        // Validate basic result structure
    -        validateBasicStateTransitionResult(purchaseResult);
    -
    -        // Get test parameters for validation (secondary identity is the buyer)
    -        const testParams = parameterInjector.testData.stateTransitionParameters.document.documentPurchase.testnet[0];
    -
    -        // Validate document purchase specific result
    -        validateDocumentPurchaseResult(
    -          purchaseResult.result,
    -          documentId,
    -          testParams.identityId, // Secondary identity as buyer
    -          purchaseConfiguredPrice  // Use the actual price from test-data.js
    -        );
    -
    -        console.log('βœ… Card purchased by secondary identity successfully');
    -      });
    -
    -      // Step 3: Transfer the card back to primary identity (tests transfer flow)
    -      await test.step('Transfer card back to primary identity', async () => {
    -        console.log('Transferring card back to primary identity...');
    -
    -        // Get primary identity ID from test data
    -        const primaryIdentityId = parameterInjector.testData.stateTransitionParameters.dataContract.dataContractCreate.testnet[0].identityId;
    -
    -        // Execute the transfer transition
    -        const transferResult = await executeStateTransitionWithCustomParams(
    -          wasmSdkPage,
    -          parameterInjector,
    -          'document',
    -          'documentTransfer',
    -          'testnet',
    -          {
    -            recipientId: primaryIdentityId // Transfer back to primary identity
    -          }
    -        );
    -
    -        // Validate basic result structure
    -        validateBasicStateTransitionResult(transferResult);
    -
    -        // Validate document transfer specific result
    -        validateDocumentTransferResult(
    -          transferResult.result,
    -          documentId,
    -          primaryIdentityId // Primary identity as recipient
    -        );
    -
    -        console.log('βœ… Complete marketplace workflow completed: Create β†’ Set Price β†’ Purchase β†’ Transfer');
    -      });
    -    });
    -
    -    test('should create, replace, and delete a document', async () => {
    -      // Set extended timeout for combined create+replace+delete operation
    -      test.setTimeout(260000);
    -
    -      let documentId;
    -
    -      // Step 1: Create document (reported separately)
    -      await test.step('Create document', async () => {
    -        console.log('Creating new document...');
    -
    -        // Set up the document create transition
    -        await wasmSdkPage.setupStateTransition('document', 'documentCreate');
    -
    -        // Inject basic parameters (contractId, documentType, identityId, privateKey)
    -        const success = await parameterInjector.injectStateTransitionParameters('document', 'documentCreate', 'testnet');
    -        expect(success).toBe(true);
    -
    -        // Fetch document schema to generate dynamic fields
    -        await wasmSdkPage.fetchDocumentSchema();
    -
    -        // Fill document fields
    -        const testParams = parameterInjector.testData.stateTransitionParameters.document.documentCreate.testnet[0];
    -        await wasmSdkPage.fillDocumentFields(testParams.documentFields);
    -
    -        // Execute the transition
    -        const createResult = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -        // Validate create result
    -        validateBasicStateTransitionResult(createResult);
    -        const documentResponse = validateDocumentCreateResult(createResult.result);
    -
    -        // Get the document ID from create result
    -        documentId = documentResponse.documentId;
    -        console.log('βœ… Document created with ID:', documentId);
    -      });
    -
    -      // Step 2: Replace the document (reported separately)
    -      await test.step('Replace document', async () => {
    -        console.log('Replacing the created document...');
    -
    -        // Set up document replace transition
    -        await wasmSdkPage.setupStateTransition('document', 'documentReplace');
    -
    -        // Inject parameters with the created document ID
    -        const success = await parameterInjector.injectStateTransitionParameters(
    -          'document',
    -          'documentReplace',
    -          'testnet',
    -          { documentId } // Override with the created document ID
    -        );
    -        expect(success).toBe(true);
    -
    -        // Load the existing document to get revision
    -        await wasmSdkPage.loadExistingDocument();
    -
    -        // Create updated message with timestamp
    -        const originalTestParams = parameterInjector.testData.stateTransitionParameters.document.documentCreate.testnet[0];
    -        const originalMessage = originalTestParams.documentFields.message;
    -        const timestamp = new Date().toISOString();
    -        const updatedFields = {
    -          message: `${originalMessage} - Updated at ${timestamp}`
    -        };
    -
    -        // Fill updated document fields
    -        await wasmSdkPage.fillDocumentFields(updatedFields);
    -
    -        // Execute the replace transition
    -        const replaceResult = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -        // Validate replace result
    -        validateBasicStateTransitionResult(replaceResult);
    -        validateDocumentReplaceResult(replaceResult.result, documentId);
    -
    -        console.log('βœ… Document replaced successfully');
    -      });
    -
    -      // Step 3: Delete the document (reported separately)
    -      await test.step('Delete document', async () => {
    -        console.log('Deleting the created document...');
    -
    -        // Set up document delete transition with the created document ID
    -        await wasmSdkPage.setupStateTransition('document', 'documentDelete');
    -
    -        // Inject parameters with the dynamic document ID
    -        const success = await parameterInjector.injectStateTransitionParameters(
    -          'document',
    -          'documentDelete',
    -          'testnet',
    -          { documentId } // Override with the created document ID
    -        );
    -        expect(success).toBe(true);
    -
    -        // Execute the delete transition
    -        const deleteResult = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -        // Validate delete result with expected document ID
    -        validateBasicStateTransitionResult(deleteResult);
    -        validateDocumentDeleteResult(deleteResult.result, documentId);
    -
    -        console.log('βœ… Document deleted successfully');
    -      });
    -    });
    -
    -    test('should show authentication inputs for document transitions', async () => {
    -      await wasmSdkPage.setupStateTransition('document', 'documentCreate');
    -
    -      // Check that authentication inputs are visible
    -      const hasAuthInputs = await wasmSdkPage.hasAuthenticationInputs();
    -      expect(hasAuthInputs).toBe(true);
    -
    -      console.log('βœ… Document state transition authentication inputs are visible');
    -    });
    -  });
    -
    -  test.describe('Identity State Transitions', () => {
    -    test('should execute identity credit transfer transition', async () => {
    -      // Set up the identity credit transfer transition
    -      await wasmSdkPage.setupStateTransition('identity', 'identityCreditTransfer');
    -
    -      // Inject parameters (senderId, recipientId, amount, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('identity', 'identityCreditTransfer', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the transfer
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.identity.identityCreditTransfer.testnet[0];
    -
    -      // Validate identity credit transfer specific result
    -      validateIdentityCreditTransferResult(
    -        result.result,
    -        testParams.identityId, // Sender is the identityId field
    -        testParams.recipientId,
    -        testParams.amount
    -      );
    -
    -      console.log('βœ… Identity credit transfer state transition completed successfully');
    -    });
    -
    -    test('should execute identity credit withdrawal transition', async () => {
    -      // Get test parameters to check withdrawal amount upfront
    -      const testParams = parameterInjector.testData.stateTransitionParameters.identity.identityCreditWithdrawal.testnet[0];
    -
    -      // Skip test if withdrawal amount is below minimum threshold
    -      if (testParams.amount < 190000) {
    -        test.skip(true, `Withdrawal amount ${testParams.amount} credits is below minimum threshold (~190,000 credits)`);
    -      }
    -
    -      // Set up the identity credit withdrawal transition
    -      await wasmSdkPage.setupStateTransition('identity', 'identityCreditWithdrawal');
    -
    -      // Inject parameters (identityId, withdrawalAddress, amount, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('identity', 'identityCreditWithdrawal', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the withdrawal
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Validate identity credit withdrawal specific result
    -      validateIdentityCreditWithdrawalResult(
    -        result.result,
    -        testParams.identityId,
    -        testParams.toAddress,
    -        testParams.amount
    -      );
    -
    -      console.log('βœ… Identity credit withdrawal state transition completed successfully');
    -    });
    -
    -    test('should show authentication inputs for identity transitions', async () => {
    -      await wasmSdkPage.setupStateTransition('identity', 'identityCreditTransfer');
    -
    -      // Check that authentication inputs are visible
    -      const hasAuthInputs = await wasmSdkPage.hasAuthenticationInputs();
    -      expect(hasAuthInputs).toBe(true);
    -
    -      console.log('βœ… Identity state transition authentication inputs are visible');
    -    });
    -  });
    -
    -  test.describe('Token State Transitions', () => {
    -    test('should execute token mint transition', async () => {
    -      // Set up the token mint transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenMint');
    -
    -      // Inject parameters (contractId, tokenId, tokenPosition, amount, issuedToIdentityId, identityId, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenMint', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the mint
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenMint.testnet[0];
    -
    -      // Validate token mint specific result
    -      validateTokenMintResult(
    -        result.result,
    -        testParams.identityId,
    -        testParams.amount
    -      );
    -
    -      console.log('βœ… Token mint state transition completed successfully');
    -    });
    -
    -    test('should execute token transfer transition', async () => {
    -      // Set up the token transfer transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenTransfer');
    -
    -      // Inject parameters (contractId, tokenId, tokenPosition, amount, recipientId, identityId, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenTransfer', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the transfer
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenTransfer.testnet[0];
    -
    -      // Validate token transfer specific result
    -      validateTokenTransferResult(
    -        result.result,
    -        testParams.identityId,
    -        testParams.recipientId,
    -        testParams.amount
    -      );
    -
    -      console.log('βœ… Token transfer state transition completed successfully');
    -    });
    -
    -    test('should execute token burn transition', async () => {
    -      // Set up the token burn transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenBurn');
    -
    -      // Inject parameters (contractId, tokenId, tokenPosition, amount, identityId, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenBurn', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the burn
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenBurn.testnet[0];
    -
    -      // Validate token burn specific result
    -      validateTokenBurnResult(
    -        result.result,
    -        testParams.identityId,
    -        testParams.amount
    -      );
    -
    -      console.log('βœ… Token burn state transition completed successfully');
    -    });
    -
    -    test('should execute token freeze transition', async () => {
    -      // Set up the token freeze transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenFreeze');
    -
    -      // Inject parameters (contractId, tokenPosition, identityId, identityToFreeze, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenFreeze', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the freeze
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenFreeze.testnet[0];
    -
    -      // Validate token freeze specific result
    -      validateTokenFreezeResult(result.result, testParams.identityToFreeze);
    -
    -      console.log('βœ… Token freeze state transition completed successfully');
    -    });
    -
    -    test('should execute token destroy frozen transition', async () => {
    -      // Set up the token destroy frozen transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenDestroyFrozen');
    -
    -      // Inject parameters (contractId, tokenPosition, identityId, destroyFromIdentityId, amount, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenDestroyFrozen', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the destroy frozen
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenDestroyFrozen.testnet[0];
    -
    -      // Validate token destroy frozen specific result
    -      validateTokenDestroyFrozenResult(result.result, testParams.frozenIdentityId);
    -
    -      console.log('βœ… Token destroy frozen state transition completed successfully');
    -    });
    -
    -    test('should execute token unfreeze transition', async () => {
    -      // Set up the token unfreeze transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenUnfreeze');
    -
    -      // Inject parameters (contractId, tokenPosition, identityId, identityToUnfreeze, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenUnfreeze', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the unfreeze
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenUnfreeze.testnet[0];
    -
    -      // Validate token unfreeze specific result
    -      validateTokenUnfreezeResult(result.result, testParams.identityToUnfreeze);
    -
    -      console.log('βœ… Token unfreeze state transition completed successfully');
    -    });
    -
    -    test('should execute token claim transition', async () => {
    -      // Set up the token claim transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenClaim');
    -
    -      // Inject parameters (contractId, tokenPosition, distributionType, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenClaim', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the claim
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Check for expected platform responses indicating no tokens available
    -      if (!result.success && result.result && result.result.includes('Missing response message')) {
    -        // Skip the test with a descriptive reason
    -        test.skip(true, 'Platform returned "Missing response message". Probably no tokens available to claim.');
    -      }
    -
    -      // Validate normal success case
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenClaim.testnet[0];
    -
    -      // Validate token claim specific result
    -      validateTokenClaimResult(result.result, testParams.distributionType);
    -
    -      console.log('βœ… Token claim state transition completed successfully');
    -    });
    -
    -    test('should execute token set price transition', async () => {
    -      // Set up the token set price transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenSetPriceForDirectPurchase');
    -
    -      // Inject parameters (contractId, tokenPosition, priceType, priceData, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenSetPriceForDirectPurchase', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the set price
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenSetPriceForDirectPurchase.testnet[0];
    -
    -      // Validate token set price specific result
    -      validateTokenSetPriceResult(result.result, testParams.priceType, testParams.priceData);
    -
    -      console.log('βœ… Token set price state transition completed successfully');
    -    });
    -
    -    test('should execute token direct purchase transition', async () => {
    -      // Set up the token direct purchase transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenDirectPurchase');
    -
    -      // Inject parameters (contractId, tokenPosition, amount, totalAgreedPrice, keyId, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenDirectPurchase', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the purchase
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Check for expected platform responses indicating issues
    -      if (!result.success && result.result && result.result.includes('Missing response message')) {
    -        // Skip the test with a descriptive reason
    -        test.skip(true, 'Platform returned "Missing response message". Possibly insufficient credits or tokens not available for purchase.');
    -      }
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenDirectPurchase.testnet[0];
    -
    -      // Validate token direct purchase specific result
    -      validateTokenDirectPurchaseResult(result.result, testParams.amount, testParams.totalAgreedPrice);
    -
    -      console.log('βœ… Token direct purchase state transition completed successfully');
    -    });
    -
    -    test('should execute token config update transition', async () => {
    -      // Set up the token config update transition
    -      await wasmSdkPage.setupStateTransition('token', 'tokenConfigUpdate');
    -
    -      // Inject parameters (contractId, tokenPosition, configItemType, configValue, privateKey)
    -      const success = await parameterInjector.injectStateTransitionParameters('token', 'tokenConfigUpdate', 'testnet');
    -      expect(success).toBe(true);
    -
    -      // Execute the config update
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Validate basic result structure
    -      validateBasicStateTransitionResult(result);
    -
    -      // Get test parameters for validation
    -      const testParams = parameterInjector.testData.stateTransitionParameters.token.tokenConfigUpdate.testnet[0];
    -
    -      // Validate token config update specific result
    -      validateTokenConfigUpdateResult(result.result, testParams.configItemType, testParams.configValue);
    -
    -      console.log('βœ… Token config update state transition completed successfully');
    -    });
    -
    -    test('should show authentication inputs for token transitions', async () => {
    -      await wasmSdkPage.setupStateTransition('token', 'tokenTransfer');
    -
    -      // Check that authentication inputs are visible
    -      const hasAuthInputs = await wasmSdkPage.hasAuthenticationInputs();
    -      expect(hasAuthInputs).toBe(true);
    -
    -      console.log('βœ… Token state transition authentication inputs are visible');
    -    });
    -  });
    -
    -  test.describe('Error Handling for State Transitions', () => {
    -    test('should handle invalid JSON schema gracefully', async () => {
    -      await wasmSdkPage.setupStateTransition('dataContract', 'dataContractCreate');
    -
    -      // Fill with invalid JSON schema
    -      const invalidParams = {
    -        canBeDeleted: false,
    -        readonly: false,
    -        keepsHistory: false,
    -        documentSchemas: 'invalid_json_here',
    -        identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -        privateKey: "11111111111111111111111111111111111111111111111111"
    -      };
    -
    -      await wasmSdkPage.fillStateTransitionParameters(invalidParams);
    -
    -      // Execute the transition
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Should show error
    -      expect(result.hasError).toBe(true);
    -      expect(result.statusText.toLowerCase()).toMatch(/error|invalid|failed/);
    -
    -      console.log('βœ… Invalid JSON schema error handling works correctly');
    -    });
    -
    -    test('should handle missing required fields', async () => {
    -      await wasmSdkPage.setupStateTransition('dataContract', 'dataContractCreate');
    -
    -      // Don't fill any parameters, try to execute
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Should show error or validation message
    -      expect(result.hasError).toBe(true);
    -      expect(result.statusText.toLowerCase()).toMatch(/error|required|missing/);
    -
    -      console.log('βœ… Missing required fields error handling works correctly');
    -    });
    -
    -    test('should handle invalid private key gracefully', async () => {
    -      await wasmSdkPage.setupStateTransition('dataContract', 'dataContractCreate');
    -
    -      // Fill with invalid private key
    -      const invalidParams = {
    -        canBeDeleted: false,
    -        readonly: false,
    -        keepsHistory: false,
    -        documentSchemas: '{"note": {"type": "object", "properties": {"message": {"type": "string", "position": 0}}, "additionalProperties": false}}',
    -        identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk",
    -        privateKey: "invalid_private_key_here"
    -      };
    -
    -      await wasmSdkPage.fillStateTransitionParameters(invalidParams);
    -
    -      // Execute the transition
    -      const result = await wasmSdkPage.executeStateTransitionAndGetResult();
    -
    -      // Should show error
    -      expect(result.hasError).toBe(true);
    -      expect(result.statusText.toLowerCase()).toMatch(/error|invalid|failed/);
    -
    -      console.log('βœ… Invalid private key error handling works correctly');
    -    });
    -  });
    -
    -  test.describe('UI State and Navigation', () => {
    -    test('should switch to state transitions operation type correctly', async () => {
    -      // Start with queries, then switch to transitions
    -      await wasmSdkPage.setOperationType('queries');
    -      await wasmSdkPage.page.waitForTimeout(500);
    -
    -      await wasmSdkPage.setOperationType('transitions');
    -
    -      // Verify the operation type is set correctly
    -      const operationType = await wasmSdkPage.page.locator('#operationType').inputValue();
    -      expect(operationType).toBe('transitions');
    -
    -      console.log('βœ… Successfully switched to state transitions operation type');
    -    });
    -
    -    test('should populate transition categories correctly', async () => {
    -      await wasmSdkPage.setOperationType('transitions');
    -
    -      // Get available categories and filter out placeholders
    -      const allCategories = await wasmSdkPage.getAvailableQueryCategories();
    -      const categories = filterPlaceholderOptions(allCategories);
    -
    -      // Define expected state transition categories
    -      const expectedCategories = [
    -        'Identity Transitions',
    -        'Data Contract Transitions',
    -        'Document Transitions',
    -        'Token Transitions',
    -        'Voting Transitions'
    -      ];
    -
    -      // Verify exact match - contains all expected and no unexpected ones
    -      expect(categories).toHaveLength(expectedCategories.length);
    -      expectedCategories.forEach(expectedCategory => {
    -        expect(categories).toContain(expectedCategory);
    -      });
    -
    -      console.log('βœ… State transition categories populated correctly:', categories);
    -    });
    -
    -    test('should populate identity transition types correctly', async () => {
    -      await wasmSdkPage.setOperationType('transitions');
    -      await wasmSdkPage.setQueryCategory('identity');
    -
    -      // Get available transition types and filter out placeholders
    -      const allTransitionTypes = await wasmSdkPage.getAvailableQueryTypes();
    -      const transitionTypes = filterPlaceholderOptions(allTransitionTypes);
    -
    -      // Define expected identity transition types
    -      const expectedTransitionTypes = [
    -        'Identity Create',
    -        'Identity Top Up',
    -        'Identity Update',
    -        'Identity Credit Transfer',
    -        'Identity Credit Withdrawal'
    -      ];
    -
    -      // Verify exact match - contains all expected and no unexpected ones
    -      expect(transitionTypes).toHaveLength(expectedTransitionTypes.length);
    -      expectedTransitionTypes.forEach(expectedType => {
    -        expect(transitionTypes).toContain(expectedType);
    -      });
    -
    -      console.log('βœ… Identity transition types populated correctly:', transitionTypes);
    -    });
    -
    -    test('should populate data contract transition types correctly', async () => {
    -      await wasmSdkPage.setOperationType('transitions');
    -      await wasmSdkPage.setQueryCategory('dataContract');
    -
    -      // Get available transition types and filter out placeholders
    -      const allTransitionTypes = await wasmSdkPage.getAvailableQueryTypes();
    -      const transitionTypes = filterPlaceholderOptions(allTransitionTypes);
    -
    -      // Define expected data contract transition types
    -      const expectedTransitionTypes = [
    -        'Data Contract Create',
    -        'Data Contract Update'
    -      ];
    -
    -      // Verify exact match - contains all expected and no unexpected ones
    -      expect(transitionTypes).toHaveLength(expectedTransitionTypes.length);
    -      expectedTransitionTypes.forEach(expectedType => {
    -        expect(transitionTypes).toContain(expectedType);
    -      });
    -
    -      console.log('βœ… Data contract transition types populated correctly:', transitionTypes);
    -    });
    -
    -    test('should populate document transition types correctly', async () => {
    -      await wasmSdkPage.setOperationType('transitions');
    -      await wasmSdkPage.setQueryCategory('document');
    -
    -      // Get available transition types and filter out placeholders
    -      const allTransitionTypes = await wasmSdkPage.getAvailableQueryTypes();
    -      const transitionTypes = filterPlaceholderOptions(allTransitionTypes);
    -
    -      // Define expected document transition types
    -      const expectedTransitionTypes = [
    -        'Document Create',
    -        'Document Replace',
    -        'Document Delete',
    -        'Document Transfer',
    -        'Document Purchase',
    -        'Document Set Price',
    -        'DPNS Register Name'
    -      ];
    -
    -      // Verify exact match - contains all expected and no unexpected ones
    -      expect(transitionTypes).toHaveLength(expectedTransitionTypes.length);
    -      expectedTransitionTypes.forEach(expectedType => {
    -        expect(transitionTypes).toContain(expectedType);
    -      });
    -
    -      console.log('βœ… Document transition types populated correctly:', transitionTypes);
    -    });
    -
    -    test('should populate token transition types correctly', async () => {
    -      await wasmSdkPage.setOperationType('transitions');
    -      await wasmSdkPage.setQueryCategory('token');
    -
    -      // Get available transition types and filter out placeholders
    -      const allTransitionTypes = await wasmSdkPage.getAvailableQueryTypes();
    -      const transitionTypes = filterPlaceholderOptions(allTransitionTypes);
    -
    -      // Define expected token transition types (based on docs.html)
    -      const expectedTransitionTypes = [
    -        'Token Burn',
    -        'Token Mint',
    -        'Token Claim',
    -        'Token Set Price',
    -        'Token Direct Purchase',
    -        'Token Config Update',
    -        'Token Transfer',
    -        'Token Freeze',
    -        'Token Unfreeze',
    -        'Token Destroy Frozen'
    -      ];
    -
    -      // Verify exact match - contains all expected and no unexpected ones
    -      expect(transitionTypes).toHaveLength(expectedTransitionTypes.length);
    -      expectedTransitionTypes.forEach(expectedType => {
    -        expect(transitionTypes).toContain(expectedType);
    -      });
    -
    -      console.log('βœ… Token transition types populated correctly:', transitionTypes);
    -    });
    -
    -    test('should populate voting transition types correctly', async () => {
    -      await wasmSdkPage.setOperationType('transitions');
    -      await wasmSdkPage.setQueryCategory('voting');
    -
    -      // Get available transition types and filter out placeholders
    -      const allTransitionTypes = await wasmSdkPage.getAvailableQueryTypes();
    -      const transitionTypes = filterPlaceholderOptions(allTransitionTypes);
    -
    -      // Define expected voting transition types
    -      const expectedTransitionTypes = [
    -        'DPNS Username',
    -        'Contested Resource'
    -      ];
    -
    -      // Verify exact match - contains all expected and no unexpected ones
    -      expect(transitionTypes).toHaveLength(expectedTransitionTypes.length);
    -      expectedTransitionTypes.forEach(expectedType => {
    -        expect(transitionTypes).toContain(expectedType);
    -      });
    -
    -      console.log('βœ… Voting transition types populated correctly:', transitionTypes);
    -    });
    -  });
    -
    -});
    diff --git a/packages/wasm-sdk/test/ui-automation/utils/base-test.js b/packages/wasm-sdk/test/ui-automation/utils/base-test.js
    deleted file mode 100644
    index 1f98c954ecb..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/utils/base-test.js
    +++ /dev/null
    @@ -1,249 +0,0 @@
    -const { expect } = require('@playwright/test');
    -const fs = require('fs');
    -
    -/**
    - * Base test utilities for WASM SDK UI automation
    - */
    -class BaseTest {
    -  constructor(page) {
    -    this.page = page;
    -  }
    -
    -  /**
    -   * Navigate to the WASM SDK index page and wait for initialization
    -   */
    -  async navigateToSdk() {
    -    await this.page.goto('/');
    -    
    -    // Wait for the WASM SDK to initialize
    -    await this.page.waitForSelector('#statusBanner.success', { 
    -      timeout: 60000,
    -      state: 'visible' 
    -    });
    -    
    -    // Verify we're on the right page
    -    await expect(this.page).toHaveTitle(/Dash Platform WASM JS SDK/);
    -    
    -    console.log('SDK initialized successfully');
    -  }
    -
    -  /**
    -   * Wait for SDK to be in success state (useful after network changes)
    -   */
    -  async waitForSdkReady() {
    -    await this.page.waitForSelector('#statusBanner.success', {
    -      timeout: 30000,
    -      state: 'visible'
    -    });
    -    
    -    // Additional wait to ensure stability
    -    await this.page.waitForTimeout(500);
    -  }
    -
    -  /**
    -   * Wait for network loading to complete
    -   */
    -  async waitForNetworkIdle() {
    -    await this.page.waitForLoadState('networkidle');
    -  }
    -
    -  /**
    -   * Take a screenshot with a descriptive name
    -   */
    -  async takeScreenshot(name) {
    -    const screenshotDir = 'test-results/screenshots/';
    -    
    -    // Create directory if it doesn't exist
    -    if (!fs.existsSync(screenshotDir)) {
    -      fs.mkdirSync(screenshotDir, { recursive: true });
    -    }
    -    
    -    await this.page.screenshot({ 
    -      path: `${screenshotDir}${name}-${Date.now()}.png`,
    -      fullPage: true 
    -    });
    -  }
    -
    -  /**
    -   * Wait for an element to be visible and ready for interaction
    -   */
    -  async waitForElement(selector, options = {}) {
    -    const element = this.page.locator(selector);
    -    await element.waitFor({ state: 'visible', ...options });
    -    return element;
    -  }
    -
    -  /**
    -   * Fill input field with validation
    -   */
    -  async fillInput(selector, value, options = {}) {
    -    const input = await this.waitForElement(selector);
    -    await input.clear();
    -    await input.fill(value);
    -    
    -    // Verify the value was set correctly
    -    if (options.verify !== false) {
    -      await expect(input).toHaveValue(value);
    -    }
    -    
    -    return input;
    -  }
    -
    -  /**
    -   * Select option from dropdown
    -   */
    -  async selectOption(selector, value) {
    -    const select = await this.waitForElement(selector);
    -    await select.selectOption(value);
    -    
    -    // Verify selection
    -    await expect(select).toHaveValue(value);
    -    
    -    return select;
    -  }
    -
    -  /**
    -   * Click button and wait for any loading states
    -   */
    -  async clickButton(selector, options = {}) {
    -    const button = await this.waitForElement(selector);
    -    
    -    // Check if button is enabled
    -    await expect(button).toBeEnabled();
    -    
    -    // Click and optionally wait for response
    -    await button.click();
    -    
    -    if (options.waitForResponse) {
    -      await this.page.waitForResponse(response => 
    -        response.url().includes('dapi')
    -      );
    -    }
    -    
    -    return button;
    -  }
    -
    -  /**
    -   * Get the current result content
    -   */
    -  async getResultContent() {
    -    const resultContainer = this.page.locator('#identityInfo');
    -    await resultContainer.waitFor({ state: 'visible' });
    -    return await resultContainer.textContent();
    -  }
    -
    -  /**
    -   * Check if result shows an error
    -   */
    -  async hasErrorResult() {
    -    const resultContainer = this.page.locator('#identityInfo');
    -    const classList = await resultContainer.getAttribute('class');
    -    return classList && classList.includes('error');
    -  }
    -
    -  /**
    -   * Clear results
    -   */
    -  async clearResults() {
    -    await this.clickButton('#clearButton');
    -    const resultContainer = this.page.locator('#identityInfo');
    -    await expect(resultContainer).toHaveClass(/empty/);
    -  }
    -
    -  /**
    -   * Set network (mainnet/testnet)
    -   */
    -  async setNetwork(network = 'testnet') {
    -    const networkRadio = this.page.locator(`#${network}`);
    -    await networkRadio.check();
    -    
    -    // Wait for network indicator to update
    -    const indicator = this.page.locator('#networkIndicator');
    -    await expect(indicator).toContainText(network.toUpperCase());
    -    
    -    // Network changes might trigger SDK re-initialization, so wait a bit
    -    await this.page.waitForTimeout(1000);
    -    
    -    console.log(`Network set to ${network}`);
    -  }
    -
    -  /**
    -   * Set operation type (queries, transitions, wallet)
    -   */
    -  async setOperationType(type = 'queries') {
    -    await this.selectOption('#operationType', type);
    -    console.log(`Operation type set to ${type}`);
    -  }
    -
    -  /**
    -   * Set query category
    -   */
    -  async setQueryCategory(category) {
    -    await this.selectOption('#queryCategory', category);
    -    
    -    // Wait for query type dropdown to populate
    -    await this.page.waitForTimeout(500);
    -    
    -    console.log(`Query category set to ${category}`);
    -  }
    -
    -  /**
    -   * Set specific query type
    -   */
    -  async setQueryType(queryType) {
    -    // Make sure query type dropdown is visible
    -    await this.waitForElement('#queryType');
    -    await this.selectOption('#queryType', queryType);
    -    
    -    // Wait for inputs to appear
    -    await this.page.waitForTimeout(500);
    -    
    -    console.log(`Query type set to ${queryType}`);
    -  }
    -
    -  /**
    -   * Execute the current query and wait for results
    -   */
    -  async executeQuery() {
    -    const executeButton = this.page.locator('#executeQuery');
    -    
    -    // Ensure button is visible and enabled
    -    await expect(executeButton).toBeVisible();
    -    await expect(executeButton).toBeEnabled();
    -    
    -    // Click execute button
    -    await executeButton.click();
    -    
    -    const statusBanner = this.page.locator('#statusBanner');
    -    
    -    // Try waiting for loading state, but handle queries that execute instantly
    -    try {
    -      // Wait for status banner to show loading
    -      await this.page.locator('#statusBanner.loading').waitFor({ state: 'visible', timeout: 5000 });
    -      
    -      // Wait for loading to complete (either success or error)
    -      // State transitions can take longer than queries, so use longer timeout
    -      await this.page.locator('#statusBanner.loading').waitFor({ state: 'hidden', timeout: 85000 });
    -    } catch (error) {
    -      // Some queries execute so quickly they never show loading state
    -      // Check if the query already completed successfully or with an error
    -      const currentStatus = await statusBanner.getAttribute('class');
    -      if (currentStatus && (currentStatus.includes('success') || currentStatus.includes('error'))) {
    -        // Query completed without showing loading state - this is okay for fast queries
    -        console.log('Query executed');
    -        return currentStatus.includes('success');
    -      }
    -      
    -      // If not in a final state, re-throw the timeout error
    -      throw error;
    -    }
    -    
    -    console.log('Query executed');
    -    
    -    // Return whether it was successful
    -    const statusClass = await statusBanner.getAttribute('class');
    -    return statusClass && statusClass.includes('success');
    -  }
    -}
    -
    -module.exports = { BaseTest };
    diff --git a/packages/wasm-sdk/test/ui-automation/utils/parameter-injector.js b/packages/wasm-sdk/test/ui-automation/utils/parameter-injector.js
    deleted file mode 100644
    index 35c5a8a67f6..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/utils/parameter-injector.js
    +++ /dev/null
    @@ -1,316 +0,0 @@
    -const { testData, getTestParameters, getAllTestParameters, getStateTransitionParameters, getAllStateTransitionParameters } = require('../fixtures/test-data');
    -
    -/**
    - * Parameter injection system for WASM SDK UI tests
    - * Maps test data to UI form fields automatically
    - */
    -class ParameterInjector {
    -  constructor(wasmSdkPage) {
    -    this.page = wasmSdkPage;
    -    this.testData = testData;
    -  }
    -
    -  /**
    -   * Inject parameters for a specific query based on test data
    -   */
    -  async injectParameters(category, queryType, network = 'testnet', parameterSetIndex = 0) {
    -    try {
    -      const allParameters = getAllTestParameters(category, queryType, network);
    -      
    -      if (allParameters.length === 0) {
    -        console.warn(`⚠️  No test parameters found for ${category}.${queryType} on ${network}`);
    -        return false;
    -      }
    -
    -      const parameters = allParameters[parameterSetIndex] || allParameters[0];
    -      console.log(`πŸ“ Injecting parameters for ${category}.${queryType}`);
    -
    -      await this.page.fillQueryParameters(parameters);
    -      return true;
    -    } catch (error) {
    -      console.error(`❌ Failed to inject parameters for ${category}.${queryType}:`, error.message);
    -      return false;
    -    }
    -  }
    -
    -  /**
    -   * Inject parameters for a specific state transition based on test data
    -   */
    -  async injectStateTransitionParameters(category, transitionType, network = 'testnet', customParams = {}) {
    -    try {
    -      // Get base parameters from test data
    -      const allParameters = getAllStateTransitionParameters(category, transitionType, network);
    -      
    -      if (allParameters.length === 0) {
    -        console.warn(`⚠️  No state transition test parameters found for ${category}.${transitionType} on ${network}`);
    -        return false;
    -      }
    -      
    -      const baseParameters = allParameters[0];
    -      
    -      // Merge base parameters with custom overrides
    -      const parameters = { ...baseParameters, ...customParams };
    -      
    -      console.log(`πŸ“ Injecting state transition parameters for ${category}.${transitionType}`);
    -
    -      await this.page.fillStateTransitionParameters(parameters);
    -      return true;
    -    } catch (error) {
    -      console.error(`❌ Failed to inject state transition parameters for ${category}.${transitionType}:`, error.message);
    -      return false;
    -    }
    -  }
    -
    -  /**
    -   * Get parameter mapping for manual field filling
    -   * Maps parameter names to likely field selectors
    -   */
    -  getParameterFieldMapping() {
    -    return {
    -      // Identity parameters
    -      'id': ['#id', '[name="id"]', 'input[placeholder*="Identity ID"]'],
    -      'identityId': ['[name="identityId"]', '#identityId', 'input[placeholder*="Identity ID"]'],
    -      'identityIds': ['input[placeholder="Enter value"]', '.array-input-container input[type="text"]', '[data-array-name="identityIds"] input[type="text"]', '.array-input-container[data-array-name="identityIds"] input', '#identityIds', '[name="identityIds"]', 'input[placeholder*="Identity IDs"]'],
    -      'identitiesIds': ['input[placeholder="Enter value"]', '.array-input-container input[type="text"]', '[data-array-name="identitiesIds"] input[type="text"]', '.array-input-container[data-array-name="identitiesIds"] input', '#identitiesIds', '[name="identitiesIds"]', 'input[placeholder*="Identity IDs"]'],
    -      
    -      // Data contract parameters
    -      'dataContractId': ['#dataContractId', '[name="dataContractId"]', 'input[placeholder*="Contract ID"]'],
    -      'contractId': ['#contractId', '[name="contractId"]', 'input[placeholder*="Contract ID"]'],
    -      'ids': ['input[placeholder="Enter value"]', '.array-input-container input[type="text"]', '[data-array-name="ids"] input[type="text"]', '.array-input-container[data-array-name="ids"] input', '#ids', '[name="ids"]', 'input[placeholder*="Contract IDs"]', 'input[placeholder*="Data Contract ID"]'],
    -      
    -      // Document parameters
    -      'documentType': ['#documentType', '[name="documentType"]', 'input[placeholder*="Document Type"]'],
    -      'documentId': ['#documentId', '[name="documentId"]', 'input[placeholder*="Document ID"]'],
    -      
    -      // Key parameters
    -      'publicKeyHash': ['#publicKeyHash', '[name="publicKeyHash"]', 'input[placeholder*="Public Key Hash"]'],
    -      'keyRequestType': ['#keyRequestType', '[name="keyRequestType"]', 'select[name="keyRequestType"]'],
    -      'specificKeyIds': ['#specificKeyIds', '[name="specificKeyIds"]'],
    -      
    -      // Token parameters
    -      'tokenId': ['#tokenId', '[name="tokenId"]', 'input[placeholder*="Token ID"]'],
    -      'tokenIds': ['input[placeholder="Enter value"]', '.array-input-container input[type="text"]', '[data-array-name="tokenIds"] input[type="text"]', '.array-input-container[data-array-name="tokenIds"] input', '#tokenIds', '[name="tokenIds"]', 'input[placeholder*="Token IDs"]'],
    -      
    -      // DPNS parameters
    -      'label': ['#label', '[name="label"]', 'input[placeholder*="Username"]', 'input[placeholder*="Label"]'],
    -      'name': ['#name', '[name="name"]', 'input[placeholder*="Name"]', 'input[placeholder*="DPNS"]'],
    -      'prefix': ['#prefix', '[name="prefix"]', 'input[placeholder*="prefix"]', 'input[placeholder*="Prefix"]'],
    -      
    -      // Query modifiers
    -      'limit': ['#limit', '[name="limit"]', 'input[placeholder*="limit" i]'],
    -      'offset': ['#offset', '[name="offset"]', 'input[placeholder*="offset" i]'],
    -      'count': ['#count', '[name="count"]', 'input[placeholder*="count" i]'],
    -      
    -      // Epoch parameters
    -      'epoch': ['#epoch', '[name="epoch"]', 'input[placeholder*="epoch" i]'],
    -      'startEpoch': ['#startEpoch', '[name="startEpoch"]'],
    -      'ascending': ['#ascending', '[name="ascending"]', 'input[type="checkbox"][name="ascending"]'],
    -      'orderAscending': ['#orderAscending', '[name="orderAscending"]', 'input[type="checkbox"][name="orderAscending"]'],
    -      'startAfter': ['#startAfter', '[name="startAfter"]', 'input[placeholder*="startAfter" i]'],
    -      
    -      // ProTx parameters
    -      'startProTxHash': ['#startProTxHash', '[name="startProTxHash"]'],
    -      'proTxHashes': ['#proTxHashes', '[name="proTxHashes"]'],
    -      
    -      // Where clause and ordering
    -      'whereClause': ['#whereClause', '[name="whereClause"]', 'textarea[placeholder*="Where"]'],
    -      'orderBy': ['#orderBy', '[name="orderBy"]', 'textarea[placeholder*="Order"]'],
    -      
    -      // Voting parameters
    -      'documentTypeName': ['#documentTypeName', '[name="documentTypeName"]'],
    -      'indexName': ['#indexName', '[name="indexName"]'],
    -      'indexValues': ['#indexValues', '[name="indexValues"]', 'textarea[name="indexValues"]', 'input[placeholder*="indexValues"]'],
    -      'resultType': ['#resultType', '[name="resultType"]'],
    -      'contestantId': ['#contestantId', '[name="contestantId"]'],
    -      'allowIncludeLockedAndAbstainingVoteTally': ['#allowIncludeLockedAndAbstainingVoteTally', '[name="allowIncludeLockedAndAbstainingVoteTally"]', 'input[type="checkbox"][name="allowIncludeLockedAndAbstainingVoteTally"]'],
    -      'startAtIdentifierInfo': ['#startAtIdentifierInfo', '[name="startAtIdentifierInfo"]'],
    -      
    -      // Group parameters
    -      'contractId': ['#contractId', '[name="contractId"]', 'input[placeholder*="Contract ID"]'],
    -      'groupContractPosition': ['#groupContractPosition', '[name="groupContractPosition"]'],
    -      'startAtGroupContractPosition': ['#startAtGroupContractPosition', '[name="startAtGroupContractPosition"]'],
    -      'startGroupContractPositionIncluded': ['#startGroupContractPositionIncluded', '[name="startGroupContractPositionIncluded"]', 'input[type="checkbox"][name="startGroupContractPositionIncluded"]'],
    -      'status': ['#status', '[name="status"]', 'select[name="status"]'],
    -      'actionId': ['#actionId', '[name="actionId"]'],
    -      'startActionId': ['#startActionId', '[name="startActionId"]'],
    -      'startActionIdIncluded': ['#startActionIdIncluded', '[name="startActionIdIncluded"]', 'input[type="checkbox"][name="startActionIdIncluded"]'],
    -      
    -      // Time parameters
    -      'startTimeMs': ['#startTimeMs', '[name="startTimeMs"]'],
    -      'endTimeMs': ['#endTimeMs', '[name="endTimeMs"]']
    -    };
    -  }
    -
    -  /**
    -   * Auto-detect and fill parameters using intelligent field matching
    -   */
    -  async autoFillParameters(parameters) {
    -    const fieldMapping = this.getParameterFieldMapping();
    -    const filledFields = [];
    -    const failedFields = [];
    -
    -    for (const [paramName, value] of Object.entries(parameters)) {
    -      const success = await this.tryFillParameter(paramName, value, fieldMapping);
    -      
    -      if (success) {
    -        filledFields.push(paramName);
    -      } else {
    -        failedFields.push(paramName);
    -      }
    -    }
    -
    -    console.log(`Successfully filled fields: ${filledFields.join(', ')}`);
    -    if (failedFields.length > 0) {
    -      console.warn(`⚠️  Failed to fill fields: ${failedFields.join(', ')}`);
    -    }
    -
    -    return { filledFields, failedFields };
    -  }
    -
    -  /**
    -   * Try to fill a parameter using multiple selector strategies
    -   */
    -  async tryFillParameter(paramName, value, fieldMapping) {
    -    const possibleSelectors = fieldMapping[paramName] || [];
    -    
    -    // Add generic fallback selectors
    -    possibleSelectors.push(
    -      `#${paramName}`,
    -      `[name="${paramName}"]`,
    -      `input[placeholder*="${paramName}" i]`,
    -      `label:has-text("${paramName}") + input`,
    -      `label:has-text("${paramName}") + select`,
    -      `label:has-text("${paramName}") + textarea`
    -    );
    -
    -    for (const selector of possibleSelectors) {
    -      try {
    -        const element = this.page.page.locator(selector).first();
    -        const count = await element.count();
    -        
    -        if (count > 0) {
    -          const isVisible = await element.isVisible();
    -          
    -          if (isVisible) {
    -            await this.page.fillInputByType(element, value);
    -            console.log(`πŸ“ Filled ${paramName} using selector: ${selector}`);
    -            return true;
    -          }
    -        }
    -      } catch (error) {
    -        continue;
    -      }
    -    }
    -
    -    return false;
    -  }
    -
    -  /**
    -   * Create test parameter sets for parameterized testing
    -   */
    -  createParameterizedTests(category, queryType, network = 'testnet') {
    -    const allParameters = getAllTestParameters(category, queryType, network);
    -    
    -    return allParameters.map((params, index) => ({
    -      testName: `${category}.${queryType} - Test Set ${index + 1}`,
    -      parameters: params,
    -      category,
    -      queryType,
    -      network,
    -      index
    -    }));
    -  }
    -
    -  /**
    -   * Validate parameters against expected schema
    -   */
    -  validateParameters(parameters, expectedSchema = {}) {
    -    const validation = {
    -      valid: true,
    -      errors: [],
    -      warnings: []
    -    };
    -
    -    for (const [key, value] of Object.entries(parameters)) {
    -      // Check for empty required values
    -      if (value === null || value === undefined || value === '') {
    -        validation.warnings.push(`Parameter '${key}' is empty`);
    -      }
    -
    -      // Validate array parameters
    -      if (Array.isArray(value) && value.length === 0) {
    -        validation.warnings.push(`Array parameter '${key}' is empty`);
    -      }
    -
    -      // Validate ID format (base58 compliance for Dash IDs)
    -      if (key.toLowerCase().includes('id') && typeof value === 'string') {
    -        if (!this.isValidBase58DashId(value)) {
    -          validation.errors.push(`Parameter '${key}' is not a valid base58-encoded Dash ID: ${value}`);
    -          validation.valid = false;
    -        }
    -      }
    -    }
    -
    -    return validation;
    -  }
    -
    -  /**
    -   * Validate if a string is a valid base58-encoded Dash ID
    -   * @param {string} id - The ID string to validate
    -   * @returns {boolean} - true if valid base58 Dash ID format
    -   */
    -  isValidBase58DashId(id) {
    -    // Dash IDs are typically 44 characters long when base58 encoded
    -    // Base58 alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
    -    // (excludes 0, O, I, l to avoid confusion)
    -    const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{43,44}$/;
    -    
    -    if (!base58Regex.test(id)) {
    -      return false;
    -    }
    -    
    -    // Additional check: ensure it doesn't contain invalid base58 characters
    -    const invalidChars = /[0OIl]/;
    -    if (invalidChars.test(id)) {
    -      return false;
    -    }
    -    
    -    return true;
    -  }
    -
    -  /**
    -   * Generate random test parameters for stress testing
    -   */
    -  generateRandomParameters(category, queryType) {
    -    // This would generate valid-looking but random parameters
    -    // for stress testing the UI with various inputs
    -    const generators = {
    -      id: () => 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', // Use known good ID
    -      identityId: () => 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec',
    -      dataContractId: () => 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec',
    -      limit: () => Math.floor(Math.random() * 100) + 1,
    -      count: () => Math.floor(Math.random() * 50) + 1,
    -      epoch: () => Math.floor(Math.random() * 10000) + 1000,
    -      ascending: () => Math.random() > 0.5
    -    };
    -
    -    // Get base parameters and randomize some values
    -    try {
    -      const baseParams = getTestParameters(category, queryType, 'testnet');
    -      const randomized = { ...baseParams };
    -
    -      for (const [key] of Object.entries(randomized)) {
    -        if (generators[key]) {
    -          randomized[key] = generators[key]();
    -        }
    -      }
    -
    -      return randomized;
    -    } catch (error) {
    -      console.warn(`Could not generate random parameters for ${category}.${queryType}`);
    -      return {};
    -    }
    -  }
    -}
    -
    -module.exports = { ParameterInjector };
    diff --git a/packages/wasm-sdk/test/ui-automation/utils/wasm-sdk-page.js b/packages/wasm-sdk/test/ui-automation/utils/wasm-sdk-page.js
    deleted file mode 100644
    index 4f876793368..00000000000
    --- a/packages/wasm-sdk/test/ui-automation/utils/wasm-sdk-page.js
    +++ /dev/null
    @@ -1,795 +0,0 @@
    -const { BaseTest } = require('./base-test');
    -const { expect } = require('@playwright/test');
    -
    -// Configuration for dynamic array parameters that require special handling
    -const DYNAMIC_ARRAY_PARAMETERS = {
    -  'ids': true,
    -  'identityIds': true,
    -  'identitiesIds': true,
    -  'tokenIds': true,
    -  'indexValues': true
    -};
    -
    -/**
    - * Page Object Model for WASM SDK index.html interface
    - */
    -class WasmSdkPage extends BaseTest {
    -  constructor(page) {
    -    super(page);
    -    
    -    // Define selectors for all interface elements
    -    this.selectors = {
    -      // Status and initialization
    -      statusBanner: '#statusBanner',
    -      statusBannerSuccess: '#statusBanner.success',
    -      statusBannerLoading: '#statusBanner.loading',
    -      statusBannerError: '#statusBanner.error',
    -      
    -      // Network controls
    -      mainnetRadio: '#mainnet',
    -      testnetRadio: '#testnet',
    -      networkIndicator: '#networkIndicator',
    -      trustedModeCheckbox: '#trustedMode',
    -      
    -      // Operation selectors
    -      operationType: '#operationType',
    -      queryCategory: '#queryCategory',
    -      queryType: '#queryType',
    -      
    -      // Query inputs
    -      queryInputs: '#queryInputs',
    -      queryTitle: '#queryTitle',
    -      dynamicInputs: '#dynamicInputs',
    -      
    -      // Authentication inputs
    -      authenticationInputs: '#authenticationInputs',
    -      identityId: '#identityId',
    -      privateKey: '#privateKey',
    -      assetLockProof: '#assetLockProof',
    -      
    -      // Proof toggle
    -      proofToggleContainer: '#proofToggleContainer',
    -      proofToggle: '#proofToggle',
    -      
    -      // Execute button
    -      executeQuery: '#executeQuery',
    -      
    -      // Results
    -      resultContainer: '.result-container',
    -      resultContent: '#identityInfo',
    -      resultHeader: '.result-header',
    -      
    -      // Action buttons
    -      clearButton: '#clearButton',
    -      copyButton: '#copyButton',
    -      clearCacheButton: '#clearCacheButton',
    -      
    -      // Advanced SDK configuration
    -      sdkConfigDetails: '.sdk-config',
    -      platformVersion: '#platformVersion',
    -      connectTimeout: '#connectTimeout',
    -      requestTimeout: '#requestTimeout',
    -      retries: '#retries',
    -      banFailedAddress: '#banFailedAddress',
    -      applyConfig: '#applyConfig'
    -    };
    -  }
    -
    -  /**
    -   * Initialize the SDK page
    -   */
    -  async initialize(network = 'testnet') {
    -    await this.navigateToSdk();
    -    await this.setNetwork(network);
    -    
    -    // Wait for SDK to be ready after network change
    -    await this.waitForSdkReady();
    -    
    -    return this;
    -  }
    -
    -  /**
    -   * Set up a query test scenario
    -   */
    -  async setupQuery(category, queryType, parameters = {}) {
    -    // Set operation type to queries
    -    await this.setOperationType('queries');
    -    
    -    // Set category and query type
    -    await this.setQueryCategory(category);
    -    await this.setQueryType(queryType);
    -    
    -    // Fill in parameters
    -    if (Object.keys(parameters).length > 0) {
    -      await this.fillQueryParameters(parameters);
    -    }
    -    
    -    return this;
    -  }
    -
    -  /**
    -   * Fill query parameters dynamically based on the input structure
    -   */
    -  async fillQueryParameters(parameters) {
    -    for (const [key, value] of Object.entries(parameters)) {
    -      await this.fillParameterByName(key, value);
    -    }
    -  }
    -
    -  /**
    -   * Fill a specific parameter by name
    -   */
    -  async fillParameterByName(paramName, value) {
    -    // Special handling for multiselect checkboxes (like purposes)
    -    if (paramName === 'purposes' && Array.isArray(value)) {
    -      for (const purposeValue of value) {
    -        const checkboxSelector = `input[name="purposes_${purposeValue}"][type="checkbox"]`;
    -        const checkbox = this.page.locator(checkboxSelector);
    -        if (await checkbox.count() > 0) {
    -          await checkbox.check();
    -        }
    -      }
    -      return;
    -    }
    -    
    -    // Special handling for array parameters that use dynamic input fields
    -    if (DYNAMIC_ARRAY_PARAMETERS[paramName]) {
    -      const enterValueInput = this.page.locator('input[placeholder="Enter value"]').first();
    -      const count = await enterValueInput.count();
    -      
    -      if (count > 0 && await enterValueInput.isVisible()) {
    -        await this.fillInputByType(enterValueInput, value);
    -        return;
    -      }
    -    }
    -    
    -    const inputSelector = `input[name="${paramName}"], select[name="${paramName}"], textarea[name="${paramName}"]`;
    -    const input = this.page.locator(inputSelector).first();
    -    
    -    // Check if input exists
    -    if (await input.count() === 0) {
    -      // Try alternative selectors based on common patterns
    -      const alternativeSelectors = [
    -        `#${paramName}`,
    -        `[id*="${paramName}"]`,
    -        `[placeholder*="${paramName}"]`,
    -        `label:has-text("${paramName}") + input`,
    -        `label:has-text("${paramName}") + select`,
    -        `label:has-text("${paramName}") + textarea`,
    -        // Special cases for contract and document fields
    -        `input[placeholder*="Contract ID"]`,
    -        `input[placeholder*="Document Type"]`,
    -        `textarea[placeholder*="JSON"]`,
    -        `textarea[placeholder*="Schema"]`
    -      ];
    -      
    -      let found = false;
    -      for (const selector of alternativeSelectors) {
    -        const altInput = this.page.locator(selector).first();
    -        if (await altInput.count() > 0) {
    -          await this.fillInputByType(altInput, value);
    -          found = true;
    -          break;
    -        }
    -      }
    -      
    -      if (!found) {
    -        console.warn(`⚠️  Could not find input for parameter: ${paramName}. Trying by label text...`);
    -        
    -        // Try finding by label text as last resort
    -        const labelSelectors = [
    -          `label:text-is("${paramName}") + input`,
    -          `label:text-is("${paramName}") + textarea`,
    -          `label:text-is("${paramName}") + select`
    -        ];
    -        
    -        for (const selector of labelSelectors) {
    -          const labelInput = this.page.locator(selector).first();
    -          if (await labelInput.count() > 0) {
    -            await this.fillInputByType(labelInput, value);
    -            found = true;
    -            break;
    -          }
    -        }
    -        
    -        if (!found) {
    -          console.warn(`⚠️  Could not find input for parameter: ${paramName} - skipping`);
    -        }
    -      }
    -    } else {
    -      await this.fillInputByType(input, value);
    -    }
    -  }
    -
    -  /**
    -   * Fill input based on its type
    -   */
    -  async fillInputByType(inputElement, value) {
    -    const tagName = await inputElement.evaluate(el => el.tagName.toLowerCase());
    -    const inputType = await inputElement.evaluate(el => el.type);
    -    
    -    if (tagName === 'select') {
    -      await inputElement.selectOption(value.toString());
    -    } else if (inputType === 'checkbox') {
    -      if (value) {
    -        await inputElement.check();
    -      } else {
    -        await inputElement.uncheck();
    -      }
    -    } else if (Array.isArray(value)) {
    -      // Handle array inputs - check if there's an "Add items" button nearby
    -      const success = await this.handleArrayInput(inputElement, value);
    -      if (!success) {
    -        // Fallback to JSON string if array handling fails
    -        await inputElement.fill(JSON.stringify(value));
    -      }
    -    } else if (typeof value === 'object') {
    -      // Handle object inputs (JSON)
    -      await inputElement.fill(JSON.stringify(value));
    -    } else {
    -      // Handle text/number inputs
    -      await inputElement.fill(value.toString());
    -    }
    -  }
    -
    -  /**
    -   * Handle array inputs with "Add items" button functionality
    -   */
    -  async handleArrayInput(baseElement, arrayValues) {
    -    try {
    -      // Look for existing input fields first (prioritize array container inputs)
    -      const arrayContainerInputs = this.page.locator('.array-input-container input[type="text"]');
    -      const allInputs = this.page.locator('input[type="text"], textarea').filter({
    -        hasNot: this.page.locator('[readonly]')
    -      });
    -      
    -      // Use array container inputs if available, otherwise use all inputs
    -      const existingInputs = await arrayContainerInputs.count() > 0 ? arrayContainerInputs : allInputs;
    -      const existingCount = await existingInputs.count();
    -
    -      // Fill the first existing field if available
    -      if (existingCount > 0 && arrayValues.length > 0) {
    -        const firstInput = existingInputs.first();
    -        await firstInput.fill(arrayValues[0].toString());
    -      }
    -
    -      // Look for "Add Item" button (specific to WASM SDK array inputs)
    -      const addButton = this.page.locator('button:has-text("+ Add Item"), button.add-array-item, button:has-text("Add Item"), button:has-text("Add"), button:has-text("add")').first();
    -      
    -      if (await addButton.count() === 0) {
    -        if (arrayValues.length <= 1) {
    -          return true;
    -        } else {
    -          return false;
    -        }
    -      }
    -
    -      // Add remaining items (starting from index 1)
    -      for (let i = 1; i < arrayValues.length; i++) {
    -        const value = arrayValues[i];
    -        
    -        // Click "Add items" button to create new field
    -        await addButton.click();
    -        await this.page.waitForTimeout(500); // Wait for new input to appear
    -        
    -        // Find all input fields again (should be one more now)
    -        const currentArrayInputs = this.page.locator('.array-input-container input[type="text"]');
    -        const currentAllInputs = this.page.locator('input[type="text"], textarea').filter({
    -          hasNot: this.page.locator('[readonly]')
    -        });
    -        
    -        // Use array container inputs if available
    -        const currentInputs = await currentArrayInputs.count() > 0 ? currentArrayInputs : currentAllInputs;
    -        const currentCount = await currentInputs.count();
    -        
    -        if (currentCount > existingCount + (i - 1)) {
    -          // Fill the newest input field
    -          const newInput = currentInputs.nth(currentCount - 1);
    -          await newInput.fill(value.toString());
    -        } else {
    -          console.warn(`Could not find new input field for item ${i + 1}`);
    -        }
    -      }
    -
    -      return true;
    -    } catch (error) {
    -      console.warn(`Array input handling failed: ${error.message}`);
    -      return false;
    -    }
    -  }
    -
    -  /**
    -   * Helper method to toggle proof information
    -   * @param {boolean} enable - true to enable, false to disable
    -   * @returns {boolean} - true if successful, false if proof toggle not available
    -   */
    -  async _toggleProofInfo(enable) {
    -    // Wait a moment for the UI to fully load after query setup
    -    await this.page.waitForTimeout(1000);
    -    
    -    const proofContainer = this.page.locator(this.selectors.proofToggleContainer);
    -    
    -    // Check if proof container exists and becomes visible
    -    try {
    -      // Wait longer and check if container becomes visible or is already attached
    -      await proofContainer.waitFor({ state: 'attached', timeout: 10000 });
    -      
    -      // Check if it's visible or can be made visible
    -      const isVisible = await proofContainer.isVisible();
    -      if (!isVisible) {
    -        // It might be hidden by display:none, check if it exists in the DOM
    -        const count = await proofContainer.count();
    -        if (count === 0) {
    -          console.log('⚠️ Proof toggle container not found in DOM');
    -          return false;
    -        }
    -        
    -        // Try to wait a bit more for it to become visible
    -        try {
    -          await proofContainer.waitFor({ state: 'visible', timeout: 3000 });
    -        } catch {
    -          console.log('⚠️ Proof toggle container exists but remains hidden - may not be available for this query type');
    -          return false;
    -        }
    -      }
    -      
    -      const proofToggle = this.page.locator(this.selectors.proofToggle);
    -      
    -      // Check current state and toggle if needed
    -      const isChecked = await proofToggle.isChecked();
    -      const needsToggle = enable ? !isChecked : isChecked;
    -      
    -      if (needsToggle) {
    -        // Click on the toggle switch container or label to toggle it
    -        // Since it's a custom toggle, we need to click the label or toggle-slider
    -        const toggleLabel = proofContainer.locator('label');
    -        await toggleLabel.click();
    -        
    -        // Wait for the toggle to reach the expected state
    -        if (enable) {
    -          await expect(proofToggle).toBeChecked();
    -          console.log('Proof toggle confirmed as checked');
    -        } else {
    -          await expect(proofToggle).not.toBeChecked();
    -          console.log('Proof toggle confirmed as unchecked');
    -        }
    -      }
    -      
    -      console.log(`Proof info ${enable ? 'enabled' : 'disabled'}`);
    -      return true;
    -    } catch (error) {
    -      console.log(`⚠️ Proof toggle not available for this query type: ${error.message}`);
    -      return false;
    -    }
    -  }
    -
    -  /**
    -   * Enable proof information toggle
    -   */
    -  async enableProofInfo() {
    -    return this._toggleProofInfo(true);
    -  }
    -
    -  /**
    -   * Disable proof information toggle
    -   */
    -  async disableProofInfo() {
    -    return this._toggleProofInfo(false);
    -  }
    -
    -  /**
    -   * Get the query description text
    -   */
    -  async getQueryDescription() {
    -    const description = this.page.locator('#queryDescription');
    -    if (await description.count() > 0) {
    -      return await description.textContent();
    -    }
    -    return null;
    -  }
    -
    -  /**
    -   * Check if authentication inputs are visible
    -   */
    -  async hasAuthenticationInputs() {
    -    const authInputs = this.page.locator(this.selectors.authenticationInputs);
    -    return await authInputs.isVisible();
    -  }
    -
    -  /**
    -   * Fill authentication information
    -   */
    -  async fillAuthentication(identityId, privateKey, assetLockProof = null) {
    -    if (await this.hasAuthenticationInputs()) {
    -      if (identityId) {
    -        await this.fillInput(this.selectors.identityId, identityId);
    -      }
    -      if (privateKey) {
    -        await this.fillInput(this.selectors.privateKey, privateKey);
    -      }
    -      if (assetLockProof) {
    -        await this.fillInput(this.selectors.assetLockProof, assetLockProof);
    -      }
    -      console.log('Authentication filled');
    -    }
    -  }
    -
    -  /**
    -   * Get current status banner state
    -   */
    -  async getStatusBannerState() {
    -    const banner = this.page.locator(this.selectors.statusBanner);
    -    const classList = await banner.getAttribute('class');
    -    
    -    // Handle null classList gracefully
    -    if (!classList) return 'unknown';
    -    
    -    if (classList.includes('success')) return 'success';
    -    if (classList.includes('error')) return 'error';
    -    if (classList.includes('loading')) return 'loading';
    -    return 'unknown';
    -  }
    -
    -  /**
    -   * Get status banner text
    -   */
    -  async getStatusBannerText() {
    -    const banner = this.page.locator(this.selectors.statusBanner);
    -    return await banner.textContent();
    -  }
    -
    -  /**
    -   * Get proof content when in split view mode
    -   */
    -  async getProofContent() {
    -    await this.page.waitForTimeout(500); // Brief wait for content to render
    -    
    -    const proofContent = this.page.locator('#proofInfo');
    -    const isVisible = await proofContent.isVisible();
    -    
    -    if (!isVisible) {
    -      console.log('⚠️ Proof content not visible');
    -      return '';
    -    }
    -    
    -    const content = await proofContent.textContent();
    -    return content || '';
    -  }
    -
    -  /**
    -   * Check if result is displayed in split view (proof mode)
    -   */
    -  async isInSplitView() {
    -    const dataSection = this.page.locator('.result-data-section');
    -    const proofSection = this.page.locator('.result-proof-section');
    -    
    -    const dataSectionVisible = await dataSection.isVisible();
    -    const proofSectionVisible = await proofSection.isVisible();
    -    
    -    return dataSectionVisible && proofSectionVisible;
    -  }
    -
    -  /**
    -   * Wait for query execution to complete and return the result
    -   */
    -  async executeQueryAndGetResult() {
    -    const success = await this.executeQuery();
    -    const result = await this.getResultContent();
    -    const hasError = await this.hasErrorResult();
    -    
    -    // Check if we're in split view mode (proof mode)
    -    const inSplitView = await this.isInSplitView();
    -    let proofContent = null;
    -    
    -    if (inSplitView) {
    -      proofContent = await this.getProofContent();
    -    }
    -    
    -    return {
    -      success,
    -      result,
    -      hasError,
    -      statusText: await this.getStatusBannerText(),
    -      inSplitView,
    -      proofContent
    -    };
    -  }
    -
    -  /**
    -   * Configure advanced SDK settings
    -   */
    -  async configureAdvancedSDK(options) {
    -    // Open SDK config if it's closed
    -    const configDetails = this.page.locator(this.selectors.sdkConfigDetails);
    -    const isOpen = await configDetails.getAttribute('open') !== null;
    -    
    -    if (!isOpen) {
    -      await configDetails.locator('summary').click();
    -    }
    -    
    -    // Fill configuration options
    -    if (options.platformVersion) {
    -      await this.fillInput(this.selectors.platformVersion, options.platformVersion);
    -    }
    -    if (options.connectTimeout) {
    -      await this.fillInput(this.selectors.connectTimeout, options.connectTimeout);
    -    }
    -    if (options.requestTimeout) {
    -      await this.fillInput(this.selectors.requestTimeout, options.requestTimeout);
    -    }
    -    if (options.retries) {
    -      await this.fillInput(this.selectors.retries, options.retries);
    -    }
    -    if (options.banFailedAddress !== undefined) {
    -      const checkbox = this.page.locator(this.selectors.banFailedAddress);
    -      if (options.banFailedAddress) {
    -        await checkbox.check();
    -      } else {
    -        await checkbox.uncheck();
    -      }
    -    }
    -    
    -    // Apply configuration
    -    await this.clickButton(this.selectors.applyConfig);
    -    
    -    console.log('Advanced SDK configuration applied');
    -  }
    -
    -  /**
    -   * Get available query categories
    -   */
    -  async getAvailableQueryCategories() {
    -    const categorySelect = this.page.locator(this.selectors.queryCategory);
    -    const options = await categorySelect.locator('option').allTextContents();
    -    return options.filter(option => option.trim() !== '' && option !== 'Select Query Category');
    -  }
    -
    -  /**
    -   * Get available query types for current category
    -   */
    -  async getAvailableQueryTypes() {
    -    const queryTypeSelect = this.page.locator(this.selectors.queryType);
    -    await queryTypeSelect.waitFor({ state: 'visible' });
    -    const options = await queryTypeSelect.locator('option').allTextContents();
    -    return options.filter(option => option.trim() !== '' && option !== 'Select Query Type');
    -  }
    -
    -  /**
    -   * Set up a state transition test scenario
    -   */
    -  async setupStateTransition(category, transitionType, parameters = {}) {
    -    // Set operation type to transitions
    -    await this.setOperationType('transitions');
    -    
    -    // Set category and transition type
    -    await this.setQueryCategory(category);
    -    await this.setQueryType(transitionType);
    -    
    -    // Fill in parameters
    -    if (Object.keys(parameters).length > 0) {
    -      await this.fillStateTransitionParameters(parameters);
    -    }
    -    
    -    return this;
    -  }
    -
    -  /**
    -   * Fill state transition parameters
    -   */
    -  async fillStateTransitionParameters(parameters) {
    -    // Handle state transition specific parameters
    -    for (const [key, value] of Object.entries(parameters)) {
    -      if (key === 'assetLockProof') {
    -        await this.fillAssetLockProof(value);
    -      } else if (key === 'privateKey') {
    -        await this.fillPrivateKey(value);
    -      } else if (key === 'identityId') {
    -        await this.fillIdentityId(value);
    -      } else if (key === 'seedPhrase') {
    -        await this.fillSeedPhrase(value);
    -      } else if (key === 'identityIndex') {
    -        await this.fillIdentityIndex(value);
    -      } else if (key === 'keySelectionMode') {
    -        // Skip keySelectionMode for now - only needed for identity create
    -        console.log('Skipping keySelectionMode field (identity create only)');
    -      } else if (key === 'documentFields') {
    -        // Handle document fields - these need to be filled after schema fetch
    -        console.log('Document fields will be handled after schema fetch');
    -      } else if (key === 'description') {
    -        // Skip description field - it's just for documentation
    -        console.log('Skipping description field (documentation only)');
    -      } else {
    -        // Use the general parameter filling method for other parameters
    -        await this.fillParameterByName(key, value);
    -      }
    -    }
    -  }
    -
    -  /**
    -   * Fill asset lock proof field
    -   */
    -  async fillAssetLockProof(assetLockProof) {
    -    await this.fillInput(this.selectors.assetLockProof, assetLockProof);
    -    console.log('Asset lock proof filled');
    -  }
    -
    -  /**
    -   * Fill private key field
    -   */
    -  async fillPrivateKey(privateKey) {
    -    await this.fillInput(this.selectors.privateKey, privateKey);
    -    console.log('Private key filled');
    -  }
    -
    -  /**
    -   * Fill identity ID field (for top-up transitions)
    -   */
    -  async fillIdentityId(identityId) {
    -    await this.fillInput(this.selectors.identityId, identityId);
    -    console.log('Identity ID filled');
    -  }
    -
    -  /**
    -   * Fill seed phrase field
    -   */
    -  async fillSeedPhrase(seedPhrase) {
    -    const seedPhraseInput = this.page.locator('textarea[name="seedPhrase"]');
    -    await seedPhraseInput.fill(seedPhrase);
    -    console.log('Seed phrase filled');
    -  }
    -
    -  /**
    -   * Fill identity index field
    -   */
    -  async fillIdentityIndex(identityIndex) {
    -    const identityIndexInput = this.page.locator('input[name="identityIndex"]');
    -    await identityIndexInput.fill(identityIndex.toString());
    -    console.log('Identity index filled');
    -  }
    -
    -  /**
    -   * Set key selection mode (simple/advanced)
    -   */
    -  async setKeySelectionMode(mode) {
    -    const keySelectionSelect = this.page.locator('select[name="keySelectionMode"]');
    -    await keySelectionSelect.selectOption(mode);
    -    console.log(`Key selection mode set to: ${mode}`);
    -  }
    -
    -  /**
    -   * Execute state transition and get result (similar to executeQueryAndGetResult)
    -   */
    -  async executeStateTransitionAndGetResult() {
    -    const success = await this.executeQuery(); // Same execute button works for transitions
    -    const result = await this.getResultContent();
    -    const hasError = await this.hasErrorResult();
    -    
    -    return {
    -      success,
    -      result,
    -      hasError,
    -      statusText: await this.getStatusBannerText()
    -    };
    -  }
    -
    -  /**
    -   * Check if state transition authentication inputs are visible
    -   */
    -  async hasStateTransitionAuthInputs() {
    -    const authInputs = this.page.locator(this.selectors.authenticationInputs);
    -    const assetLockProofGroup = this.page.locator('#assetLockProofGroup');
    -    
    -    const authVisible = await authInputs.isVisible();
    -    const assetLockVisible = await assetLockProofGroup.isVisible();
    -    
    -    return authVisible && assetLockVisible;
    -  }
    -
    -  /**
    -   * Fetch document schema and generate dynamic fields for document transitions
    -   */
    -  async fetchDocumentSchema() {
    -    console.log('Attempting to fetch document schema...');
    -    
    -    // First check if the function exists and call it directly
    -    try {
    -      await this.page.evaluate(() => {
    -        if (typeof window.fetchDocumentSchema === 'function') {
    -          return window.fetchDocumentSchema();
    -        } else {
    -          throw new Error('fetchDocumentSchema function not found');
    -        }
    -      });
    -      console.log('Called fetchDocumentSchema function directly');
    -    } catch (error) {
    -      console.error('Error calling fetchDocumentSchema:', error);
    -      throw error;
    -    }
    -    
    -    // Wait for schema to load and fields to be generated
    -    await this.page.waitForTimeout(3000);
    -    
    -    // Check if dynamic fields container is visible
    -    const dynamicFieldsContainer = this.page.locator('#dynamic_documentFields');
    -    await dynamicFieldsContainer.waitFor({ state: 'visible', timeout: 15000 });
    -    
    -    console.log('Document schema fetched and fields generated');
    -  }
    -
    -  /**
    -   * Fill a specific document field by name
    -   */
    -  async fillDocumentField(fieldName, value) {
    -    const fieldInput = this.page.locator(`#dynamic_documentFields input[data-field-name="${fieldName}"], #dynamic_documentFields textarea[data-field-name="${fieldName}"]`);
    -    
    -    // Convert value to string based on type
    -    let stringValue = '';
    -    if (value === null || value === undefined) {
    -      stringValue = '';
    -    } else if (typeof value === 'object') {
    -      stringValue = JSON.stringify(value);
    -    } else {
    -      stringValue = value.toString();
    -    }
    -    
    -    await fieldInput.fill(stringValue);
    -    console.log(`Document field '${fieldName}' filled with value: ${stringValue}`);
    -  }
    -
    -  /**
    -   * Fill multiple document fields
    -   */
    -  async fillDocumentFields(fields) {
    -    for (const [fieldName, value] of Object.entries(fields)) {
    -      await this.fillDocumentField(fieldName, value);
    -    }
    -    console.log('All document fields filled');
    -  }
    -
    -  /**
    -   * Load existing document for replacement (gets revision and populates fields)
    -   */
    -  async loadExistingDocument() {
    -    console.log('Loading existing document for replacement...');
    -    
    -    // Call the loadExistingDocument function directly via page.evaluate
    -    try {
    -      await this.page.evaluate(() => {
    -        if (typeof window.loadExistingDocument === 'function') {
    -          return window.loadExistingDocument();
    -        } else {
    -          throw new Error('loadExistingDocument function not found');
    -        }
    -      });
    -      console.log('Existing document loaded successfully');
    -    } catch (error) {
    -      console.error('Error loading existing document:', error);
    -      throw error;
    -    }
    -    
    -    // Wait for the document to be loaded and fields to be populated
    -    await this.page.waitForTimeout(3000);
    -    
    -    console.log('Document loaded and fields populated');
    -  }
    -
    -  /**
    -   * Fill complete state transition authentication (asset lock proof + private key)
    -   */
    -  async fillStateTransitionAuthentication(assetLockProof, privateKey, identityId = null) {
    -    if (await this.hasStateTransitionAuthInputs()) {
    -      if (assetLockProof) {
    -        await this.fillAssetLockProof(assetLockProof);
    -      }
    -      if (privateKey) {
    -        await this.fillPrivateKey(privateKey);
    -      }
    -      if (identityId) {
    -        await this.fillIdentityId(identityId);
    -      }
    -      console.log('State transition authentication filled');
    -    }
    -  }
    -}
    -
    -module.exports = { WasmSdkPage };
    diff --git a/packages/wasm-sdk/test/utilities-simple.test.mjs b/packages/wasm-sdk/test/utilities-simple.test.mjs
    deleted file mode 100644
    index 3230d8bc11a..00000000000
    --- a/packages/wasm-sdk/test/utilities-simple.test.mjs
    +++ /dev/null
    @@ -1,307 +0,0 @@
    -#!/usr/bin/env node
    -// utilities-simple.test.mjs - Simplified utility function tests avoiding panics
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from '../pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test utilities
    -let passed = 0;
    -let failed = 0;
    -
    -async function test(name, fn) {
    -    try {
    -        await fn();
    -        console.log(`βœ… ${name}`);
    -        passed++;
    -    } catch (error) {
    -        console.log(`❌ ${name}`);
    -        console.log(`   ${error.message}`);
    -        failed++;
    -    }
    -}
    -
    -function describe(name) {
    -    console.log(`\n${name}`);
    -}
    -
    -console.log('\nUtility Functions Tests (Simplified)\n');
    -
    -// SDK Version Test
    -describe('SDK Version and Initialization');
    -
    -await test('Create SDK and check version', async () => {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    const sdk = await builder.build();
    -    
    -    const version = sdk.version();
    -    if (typeof version !== 'number') {
    -        throw new Error('Version should be a number');
    -    }
    -    if (version < 1) {
    -        throw new Error('Version should be at least 1');
    -    }
    -    console.log(`   SDK version: ${version}`);
    -    
    -    sdk.free();
    -});
    -
    -// Trusted Quorum Prefetch Tests
    -describe('Trusted Quorum Prefetch');
    -
    -await test('prefetch_trusted_quorums_mainnet', async () => {
    -    try {
    -        await wasmSdk.prefetch_trusted_quorums_mainnet();
    -        // Success means network is available
    -    } catch (error) {
    -        // Network error is acceptable
    -        if (!error.message.includes('network') && !error.message.includes('fetch')) {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('prefetch_trusted_quorums_testnet', async () => {
    -    try {
    -        await wasmSdk.prefetch_trusted_quorums_testnet();
    -        // Success means network is available
    -    } catch (error) {
    -        // Network error is acceptable
    -        if (!error.message.includes('network') && !error.message.includes('fetch')) {
    -            throw error;
    -        }
    -    }
    -});
    -
    -// Test Serialization
    -describe('Test Serialization (if method exists)');
    -
    -await test('testSerialization method availability', async () => {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    const sdk = await builder.build();
    -    
    -    if (typeof sdk.testSerialization === 'function') {
    -        console.log('   testSerialization method exists');
    -        
    -        // Try calling it with a valid type
    -        const result = sdk.testSerialization('simple');
    -        console.log(`   Result type: ${typeof result}, value:`, result);
    -        
    -        // Should return a proper serialized object
    -        if (typeof result !== 'object' || result === null) {
    -            throw new Error(`Expected object result, got ${typeof result}`);
    -        }
    -    } else {
    -        console.log('   testSerialization method not found');
    -    }
    -    
    -    sdk.free();
    -});
    -
    -// Error Handling Tests
    -describe('Error Handling');
    -
    -await test('Using null SDK should fail gracefully', async () => {
    -    try {
    -        await wasmSdk.get_status(null);
    -        throw new Error('Should have failed with null SDK');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected error
    -    }
    -});
    -
    -await test('Using undefined SDK should fail gracefully', async () => {
    -    try {
    -        await wasmSdk.get_status(undefined);
    -        throw new Error('Should have failed with undefined SDK');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected error
    -    }
    -});
    -
    -await test('Using freed SDK should fail gracefully', async () => {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    const sdk = await builder.build();
    -    sdk.free();
    -    
    -    try {
    -        sdk.version();
    -        throw new Error('Should have failed with freed SDK');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected error
    -    }
    -});
    -
    -// Type Validation Tests
    -describe('Type Validation');
    -
    -await test('String parameter type validation', async () => {
    -    try {
    -        // Pass number where string expected
    -        wasmSdk.validate_mnemonic(123);
    -        throw new Error('Should have failed with wrong type');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected type error
    -    }
    -});
    -
    -await test('Array parameter type validation', async () => {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    const sdk = await builder.build();
    -    
    -    try {
    -        // Pass string where array expected
    -        await wasmSdk.get_path_elements(sdk, "not-an-array", []);
    -        throw new Error('Should have failed with non-array');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected type error
    -    }
    -    
    -    sdk.free();
    -});
    -
    -await test('Number parameter type validation', async () => {
    -    try {
    -        // Pass string where number expected
    -        wasmSdk.generate_mnemonic("twelve");
    -        throw new Error('Should have failed with wrong type');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected type error
    -    }
    -});
    -
    -// Network-dependent utility functions
    -describe('Network-dependent Utilities');
    -
    -// TODO: Enable this test once we have a valid state transition hash to test with
    -// This test is currently disabled because:
    -// 1. Using an invalid hash (all zeros) only tests the error path, not success path
    -// 2. It takes 80+ seconds to timeout with invalid hash, slowing down test suite
    -// 3. It has Rust ownership issues that prevent proper execution
    -// 4. To be valuable, we need a real state transition hash to verify the function
    -//    correctly retrieves and parses state transition results
    -/*
    -await test('wait_for_state_transition_result - with valid hash', async () => {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    const sdk = await builder.build();
    -    
    -    // TODO: Replace with actual valid state transition hash from a real transaction
    -    const validHash = "REPLACE_WITH_ACTUAL_VALID_STATE_TRANSITION_HASH";
    -    
    -    try {
    -        const result = await wasmSdk.wait_for_state_transition_result(sdk, validHash);
    -        
    -        // Verify result structure
    -        if (!result || typeof result !== 'object') {
    -            throw new Error('Expected valid result object');
    -        }
    -        
    -        // TODO: Add more specific validation based on expected response structure
    -        
    -    } finally {
    -        sdk.free();
    -    }
    -});
    -*/
    -
    -await test('get_path_elements - requires network', async () => {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    const sdk = await builder.build();
    -    
    -    try {
    -        const result = await wasmSdk.get_path_elements(sdk, [], []);
    -        // If it succeeds, check result
    -        if (result && typeof result === 'object') {
    -            console.log('   Successfully got path elements');
    -        }
    -    } catch (error) {
    -        // Network error is expected
    -        console.log('   Expected network error');
    -    }
    -    
    -    sdk.free();
    -});
    -
    -// Start function
    -describe('Start Function');
    -
    -await test('start function exists', async () => {
    -    // The start function should exist
    -    if (typeof wasmSdk.start !== 'function') {
    -        throw new Error('start function not found');
    -    }
    -    
    -    // Since the WASM module auto-calls start() on initialization,
    -    // calling it again will cause a panic due to tracing already being set.
    -    // This is expected behavior - start() should only be called once.
    -    
    -    // We'll test that it exists and is callable, but we won't call it
    -    // since it's already been called during WASM initialization
    -    console.log('   start function exists and has been called during WASM init');
    -    console.log('   (calling it again would panic due to tracing already initialized)');
    -});
    -
    -// Function existence checks
    -describe('Function Existence');
    -
    -await test('All expected utility functions exist', () => {
    -    const utilityFunctions = [
    -        'prefetch_trusted_quorums_mainnet',
    -        'prefetch_trusted_quorums_testnet',
    -        'wait_for_state_transition_result',
    -        'start',
    -        'get_path_elements'
    -    ];
    -    
    -    for (const fn of utilityFunctions) {
    -        if (typeof wasmSdk[fn] !== 'function') {
    -            throw new Error(`${fn} not found`);
    -        }
    -    }
    -    
    -    console.log('   All utility functions found');
    -});
    -
    -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`);
    -process.exit(failed > 0 ? 1 : 0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test/utilities.test.mjs b/packages/wasm-sdk/test/utilities.test.mjs
    deleted file mode 100644
    index 5767761c007..00000000000
    --- a/packages/wasm-sdk/test/utilities.test.mjs
    +++ /dev/null
    @@ -1,364 +0,0 @@
    -#!/usr/bin/env node
    -// utilities.test.mjs - Tests for utility functions
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from '../pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test utilities
    -let passed = 0;
    -let failed = 0;
    -
    -async function test(name, fn) {
    -    try {
    -        await fn();
    -        console.log(`βœ… ${name}`);
    -        passed++;
    -    } catch (error) {
    -        console.log(`❌ ${name}`);
    -        console.log(`   ${error.message}`);
    -        failed++;
    -    }
    -}
    -
    -function describe(name) {
    -    console.log(`\n${name}`);
    -}
    -
    -console.log('\nUtility Functions Tests\n');
    -
    -// Test Serialization Tests
    -describe('Test Serialization');
    -
    -// Initialize SDK for tests that need it
    -let sdk = null;
    -try {
    -    const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -    sdk = await builder.build();
    -} catch (error) {
    -    console.log('   Failed to create SDK for tests');
    -}
    -
    -if (sdk) {
    -    await test('testSerialization - check method exists', () => {
    -        if (typeof sdk.testSerialization !== 'function') {
    -            throw new Error('testSerialization method not found on SDK');
    -        }
    -    });
    -
    -    await test('testSerialization - string type', () => {
    -        const result = sdk.testSerialization('string');
    -        if (result === undefined) {
    -            throw new Error('Result is undefined');
    -        }
    -        if (typeof result !== 'object' && result !== null) {
    -            throw new Error(`Should return object, got ${typeof result}`);
    -        }
    -    });
    -
    -    await test('testSerialization - number type', () => {
    -        const result = sdk.testSerialization('number');
    -        if (result === undefined) {
    -            throw new Error('Result is undefined');
    -        }
    -        if (typeof result !== 'object' && result !== null) {
    -            throw new Error(`Should return object, got ${typeof result}`);
    -        }
    -    });
    -
    -    await test('testSerialization - array type', () => {
    -        const result = sdk.testSerialization('array');
    -        if (result === undefined) {
    -            throw new Error('Result is undefined');
    -        }
    -        if (typeof result !== 'object' && result !== null) {
    -            throw new Error(`Should return object, got ${typeof result}`);
    -        }
    -    });
    -
    -    await test('testSerialization - object type', () => {
    -        const result = sdk.testSerialization('object');
    -        if (result === undefined) {
    -            throw new Error('Result is undefined');
    -        }
    -        if (typeof result !== 'object' && result !== null) {
    -            throw new Error(`Should return object, got ${typeof result}`);
    -        }
    -    });
    -
    -    await test('testSerialization - invalid type', () => {
    -        try {
    -            const result = sdk.testSerialization('invalid');
    -            // If it doesn't throw, check what it returns
    -            if (result !== undefined && result !== null) {
    -                throw new Error('Should have thrown error or returned null/undefined for invalid type');
    -            }
    -        } catch (error) {
    -            // Expected error
    -        }
    -    });
    -}
    -
    -// Trusted Quorum Prefetch Tests
    -describe('Trusted Quorum Prefetch');
    -
    -await test('prefetch_trusted_quorums_mainnet - expected to work or timeout', async () => {
    -    try {
    -        // This might timeout or succeed depending on network
    -        const promise = wasmSdk.prefetch_trusted_quorums_mainnet();
    -        
    -        // Set a timeout to prevent hanging
    -        const timeoutPromise = new Promise((_, reject) => 
    -            setTimeout(() => reject(new Error('Timeout')), 5000)
    -        );
    -        
    -        await Promise.race([promise, timeoutPromise]);
    -        // If it succeeds, that's fine
    -    } catch (error) {
    -        // Timeout or network error is acceptable
    -        if (!error.message.includes('Timeout') && !error.message.includes('network')) {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('prefetch_trusted_quorums_testnet - expected to work or timeout', async () => {
    -    try {
    -        // This might timeout or succeed depending on network
    -        const promise = wasmSdk.prefetch_trusted_quorums_testnet();
    -        
    -        // Set a timeout to prevent hanging
    -        const timeoutPromise = new Promise((_, reject) => 
    -            setTimeout(() => reject(new Error('Timeout')), 5000)
    -        );
    -        
    -        await Promise.race([promise, timeoutPromise]);
    -        // If it succeeds, that's fine
    -    } catch (error) {
    -        // Timeout or network error is acceptable
    -        if (!error.message.includes('Timeout') && !error.message.includes('network')) {
    -            throw error;
    -        }
    -    }
    -});
    -
    -// Wait for State Transition Tests (requires network and valid hash)
    -describe('Wait for State Transition');
    -
    -if (sdk) {
    -    await test('wait_for_state_transition_result - invalid hash', async () => {
    -        try {
    -            // This will fail with invalid hash
    -            await wasmSdk.wait_for_state_transition_result(
    -                sdk,
    -                "0000000000000000000000000000000000000000000000000000000000000000"
    -            );
    -            throw new Error('Should have failed with invalid hash');
    -        } catch (error) {
    -            // Expected to fail
    -            if (error.message.includes('Should have failed')) {
    -                throw error;
    -            }
    -        }
    -    });
    -
    -    await test('wait_for_state_transition_result - malformed hash', async () => {
    -        try {
    -            await wasmSdk.wait_for_state_transition_result(sdk, "invalid-hash");
    -            throw new Error('Should have failed with malformed hash');
    -        } catch (error) {
    -            // Expected to fail
    -            if (error.message.includes('Should have failed')) {
    -                throw error;
    -            }
    -        }
    -    });
    -}
    -
    -// Testing Functions (not part of public API but useful for debugging)
    -describe('Testing Functions');
    -
    -if (sdk) {
    -    // These are test functions that might not be in the public API
    -    // Skip identity_put as it causes a panic
    -    await test('identity_put - test function (skipped due to panic)', async () => {
    -        // This function causes a panic, so we skip it
    -        console.log('   Skipped: causes panic in test environment');
    -    });
    -
    -    await test('epoch_testing - test function', async () => {
    -        try {
    -            await wasmSdk.epoch_testing();
    -            // If it succeeds, that's fine
    -        } catch (error) {
    -            // Might fail without proper setup
    -        }
    -    });
    -
    -    await test('docs_testing - test function', async () => {
    -        try {
    -            await wasmSdk.docs_testing(sdk);
    -            // If it succeeds, that's fine
    -        } catch (error) {
    -            // Expected to fail without network
    -        }
    -    });
    -}
    -
    -// SDK Version Test
    -describe('SDK Version');
    -
    -if (sdk) {
    -    await test('SDK version method', () => {
    -        const version = sdk.version();
    -        if (typeof version !== 'number') {
    -            throw new Error('Version should be a number');
    -        }
    -        if (version < 1) {
    -            throw new Error('Version should be at least 1');
    -        }
    -        console.log(`   SDK version: ${version}`);
    -    });
    -}
    -
    -// Start Function Test
    -describe('Start Function');
    -
    -await test('start - initialization function', async () => {
    -    try {
    -        await wasmSdk.start();
    -        // If it succeeds, that's fine
    -    } catch (error) {
    -        // Might fail if already started or other reasons
    -        // This is acceptable
    -    }
    -});
    -
    -// Path Elements Test (requires valid paths and keys)
    -describe('Path Elements');
    -
    -if (sdk) {
    -    await test('get_path_elements - empty arrays', async () => {
    -        try {
    -            const result = await wasmSdk.get_path_elements(sdk, [], []);
    -            // Should handle empty arrays gracefully
    -            if (!result) {
    -                throw new Error('Should return a result even for empty arrays');
    -            }
    -        } catch (error) {
    -            // Might fail without network
    -            // This is acceptable
    -        }
    -    });
    -
    -    await test('get_path_elements - sample path', async () => {
    -        try {
    -            const path = ["contracts", "documents"];
    -            const keys = ["somekey"];
    -            const result = await wasmSdk.get_path_elements(sdk, path, keys);
    -            // If it succeeds, check result structure
    -            if (result && typeof result !== 'object') {
    -                throw new Error('Should return an object');
    -            }
    -        } catch (error) {
    -            // Expected to fail without proper setup
    -            // This is acceptable
    -        }
    -    });
    -}
    -
    -// Additional Utility Patterns
    -describe('Error Handling');
    -
    -await test('Function with null SDK should fail gracefully', async () => {
    -    try {
    -        await wasmSdk.get_status(null);
    -        throw new Error('Should have failed with null SDK');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected error
    -    }
    -});
    -
    -await test('Function with freed SDK should fail gracefully', async () => {
    -    try {
    -        const builder = wasmSdk.WasmSdkBuilder.new_testnet();
    -        const tempSdk = await builder.build();
    -        tempSdk.free();
    -        
    -        // Try to use freed SDK
    -        await wasmSdk.get_status(tempSdk);
    -        throw new Error('Should have failed with freed SDK');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected error
    -    }
    -});
    -
    -// Type Validation
    -describe('Type Validation');
    -
    -await test('Functions validate parameter types', async () => {
    -    if (!sdk) return;
    -    
    -    try {
    -        // Pass wrong type to a function expecting string
    -        await wasmSdk.wait_for_state_transition_result(sdk, 123);
    -        throw new Error('Should have failed with wrong type');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected type error
    -    }
    -});
    -
    -await test('Arrays are properly validated', async () => {
    -    if (!sdk) return;
    -    
    -    try {
    -        // Pass non-array to function expecting array
    -        await wasmSdk.get_path_elements(sdk, "not-an-array", []);
    -        throw new Error('Should have failed with non-array');
    -    } catch (error) {
    -        if (error.message.includes('Should have failed')) {
    -            throw error;
    -        }
    -        // Expected type error
    -    }
    -});
    -
    -// Clean up
    -if (sdk) {
    -    sdk.free();
    -}
    -
    -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`);
    -process.exit(failed > 0 ? 1 : 0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test/voting-contested-resources.test.mjs b/packages/wasm-sdk/test/voting-contested-resources.test.mjs
    deleted file mode 100644
    index 6b60d86fa20..00000000000
    --- a/packages/wasm-sdk/test/voting-contested-resources.test.mjs
    +++ /dev/null
    @@ -1,195 +0,0 @@
    -#!/usr/bin/env node
    -// voting-contested-resources.test.mjs - Tests for voting and contested resources queries
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from '../pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, '../pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test utilities
    -let passed = 0;
    -let failed = 0;
    -
    -async function test(name, fn) {
    -    try {
    -        await fn();
    -        console.log(`βœ… ${name}`);
    -        passed++;
    -    } catch (error) {
    -        console.log(`❌ ${name}`);
    -        console.log(`   ${error.message}`);
    -        failed++;
    -    }
    -}
    -
    -function describe(name) {
    -    console.log(`\n${name}`);
    -}
    -
    -console.log('\nVoting & Contested Resources Query Tests\n');
    -
    -// Test values from docs.html
    -const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec';
    -const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk';
    -// Username for TEST_IDENTITY is "therealslimshaddy5.dash"
    -const TEST_PARENT_DOMAIN = 'dash';
    -const TEST_LABEL = 'therealslimshaddy5';
    -
    -// Initialize SDK - prefetch quorums for trusted mode
    -console.log('Prefetching trusted quorums...');
    -try {
    -    await wasmSdk.prefetch_trusted_quorums_testnet();
    -    console.log('Quorums prefetched successfully');
    -} catch (error) {
    -    console.log('Warning: Could not prefetch quorums:', error.message);
    -}
    -
    -// Use trusted builder as required for WASM
    -const builder = wasmSdk.WasmSdkBuilder.new_testnet_trusted();
    -const sdk = await builder.build();
    -
    -// Contested Resources Tests
    -describe('Contested Resources Queries');
    -
    -await test('get_contested_resources - fetch contested domain names', async () => {
    -    try {
    -        // Based on Rust signature, we need all parameters
    -        const result = await wasmSdk.get_contested_resources(
    -            sdk,
    -            'domain',                           // document_type_name
    -            DPNS_CONTRACT,                      // data_contract_id
    -            'parentNameAndLabel',               // index_name
    -            null,                               // start_at_value
    -            100,                                // limit
    -            null,                               // offset
    -            true                                // order_ascending
    -        );
    -        console.log(`   Found ${result?.contestedResources?.length || 0} contested resources`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_contested_resource_vote_state - get vote state for contested resource', async () => {
    -    try {
    -        const result = await wasmSdk.get_contested_resource_vote_state(
    -            sdk,
    -            DPNS_CONTRACT,                      // data_contract_id
    -            'domain',                           // document_type_name
    -            'parentNameAndLabel',               // index_name
    -            [TEST_PARENT_DOMAIN, TEST_LABEL],   // index_values: [parent domain, label]
    -            'documentTypeName',                 // result_type
    -            null,                               // allow_include_locked_and_abstaining_vote_tally
    -            null,                               // start_at_identifier_info
    -            100,                                // count
    -            true                                // order_ascending
    -        );
    -        console.log(`   Vote state retrieved`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_contested_resource_voters_for_identity - get voters for identity', async () => {
    -    try {
    -        const result = await wasmSdk.get_contested_resource_voters_for_identity(
    -            sdk,
    -            DPNS_CONTRACT,                      // contract_id
    -            'domain',                           // document_type_name
    -            'parentNameAndLabel',               // index_name
    -            [TEST_PARENT_DOMAIN, TEST_LABEL],   // index_values: [parent domain, label]
    -            TEST_IDENTITY,                      // contestant_id
    -            null,                               // start_at_voter_info
    -            100,                                // limit
    -            true                                // order_ascending
    -        );
    -        console.log(`   Found ${result?.voters?.length || 0} voters`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_contested_resource_identity_votes - get votes by identity', async () => {
    -    try {
    -        const result = await wasmSdk.get_contested_resource_identity_votes(
    -            sdk,
    -            TEST_IDENTITY      // identity ID
    -        );
    -        console.log(`   Found ${result?.length || 0} votes by identity`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -await test('get_vote_polls_by_end_date - get vote polls in date range', async () => {
    -    try {
    -        // Function expects string timestamps based on Rust signature
    -        const endTime = Date.now();
    -        const startTime = endTime - 86400000; // 24 hours ago
    -        
    -        const result = await wasmSdk.get_vote_polls_by_end_date(
    -            sdk,
    -            startTime.toString(),
    -            endTime.toString(),
    -            100,    // limit
    -            true    // order_ascending
    -        );
    -        console.log(`   Found ${result?.votePolls?.length || 0} vote polls in range`);
    -    } catch (error) {
    -        if (error.message.includes('network') || error.message.includes('connection')) {
    -            console.log('   Expected network error (offline)');
    -        } else {
    -            throw error;
    -        }
    -    }
    -});
    -
    -// Clean up
    -sdk.free();
    -
    -console.log(`\n\nTest Results: ${passed} passed, ${failed} failed, ${passed + failed} total`);
    -
    -console.log('\nπŸ“ Notes:');
    -console.log('- Contested resources are typically domain names that multiple users want');
    -console.log('- Vote polls track masternode voting on contested resources');
    -console.log('- Network errors are expected when running offline');
    -
    -process.exit(failed > 0 ? 1 : 0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test_bip39_entropy.rs b/packages/wasm-sdk/test_bip39_entropy.rs
    deleted file mode 100644
    index 7e31ce36669..00000000000
    --- a/packages/wasm-sdk/test_bip39_entropy.rs
    +++ /dev/null
    @@ -1,36 +0,0 @@
    -use bip39::{Mnemonic, Language};
    -
    -fn main() {
    -    println!("Testing BIP39 entropy sizes...");
    -
    -    let test_sizes = vec![
    -        (16, "128-bit (12 words)"),
    -        (20, "160-bit (15 words)"),
    -        (24, "192-bit (18 words)"),
    -        (28, "224-bit (21 words)"),
    -        (32, "256-bit (24 words)"),
    -    ];
    -
    -    for (size, desc) in test_sizes {
    -        println!("\nTesting {}: ", desc);
    -
    -        // Create entropy of the specified size
    -        let entropy = vec![0u8; size];
    -
    -        // Try to create mnemonic from entropy
    -        match Mnemonic::from_entropy_in(Language::English, &entropy) {
    -            Ok(mnemonic) => {
    -                let words: Vec<&str> = mnemonic.word_iter().collect();
    -                println!("  βœ“ Success! Generated {} words", words.len());
    -            }
    -            Err(e) => {
    -                println!("  βœ— Failed: {}", e);
    -            }
    -        }
    -    }
    -
    -    // Also test if generate_in exists
    -    println!("\nChecking if generate_in method exists...");
    -    // This will fail to compile if generate_in doesn't exist
    -    // let _mnemonic = Mnemonic::generate_in(Language::English, 16);
    -}
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test_correct_path.mjs b/packages/wasm-sdk/test_correct_path.mjs
    deleted file mode 100755
    index d95b12eb73d..00000000000
    --- a/packages/wasm-sdk/test_correct_path.mjs
    +++ /dev/null
    @@ -1,59 +0,0 @@
    -#!/usr/bin/env node
    -
    -import { readFileSync } from 'fs';
    -import { fileURLToPath } from 'url';
    -import { dirname, join } from 'path';
    -import { webcrypto } from 'crypto';
    -
    -// Get directory paths
    -const __filename = fileURLToPath(import.meta.url);
    -const __dirname = dirname(__filename);
    -
    -// Set up globals for WASM
    -if (!global.crypto) {
    -    Object.defineProperty(global, 'crypto', {
    -        value: webcrypto,
    -        writable: true,
    -        configurable: true
    -    });
    -}
    -
    -// Import WASM SDK
    -import init, * as wasmSdk from './pkg/wasm_sdk.js';
    -
    -// Initialize WASM
    -console.log('Initializing WASM SDK...');
    -const wasmPath = join(__dirname, './pkg/wasm_sdk_bg.wasm');
    -const wasmBuffer = readFileSync(wasmPath);
    -await init(wasmBuffer);
    -
    -// Test both paths
    -const mnemonic = "birth kingdom trash renew flavor utility donkey gasp regular alert pave layer";
    -
    -// Path 1: Just the 256-bit indices (from test_dip14_vector.mjs)
    -const path1 = "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a/0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'";
    -
    -// Path 2: Full DIP15 path (from test_dip14_implementation.mjs) 
    -const path2 = "m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0";
    -
    -console.log('\n=== Testing Path 1 (Short) ===');
    -console.log('Path:', path1);
    -try {
    -    const result1 = wasmSdk.derive_key_from_seed_with_extended_path(mnemonic, null, path1, "testnet");
    -    console.log('Private key:', result1.private_key_hex);
    -} catch (error) {
    -    console.error('Error:', error.message);
    -}
    -
    -console.log('\n=== Testing Path 2 (Full DIP15) ===');
    -console.log('Path:', path2);
    -try {
    -    const result2 = wasmSdk.derive_key_from_seed_with_extended_path(mnemonic, null, path2, "testnet");
    -    console.log('Private key:', result2.private_key_hex);
    -    console.log('Expected:    fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60');
    -    console.log('Match:', result2.private_key_hex === 'fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60' ? 'βœ…' : '❌');
    -} catch (error) {
    -    console.error('Error:', error.message);
    -}
    -
    -process.exit(0);
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/test_custom_paths.html b/packages/wasm-sdk/test_custom_paths.html
    deleted file mode 100644
    index a160869074b..00000000000
    --- a/packages/wasm-sdk/test_custom_paths.html
    +++ /dev/null
    @@ -1,191 +0,0 @@
    -
    -
    -
    -    Test Custom Path Derivation
    -    
    -
    -
    -    

    Test Custom Path Derivation

    - -
    -

    1. Generate Test Seed

    - - -
    -
    - -
    -

    2. Test Custom Path Derivation

    -
    -
    -
    -
    - -
    -
    - -
    -

    3. Test Different Path Types

    - - - - - - -
    -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/test_derive_path.html b/packages/wasm-sdk/test_derive_path.html deleted file mode 100644 index 34a3eca0802..00000000000 --- a/packages/wasm-sdk/test_derive_path.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - Test Derive Key from Seed with Path - - - -

    Test derive_key_from_seed_with_path

    - -
    -

    Direct Function Test

    - - -
    -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/test_dip13.html b/packages/wasm-sdk/test_dip13.html deleted file mode 100644 index 3f2106d9a02..00000000000 --- a/packages/wasm-sdk/test_dip13.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - Test DIP13 Key Derivation - - - -

    Test DIP13 Key Derivation

    - -
    -

    Test Direct DIP13 Path Derivation

    - - -
    -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/test_dip14_implementation.mjs b/packages/wasm-sdk/test_dip14_implementation.mjs deleted file mode 100755 index 2119bc2a100..00000000000 --- a/packages/wasm-sdk/test_dip14_implementation.mjs +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env node - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from './pkg/wasm_sdk.js'; - -async function runTest() { - console.log('Testing DIP14 256-bit Derivation Implementation...\n'); - - // Initialize WASM - const wasmPath = join(__dirname, 'pkg/wasm_sdk_bg.wasm'); - const wasmBuffer = readFileSync(wasmPath); - await init(wasmBuffer); - - // Test Vector 2 from DIP14 - console.log('=== DIP14 Test Vector 2 ===\n'); - - const testMnemonic = "birth kingdom trash renew flavor utility donkey gasp regular alert pave layer"; - const testPath = "m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0"; - - // Expected results from test vector - const expected = { - privateKey: "0xfac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60", - xprv: "tprv8p9LqE2tA2b94gc3ciRNA525WVkFvzkcC9qjpKEcGaTqjb9u2pwTXj41KkZTj3c1a6fJUpyXRfcB4dimsYsLMjQjsTJwi5Ukx6tJ5BpmYpx", - xpub: "tpubDKZGWTkDtRpjWxsJ2qGVGNNq33aL8ji9Dz6ndDK46BQagP2kByTCFiQYu9fkBwCBrKNhCk7pL9ysjdtcaqAXEQNHDCZY8iXN6YAdq1qecKN" - }; - - try { - // Test using extended derivation - console.log('Testing with derive_key_from_seed_with_extended_path...'); - const result = await wasmSdk.derive_key_from_seed_with_extended_path( - testMnemonic, - null, - testPath, - 'testnet' - ); - - console.log('\nResult:'); - console.log('Path:', result.path); - console.log('Private Key:', result.private_key_hex); - console.log('Extended Private Key:', result.xprv); - console.log('Extended Public Key:', result.xpub); - console.log('Address:', result.address); - - console.log('\nExpected:'); - console.log('Private Key:', expected.privateKey); - console.log('Extended Private Key:', expected.xprv); - console.log('Extended Public Key:', expected.xpub); - - console.log('\nComparison:'); - console.log('Private Key Match:', result.private_key_hex === expected.privateKey.slice(2)); - console.log('xprv Match:', result.xprv === expected.xprv); - console.log('xpub Match:', result.xpub === expected.xpub); - - // Also test the dashpay contact key function - console.log('\n\nTesting with derive_dashpay_contact_key...'); - const contactResult = await wasmSdk.derive_dashpay_contact_key( - testMnemonic, - null, - "0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a", - "0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5", - 0, - 0, - 'testnet' - ); - - console.log('\nContact Key Result:'); - console.log('Path:', contactResult.path); - console.log('Private Key:', contactResult.private_key_hex); - console.log('Extended Private Key:', contactResult.xprv); - console.log('Extended Public Key:', contactResult.xpub); - - } catch (error) { - console.error('Error:', error.message); - } - - process.exit(0); -} - -runTest().catch(console.error); \ No newline at end of file diff --git a/packages/wasm-sdk/test_dip14_vector.mjs b/packages/wasm-sdk/test_dip14_vector.mjs deleted file mode 100755 index 4d4fa33b458..00000000000 --- a/packages/wasm-sdk/test_dip14_vector.mjs +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env node -// Test DIP14 256-bit derivation with test vector 2 - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from './pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, './pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Test Vector 2 -const testVector = { - mnemonic: "birth kingdom trash renew flavor utility donkey gasp regular alert pave layer", - seedHex: "b16d3782e714da7c55a397d5f19104cfed7ffa8036ac514509bbb50807f8ac598eeb26f0797bd8cc221a6cbff2168d90a5e9ee025a5bd977977b9eccd97894bb", - masterHD: { - xprv: "tprv8ZgxMBicQKsPeTb4MhYiJKST5uCW8dQ2swMcH9rAv9JPdadYK9LKCcdKd8a2FopTjKH9rvw8rELFpJSKCEV6pzVLmNUVFGKvwN1Y8WqhSoZ", - xpub: "tpubD6NzVbkMyxFKPuttqg8FFuNrJK7TiVVwKygWzcdDmLbtKo3F1cvZxtukxAirHbQLDG4pCkc4FGxgpVw3zPaRMDDugf3e8NVFHiYMnpmn3Bg" - }, - path: "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a/0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'", - expected: { - privateKey: "0xfac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60", - xprv: "tprv8p9LqE2tA2b94gc3ciRNA525WVkFvzkcC9qjpKEcGaTqjb9u2pwTXj41KkZTj3c1a6fJUpyXRfcB4dimsYsLMjQjsTJwi5Ukx6tJ5BpmYpx", - xpub: "tpubDKZGWTkDtRpjWxsJ2qGVGNNq33aL8ji9Dz6ndDK46BQagP2kByTCFiQYu9fkBwCBrKNhCk7pL9ysjdtcaqAXEQNHDCZY8iXN6YAdq1qecKN" - } -}; - -console.log('\n=== DIP14 Test Vector 2 ===\n'); - -// First, verify the seed generation -const seedBytes = wasmSdk.mnemonic_to_seed(testVector.mnemonic, null); -const seed = Buffer.from(seedBytes).toString('hex'); -console.log('Generated seed:', seed); -console.log('Expected seed: ', testVector.seedHex); -console.log('Seed match:', seed === testVector.seedHex ? 'βœ…' : '❌'); - -// Try to derive using the extended path -console.log('\nDeriving key with path:', testVector.path); - -try { - const result = wasmSdk.derive_key_from_seed_with_extended_path( - testVector.mnemonic, - null, - testVector.path, - "testnet" - ); - - console.log('\nDerived values:'); - console.log('Private key:', result.private_key_hex); - console.log('Expected: ', testVector.expected.privateKey.slice(2)); // Remove 0x prefix - console.log('Match:', result.private_key_hex === testVector.expected.privateKey.slice(2) ? 'βœ…' : '❌'); - - console.log('\nExtended private key:', result.xprv); - console.log('Expected: ', testVector.expected.xprv); - console.log('Match:', result.xprv === testVector.expected.xprv ? 'βœ…' : '❌'); - - console.log('\nExtended public key:', result.xpub); - console.log('Expected: ', testVector.expected.xpub); - console.log('Match:', result.xpub === testVector.expected.xpub ? 'βœ…' : '❌'); - - // Analyze what's happening - console.log('\n=== Analysis ==='); - console.log('Path components:'); - console.log('1. Master key: m'); - console.log('2. First ID: 0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a (256 bits)'); - console.log('3. Second ID: 0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6\' (256 bits, hardened)'); - - // Try to understand what index is being used - const firstIdBytes = Buffer.from('775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a', 'hex'); - const firstFourBytes = firstIdBytes.slice(0, 4); - const index1 = firstFourBytes.readUInt32BE(0); - console.log('\nFirst ID first 4 bytes:', firstFourBytes.toString('hex')); - console.log('As u32 (BE):', index1); - console.log('As u32 (BE) & 0x7FFFFFFF:', index1 & 0x7FFFFFFF); - - const secondIdBytes = Buffer.from('f537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6', 'hex'); - const secondFourBytes = secondIdBytes.slice(0, 4); - const index2 = secondFourBytes.readUInt32BE(0); - console.log('\nSecond ID first 4 bytes:', secondFourBytes.toString('hex')); - console.log('As u32 (BE):', index2); - console.log('As u32 (BE) & 0x7FFFFFFF:', index2 & 0x7FFFFFFF); - -} catch (error) { - console.error('Error deriving key:', error.message); -} - -// Also try deriving the master key manually -console.log('\n=== Master Key Verification ==='); -try { - const masterResult = wasmSdk.derive_key_from_seed_with_path( - testVector.mnemonic, - null, - "m", - "testnet" - ); - - console.log('Master xprv:', masterResult.xprv); - console.log('Expected: ', testVector.masterHD.xprv); - console.log('Match:', masterResult.xprv === testVector.masterHD.xprv ? 'βœ…' : '❌'); - - console.log('\nMaster xpub:', masterResult.xpub); - console.log('Expected: ', testVector.masterHD.xpub); - console.log('Match:', masterResult.xpub === testVector.masterHD.xpub ? 'βœ…' : '❌'); -} catch (error) { - console.error('Error deriving master key:', error.message); -} - -process.exit(0); \ No newline at end of file diff --git a/packages/wasm-sdk/test_improvements.md b/packages/wasm-sdk/test_improvements.md deleted file mode 100644 index 9bf27986fd0..00000000000 --- a/packages/wasm-sdk/test_improvements.md +++ /dev/null @@ -1,91 +0,0 @@ -# Testing the Improved Where Clause Dropdowns - -## Summary of Improvements - -### 1. **Fixed Operator Dropdown Options** -- Removed invalid operators: `!=`, `contains`, `endsWith`, `elementMatch` -- Only shows valid Dash Platform operators: - - `==` (Equal) - for all types - - `>`, `>=`, `<`, `<=` - for numeric types only - - `startsWith` - for strings only - - `in` - for all types - -### 2. **Compound Index Validation** -- Visual indicators showing property positions in the index (green, blue, orange badges) -- Real-time validation that enforces compound index rules -- Clear error messages when trying to use range queries on non-first properties without equality on preceding properties - -### 3. **Support for Multiple Range Clauses** -- "Add Range" button appears when selecting a range operator -- Can add a second range condition on the same field -- Automatically combines into appropriate Between operator: - - `>` + `<` β†’ `BetweenExcludeBounds` - - `>=` + `<=` β†’ `Between` - - `>` + `<=` β†’ `BetweenExcludeLeft` - - `>=` + `<` β†’ `BetweenExcludeRight` - -### 4. **Query Preview** -- Live preview showing the exact query structure -- Shows how range clauses will be combined -- Updates in real-time as you modify inputs - -### 5. **Enhanced UI/UX** -- Field type indicators (string, integer, date, identifier) -- Compound index rules displayed prominently -- Validation errors prevent invalid queries from being executed -- Better visual hierarchy with grouped range clauses - -## Testing Steps - -1. **Start the web server**: - ```bash - cd /Users/quantum/src/platform/packages/wasm-sdk - python3 -m http.server 8888 - ``` - -2. **Open browser**: - Navigate to http://localhost:8888 - -3. **Test scenarios**: - - ### A. Test Valid Operators - - Select Document Queries β†’ Get Documents - - Enter a contract ID and document type - - Click "Load Contract" - - Select an index - - Verify only valid operators appear for each field type - - ### B. Test Compound Index Validation - - Select an index with multiple properties - - Try to add a range query on the second property without equality on the first - - Verify error message appears - - Add equality on first property, then range on second - - Verify error disappears - - ### C. Test Multiple Range Clauses - - Select a numeric field - - Choose `>` operator and enter a value - - Click "+ Add Range" button - - Add `<` operator with a higher value - - Verify the preview shows it will become a Between operator - - ### D. Test Query Preview - - Build various queries - - Verify the preview updates correctly - - Check that IN clauses show arrays properly - - Verify Between operators show correct bounds - -## Known Limitations - -1. Only one IN clause allowed per query (platform limitation) -2. Range queries must be on indexed fields -3. All properties before a range property must have equality constraints -4. Maximum of 2 range clauses per field (will be combined) - -## Future Enhancements - -1. Add autocomplete for field values based on data type -2. Add query history/favorites -3. Add query templates for common patterns -4. Add export/import of queries -5. Add query performance hints based on index usage \ No newline at end of file diff --git a/packages/wasm-sdk/test_languages.html b/packages/wasm-sdk/test_languages.html deleted file mode 100644 index b786830a37e..00000000000 --- a/packages/wasm-sdk/test_languages.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - Test Multi-Language BIP39 - - - -

    Test Multi-Language BIP39 Support

    -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/test_matching_ids.mjs b/packages/wasm-sdk/test_matching_ids.mjs deleted file mode 100755 index 658b6b617c4..00000000000 --- a/packages/wasm-sdk/test_matching_ids.mjs +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env node - -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { webcrypto } from 'crypto'; - -// Get directory paths -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Set up globals for WASM -if (!global.crypto) { - Object.defineProperty(global, 'crypto', { - value: webcrypto, - writable: true, - configurable: true - }); -} - -// Import WASM SDK -import init, * as wasmSdk from './pkg/wasm_sdk.js'; - -// Initialize WASM -console.log('Initializing WASM SDK...'); -const wasmPath = join(__dirname, './pkg/wasm_sdk_bg.wasm'); -const wasmBuffer = readFileSync(wasmPath); -await init(wasmBuffer); - -// Use the correct test mnemonic and IDs from wallet-lib -const mnemonic = "birth kingdom trash renew flavor utility donkey gasp regular alert pave layer"; - -// These are the exact IDs from wallet-lib test -const userUniqueId = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; -const contactUniqueId = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; - -// Build the exact same path as wallet-lib -const path = `m/9'/5'/15'/0'/${userUniqueId}'/${contactUniqueId}'/0`; - -console.log('\n=== Testing DIP15 Path with Correct IDs ==='); -console.log('Path:', path); -console.log('Expected private key: fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60'); - -try { - const result = wasmSdk.derive_key_from_seed_with_extended_path(mnemonic, null, path, "mainnet"); - console.log('\nResult private key: ', result.private_key_hex); - console.log('Match:', result.private_key_hex === 'fac40790776d171ee1db90899b5eb2df2f7d2aaf35ad56f07ffb8ed2c57f8e60' ? 'βœ…' : '❌'); - - // Also check xprv - console.log('\nResult xprv:', result.xprv); - console.log('Expected: xprvA7UQ3tiYkkm4TsNWx9ZrzRQ6CNL3hUibrbvcwtp9nbyMwzQp3Tbi1ygZQaPoigDhCf8XUjMmGK2NbnB2kLXPYg99Lp6e3iki318sdWcFN3q'); -} catch (error) { - console.error('Error:', error.message); -} - -process.exit(0); \ No newline at end of file diff --git a/packages/wasm-sdk/test_token_mint.md b/packages/wasm-sdk/test_token_mint.md deleted file mode 100644 index b8ec7376f6c..00000000000 --- a/packages/wasm-sdk/test_token_mint.md +++ /dev/null @@ -1,84 +0,0 @@ -# Token Mint Test Instructions - -## Prerequisites -1. The WASM SDK server should be running on http://localhost:8080 -2. You need a valid testnet identity ID and private key in WIF format - -## Test Steps - -1. Open http://localhost:8080 in your browser -2. Select "State Transitions" from the Operation Type dropdown -3. Select "Token Transitions" from the Category dropdown -4. Select "Token Mint" from the Type dropdown - -## Fill in the following test values: - -### Authentication -- **Identity ID**: Your testnet identity ID (base58 encoded) -- **Private Key (WIF)**: Your private key in WIF format - -### Token Mint Parameters -- **Data Contract ID**: The ID of a data contract that has tokens defined -- **Token Contract Position**: 0 (or the position of the token in the contract) -- **Amount to Mint**: 1000000 (or any amount) -- **Key ID**: 0 (or the ID of your authentication key) -- **Issue To Identity ID**: (Optional) Leave empty to mint to yourself -- **Public Note**: (Optional) "Test mint from WASM SDK" - -## Expected Result - -If successful, you should see a JSON response with one of these formats: - -### Standard Token Balance Result -```json -{ - "type": "VerifiedTokenBalance", - "recipientId": "base58-encoded-identity-id", - "newBalance": "1000000" -} -``` - -### Token with History Tracking -```json -{ - "type": "VerifiedTokenActionWithDocument", - "document": "Document tracking mint history" -} -``` - -### Group-managed Token Results -```json -{ - "type": "VerifiedTokenGroupActionWithDocument", - "groupPower": 100, - "document": true -} -``` - -or - -```json -{ - "type": "VerifiedTokenGroupActionWithTokenBalance", - "groupPower": 100, - "status": "Pending", - "balance": "1000000" -} -``` - -## Troubleshooting - -If you get an error: -1. Check that your identity ID and private key are valid -2. Ensure the data contract ID exists and has tokens defined -3. Verify the token position is correct (starts at 0) -4. Check that your identity has sufficient credits for the transaction -5. Ensure your key ID matches an authentication key in your identity - -## Token Burn Test - -You can also test token burning by: -1. Select "Token Burn" from the Type dropdown -2. Use the same contract ID and token position -3. Enter an amount to burn (must be less than or equal to your balance) -4. The result should show your new reduced balance \ No newline at end of file diff --git a/packages/wasm-sdk/test_wallet_fixes.html b/packages/wasm-sdk/test_wallet_fixes.html deleted file mode 100644 index 2b83be2f4b5..00000000000 --- a/packages/wasm-sdk/test_wallet_fixes.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - Test Wallet Fixes - - - -

    Test Wallet Fixes

    - -
    -

    1. Test Generate Mnemonic (Valid Checksum)

    - - -
    -
    - -
    -

    2. Test BIP44 Key Derivation from Seed

    -
    - -
    -
    - -
    -

    3. Test UI Button Text

    -

    Check main index.html:

    -
      -
    • Select "Key Derivation" from operation type
    • -
    • Button should show "View" instead of "Execute Query"
    • -
    -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/.eslintrc.yml b/packages/wasm-sdk/tests/.eslintrc.yml new file mode 100644 index 00000000000..509d508a284 --- /dev/null +++ b/packages/wasm-sdk/tests/.eslintrc.yml @@ -0,0 +1,25 @@ +extends: + - airbnb-base + - plugin:jsdoc/recommended +env: + es2020: true + node: true + mocha: true +rules: + eol-last: + - error + - always + import/extensions: off + class-methods-use-this: off + import/no-extraneous-dependencies: off + curly: + - error + - all + no-restricted-syntax: + - error + - selector: "LabeledStatement" + message: Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand. + - selector: "WithStatement" + message: "`with` is disallowed in strict mode because it makes code impossible to predict and optimize." +globals: + expect: true diff --git a/packages/wasm-sdk/tests/bootstrap.cjs b/packages/wasm-sdk/tests/bootstrap.cjs new file mode 100644 index 00000000000..6cfe32eb5be --- /dev/null +++ b/packages/wasm-sdk/tests/bootstrap.cjs @@ -0,0 +1,10 @@ +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const chaiAsPromised = require('chai-as-promised'); + +chai.use(chaiAsPromised); +chai.use(dirtyChai); + +const { expect } = chai; +const g = (typeof globalThis !== 'undefined') ? globalThis : global; +g.expect = expect; diff --git a/packages/wasm-sdk/tests/check_identity_keys.html b/packages/wasm-sdk/tests/check_identity_keys.html deleted file mode 100644 index 5bec8de4a2b..00000000000 --- a/packages/wasm-sdk/tests/check_identity_keys.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - Check Identity Keys - - - -

    Check Identity Keys

    - -
    - - -
    - -
    - - -
    - - - - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/check_posts.html b/packages/wasm-sdk/tests/check_posts.html deleted file mode 100644 index ecd9d965e0b..00000000000 --- a/packages/wasm-sdk/tests/check_posts.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - Check Posts - - - -

    Check Your Posts

    - -

    Identity ID: 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk

    -

    Contract ID: 9nzpvjVSStUrhkEs3eNHw2JYpcNoLh1MjmqW45QiyjSa

    - - - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/derive_public_key.html b/packages/wasm-sdk/tests/derive_public_key.html deleted file mode 100644 index 288789c24ea..00000000000 --- a/packages/wasm-sdk/tests/derive_public_key.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - - Derive Public Key - - - - -

    Derive Public Key from Private Key

    - -
    - - -
    - - - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/functional/dpns.spec.mjs b/packages/wasm-sdk/tests/functional/dpns.spec.mjs new file mode 100644 index 00000000000..81b5b52c8ee --- /dev/null +++ b/packages/wasm-sdk/tests/functional/dpns.spec.mjs @@ -0,0 +1,57 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Document queries', function describeDocumentQueries() { + this.timeout(60000); + + const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('lists DPNS documents (no filters)', async () => { + const docs = await client.getDocuments(DPNS_CONTRACT, 'domain', null, null, 5, null, null); + expect(docs).to.be.an('array'); + }); + + it('queries with where clause', async () => { + const where = JSON.stringify([['normalizedParentDomainName', '==', 'dash']]); + const docs = await client.getDocuments(DPNS_CONTRACT, 'domain', where, null, 5, null, null); + expect(docs).to.be.an('array'); + }); + + it('queries with orderBy', async () => { + const orderBy = JSON.stringify([['normalizedParentDomainName', 'asc']]); + const docs = await client.getDocuments(DPNS_CONTRACT, 'domain', null, orderBy, 5, null, null); + expect(docs).to.be.an('array'); + }); + + it('complex where + orderBy', async () => { + const where = JSON.stringify([['normalizedLabel', 'startsWith', 'test'], ['normalizedParentDomainName', '==', 'dash']]); + const orderBy = JSON.stringify([['normalizedParentDomainName', 'asc'], ['normalizedLabel', 'asc']]); + const docs = await client.getDocuments(DPNS_CONTRACT, 'domain', where, orderBy, 5, null, null); + expect(docs).to.be.an('array'); + }); + + it('getDocument by id (should handle invalid id gracefully)', async () => { + await expect( + client.getDocument(DPNS_CONTRACT, 'domain', 'invalidDocumentId'), + ).to.be.rejected(); + }); + + it('fetches usernames for a known identity and verifies fields', async () => { + const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; + const list = await client.getDpnsUsernames(TEST_IDENTITY, 10); + expect(list).to.be.an('array'); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/epochs-blocks.spec.mjs b/packages/wasm-sdk/tests/functional/epochs-blocks.spec.mjs new file mode 100644 index 00000000000..752c241003e --- /dev/null +++ b/packages/wasm-sdk/tests/functional/epochs-blocks.spec.mjs @@ -0,0 +1,34 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Epochs and evonode blocks', function describeEpochs() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('gets epochs info and finalized epochs', async () => { + const current = await client.getCurrentEpoch().catch(() => 1000); + const start = Math.max(0, (current || 1000) - 5); + const infos = await client.getEpochsInfo(start, 5, true); + expect(infos).to.be.an('array'); + const finalized = await client.getFinalizedEpochInfos(start, 5); + expect(finalized).to.be.an('array'); + }); + + it('queries evonode proposed blocks by id/range', async () => { + const EVONODE_ID = '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113'; + await client.getEvonodesProposedEpochBlocksByIds(8635, [EVONODE_ID]); + await client.getEvonodesProposedEpochBlocksByRange(EVONODE_ID, 50); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/groups.spec.mjs b/packages/wasm-sdk/tests/functional/groups.spec.mjs new file mode 100644 index 00000000000..878274c28cc --- /dev/null +++ b/packages/wasm-sdk/tests/functional/groups.spec.mjs @@ -0,0 +1,32 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Group queries', function describeGroupQueries() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('fetches identity groups and group members', async () => { + const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; + const IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; + // These calls may fail in offline runs; permit network errors + await client.getIdentityGroups(IDENTITY, 0, null, null, 10); + await client.getGroupMembers(DPNS_CONTRACT, 0, null, null, 10); + }); + + it('fetches groups data contracts', async () => { + const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; + await client.getGroupsDataContracts([DPNS_CONTRACT]); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/identities.spec.mjs b/packages/wasm-sdk/tests/functional/identities.spec.mjs new file mode 100644 index 00000000000..a87db2ff3b7 --- /dev/null +++ b/packages/wasm-sdk/tests/functional/identities.spec.mjs @@ -0,0 +1,65 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Identity queries', function describeBlock() { + this.timeout(90000); + + const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; + const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('fetches identity and basic fields', async () => { + const r = await client.getIdentity(TEST_IDENTITY); + expect(r).to.be.ok(); + }); + + it('gets identity balance and nonce', async () => { + const bal = await client.getIdentityBalance(TEST_IDENTITY); + expect(bal).to.be.an('object'); + expect(String(bal.balance)).to.match(/^\d+$/); + + const nonce = await client.getIdentityNonce(TEST_IDENTITY); + expect(nonce).to.be.an('object'); + expect(String(nonce.nonce)).to.match(/^\d+$/); + }); + + it('gets contract nonce and keys', async () => { + await client.getIdentityContractNonce(TEST_IDENTITY, DPNS_CONTRACT); + const keys = await client.getIdentityKeys(TEST_IDENTITY, 'all'); + expect(keys).to.be.an('array'); + }); + + it('batch identity balances and balance+revision', async () => { + const balances = await client.getIdentitiesBalances([TEST_IDENTITY]); + expect(balances).to.be.an('array'); + const br = await client.getIdentityBalanceAndRevision(TEST_IDENTITY); + expect(br).to.be.ok(); + }); + + it('contract keys for identity', async () => { + const r = await client.getIdentitiesContractKeys([TEST_IDENTITY], DPNS_CONTRACT); + expect(r).to.be.an('array'); + }); + + it('token balances/infos for identity and batches', async () => { + const TOKEN_CONTRACT = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'; + const tokenId = sdk.WasmSdk.calculateTokenIdFromContract(TOKEN_CONTRACT, 1); + + await client.getIdentityTokenBalances(TEST_IDENTITY, [tokenId]); + await client.getIdentitiesTokenBalances([TEST_IDENTITY], tokenId); + await client.getIdentityTokenInfos(TEST_IDENTITY, [tokenId]); + await client.getIdentitiesTokenInfos([TEST_IDENTITY], 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/protocol.spec.mjs b/packages/wasm-sdk/tests/functional/protocol.spec.mjs new file mode 100644 index 00000000000..402580cd76d --- /dev/null +++ b/packages/wasm-sdk/tests/functional/protocol.spec.mjs @@ -0,0 +1,30 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Protocol versions', function describeProtocolVersions() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('fetches protocol upgrade state', async () => { + const state = await client.getProtocolVersionUpgradeState(); + expect(state).to.be.ok(); + }); + + it('lists protocol upgrade vote statuses', async () => { + const START_PROTX = '143dcd6a6b7684fde01e88a10e5d65de9a29244c5ecd586d14a342657025f113'; + const res = await client.getProtocolVersionUpgradeVoteStatus(START_PROTX, 50); + expect(res).to.be.an('array'); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/status.spec.mjs b/packages/wasm-sdk/tests/functional/status.spec.mjs new file mode 100644 index 00000000000..3e64450766f --- /dev/null +++ b/packages/wasm-sdk/tests/functional/status.spec.mjs @@ -0,0 +1,24 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Status endpoint', function describeBlock() { + this.timeout(30000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('getStatus', async () => { + const status = await client.getStatus(); + expect(status).to.be.ok(); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/system.spec.mjs b/packages/wasm-sdk/tests/functional/system.spec.mjs new file mode 100644 index 00000000000..38f8a186d43 --- /dev/null +++ b/packages/wasm-sdk/tests/functional/system.spec.mjs @@ -0,0 +1,30 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('System info', function describeSystemInfo() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('getCurrentQuorumsInfo', async () => { + const r = await client.getCurrentQuorumsInfo(); + expect(r).to.be.ok(); + }); + + it('getTotalCreditsInPlatform', async () => { + const r = await client.getTotalCreditsInPlatform(); + expect(r).to.be.an('object'); + expect(String(r.totalCreditsInPlatform)).to.match(/^\d+$/); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/token-pricing.spec.mjs b/packages/wasm-sdk/tests/functional/token-pricing.spec.mjs new file mode 100644 index 00000000000..e3ec33728a7 --- /dev/null +++ b/packages/wasm-sdk/tests/functional/token-pricing.spec.mjs @@ -0,0 +1,33 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Token pricing', function describeTokenPricing() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it('calculates token id and fetches price by contract', async () => { + const CONTRACT_ID = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'; + const tokenId = sdk.WasmSdk.calculateTokenIdFromContract(CONTRACT_ID, 0); + expect(tokenId).to.be.a('string'); + try { + const info = await client.getTokenPriceByContract(CONTRACT_ID, 0); + expect(info).to.be.ok(); + } catch (e) { + if (!(e.message.includes('No pricing schedule'))) { + throw e; + } + } + }); +}); diff --git a/packages/wasm-sdk/tests/functional/tokens.spec.mjs b/packages/wasm-sdk/tests/functional/tokens.spec.mjs new file mode 100644 index 00000000000..78c2d19e375 --- /dev/null +++ b/packages/wasm-sdk/tests/functional/tokens.spec.mjs @@ -0,0 +1,49 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Token queries', function describeTokenQueries() { + this.timeout(60000); + + const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; + const TOKEN_CONTRACT = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'; + const TOKEN_CONTRACT_2 = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'; + const TOKEN_CONTRACT_3 = 'EETVvWgohFDKtbB3ejEzBcDRMNYkc9TtgXY6y8hzP3Ta'; + + let client; + let builder; + + before(async () => { + await init(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + // TODO: fix this test + + it.skip('getTokenTotalSupply using derived token id', async () => { + const tokenId = sdk.WasmSdk.calculateTokenIdFromContract(TOKEN_CONTRACT, 0); + const total = await client.getTokenTotalSupply(tokenId); + // Returns an object with totalSupply as string + expect(total).to.be.an('object'); + expect(String(total.totalSupply)).to.match(/^\d+$/); + }); + + it('getTokenStatuses for multiple tokens', async () => { + await client.getTokenStatuses([TOKEN_CONTRACT]); + }); + + it('getTokenDirectPurchasePrices', async () => { + await client.getTokenDirectPurchasePrices([TOKEN_CONTRACT_2]); + }); + + it('getTokenContractInfo', async () => { + await client.getTokenContractInfo(TOKEN_CONTRACT_3); + }); + + it('getTokenPerpetualDistributionLastClaim', async () => { + await client.getTokenPerpetualDistributionLastClaim(TEST_IDENTITY, TOKEN_CONTRACT_3); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/transitions.spec.mjs b/packages/wasm-sdk/tests/functional/transitions.spec.mjs new file mode 100644 index 00000000000..7f4116c44f4 --- /dev/null +++ b/packages/wasm-sdk/tests/functional/transitions.spec.mjs @@ -0,0 +1,28 @@ +import init, * as sdk from '../../dist/sdk.js'; + +const TOKEN_CONTRACT = 'H7FRpZJqZK933r9CzZMsCuf1BM34NT5P2wSJyjDkprqy'; +const TEST_IDENTITY = '5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk'; + +// TODO: Implement tests for all state transitions factories + +describe('Basic state transitions', function describeBasicStateTransitions() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { client.free(); } + }); + + it.skip('tokenTransfer rejects invalid parameters', async () => { + await client.tokenTransfer(TOKEN_CONTRACT, 0, '1000', TEST_IDENTITY, TEST_IDENTITY, 'Kx...', null); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/utilities.spec.mjs b/packages/wasm-sdk/tests/functional/utilities.spec.mjs new file mode 100644 index 00000000000..a2725f00aea --- /dev/null +++ b/packages/wasm-sdk/tests/functional/utilities.spec.mjs @@ -0,0 +1,21 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Utilities', function describeUtilities() { + before(async () => { await init(); }); + this.timeout(60000); + + it('prefetches trusted quorums (mainnet/testnet) or tolerates network errors', async () => { + await sdk.WasmSdk.prefetchTrustedQuorumsMainnet(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + }); + + it('testSerialization method (if present) returns object', async () => { + const builder = sdk.WasmSdkBuilder.testnet(); + const client = await builder.build(); + if (typeof client.testSerialization === 'function') { + const res = client.testSerialization('simple'); + expect(res).to.be.an('object'); + } + client.free(); + }); +}); diff --git a/packages/wasm-sdk/tests/functional/voting.spec.mjs b/packages/wasm-sdk/tests/functional/voting.spec.mjs new file mode 100644 index 00000000000..2ea9175eb92 --- /dev/null +++ b/packages/wasm-sdk/tests/functional/voting.spec.mjs @@ -0,0 +1,49 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Contested resources & voting', function describeContestedResources() { + this.timeout(60000); + + let client; + let builder; + + before(async () => { + await init(); + await sdk.WasmSdk.prefetchTrustedQuorumsTestnet(); + builder = sdk.WasmSdkBuilder.testnetTrusted(); + client = await builder.build(); + }); + + after(() => { + if (client) { + client.free(); + } + }); + + it('lists contested resources and vote state', async () => { + const DPNS_CONTRACT = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; + const PARENT = 'dash'; + const LABEL = 'therealslimshaddy5'; + + await client.getContestedResources( + 'domain', + DPNS_CONTRACT, + 'parentNameAndLabel', + null, + 50, + null, + true, + ); + + await client.getContestedResourceVoteState( + DPNS_CONTRACT, + 'domain', + 'parentNameAndLabel', + [PARENT, LABEL], + 'documents', + null, + null, + 50, + true, + ); + }); +}); diff --git a/packages/wasm-sdk/tests/karma/karma.conf.cjs b/packages/wasm-sdk/tests/karma/karma.conf.cjs new file mode 100644 index 00000000000..98bceeb9526 --- /dev/null +++ b/packages/wasm-sdk/tests/karma/karma.conf.cjs @@ -0,0 +1,16 @@ +const options = require('./options.cjs'); + +module.exports = (config) => { + config.set({ + ...options, + files: [ + // Load bootstrap first to initialize chai and globals + '../bootstrap.cjs', + '../unit/**/*.spec.mjs', + ], + preprocessors: { + '../bootstrap.cjs': ['webpack'], + '../unit/**/*.spec.mjs': ['webpack'], + }, + }); +}; diff --git a/packages/wasm-sdk/tests/karma/karma.functional.conf.cjs b/packages/wasm-sdk/tests/karma/karma.functional.conf.cjs new file mode 100644 index 00000000000..39ac2e46ab8 --- /dev/null +++ b/packages/wasm-sdk/tests/karma/karma.functional.conf.cjs @@ -0,0 +1,16 @@ +const options = require('./options.cjs'); + +module.exports = (config) => { + config.set({ + ...options, + files: [ + // Load bootstrap first to initialize chai and globals + '../bootstrap.cjs', + '../functional/**/*.spec.mjs', + ], + preprocessors: { + '../bootstrap.cjs': ['webpack'], + '../functional/**/*.spec.mjs': ['webpack'], + }, + }); +}; diff --git a/packages/wasm-sdk/tests/karma/options.cjs b/packages/wasm-sdk/tests/karma/options.cjs new file mode 100644 index 00000000000..bcc05f28d0b --- /dev/null +++ b/packages/wasm-sdk/tests/karma/options.cjs @@ -0,0 +1,67 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const webpack = require('webpack'); +const karmaMocha = require('karma-mocha'); +const karmaMochaReporter = require('karma-mocha-reporter'); +const karmaChai = require('karma-chai'); +const karmaChromeLauncher = require('karma-chrome-launcher'); +const karmaFirefoxLauncher = require('karma-firefox-launcher'); +const karmaWebpack = require('karma-webpack'); + +module.exports = { + frameworks: ['mocha', 'chai', 'webpack'], + webpack: { + mode: 'development', + devtool: 'eval', + // No special wasm handling needed (WASM is inlined in dist/sdk.js) + plugins: [ + new webpack.ProvidePlugin({ + Buffer: [require.resolve('buffer/'), 'Buffer'], + process: require.resolve('process/browser'), + }), + ], + resolve: { + extensions: ['.mjs', '.js'], + fallback: { + fs: false, + path: require.resolve('path-browserify'), + url: require.resolve('url/'), + util: require.resolve('util/'), + buffer: require.resolve('buffer/'), + events: require.resolve('events/'), + assert: require.resolve('assert/'), + string_decoder: require.resolve('string_decoder/'), + process: require.resolve('process/browser'), + }, + }, + }, + reporters: ['mocha'], + port: 9876, + colors: true, + autoWatch: false, + browsers: ['ChromeHeadlessInsecure'], + singleRun: false, + concurrency: Infinity, + browserNoActivityTimeout: 7 * 60 * 1000, + browserDisconnectTimeout: 3 * 2000, + pingTimeout: 3 * 5000, + plugins: [ + karmaMocha, + karmaMochaReporter, + karmaChai, + karmaChromeLauncher, + karmaFirefoxLauncher, + karmaWebpack, + ], + webpackMiddleware: { + stats: 'errors-warnings', + }, + customLaunchers: { + ChromeHeadlessInsecure: { + base: 'ChromeHeadless', + flags: [ + '--allow-insecure-localhost', + ], + displayName: 'Chrome w/o security', + }, + }, +}; diff --git a/packages/wasm-sdk/tests/test-cache.html b/packages/wasm-sdk/tests/test-cache.html deleted file mode 100644 index 8bf04b686e8..00000000000 --- a/packages/wasm-sdk/tests/test-cache.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - Cache Test - - - -

    WASM SDK Cache Test

    - -
    Checking service worker...
    - -
    - - - - -
    - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test-imports.html b/packages/wasm-sdk/tests/test-imports.html deleted file mode 100644 index 242f56bace7..00000000000 --- a/packages/wasm-sdk/tests/test-imports.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Test Import Map - - - - -

    Import Map Configuration

    -

    This file shows how to use import maps for centralized module path management.

    -

    To use in test files, include this script tag in the head:

    -
    <script type="importmap">
    -{
    -    "imports": {
    -        "wasm-sdk": "../pkg/wasm_sdk.js"
    -    }
    -}
    -</script>
    -

    Then import using: import init, { WasmSdkBuilder } from 'wasm-sdk';

    - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test_group_queries.html b/packages/wasm-sdk/tests/test_group_queries.html deleted file mode 100644 index 3ffe4a94de1..00000000000 --- a/packages/wasm-sdk/tests/test_group_queries.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - Test Group Queries - - -

    Group Query Tests

    -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test_groups.html b/packages/wasm-sdk/tests/test_groups.html deleted file mode 100644 index a3694fccfb3..00000000000 --- a/packages/wasm-sdk/tests/test_groups.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - Test Group Queries - - -

    Test Group Queries

    - -
    
    -
    -    
    -
    -
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/tests/test_nonce.html b/packages/wasm-sdk/tests/test_nonce.html
    deleted file mode 100644
    index fdcd377b868..00000000000
    --- a/packages/wasm-sdk/tests/test_nonce.html
    +++ /dev/null
    @@ -1,55 +0,0 @@
    -
    -
    -
    -    
    -    Test Identity Nonce
    -
    -
    -    

    Identity Nonce Test

    -
    
    -    
    -    
    -
    -
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/tests/test_post_create.html b/packages/wasm-sdk/tests/test_post_create.html
    deleted file mode 100644
    index fc60a19d611..00000000000
    --- a/packages/wasm-sdk/tests/test_post_create.html
    +++ /dev/null
    @@ -1,165 +0,0 @@
    -
    -
    -
    -    
    -    Test Post Creation
    -    
    -
    -
    -    

    Test Post Creation

    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - - - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test_proof.html b/packages/wasm-sdk/tests/test_proof.html deleted file mode 100644 index 68aeff775a3..00000000000 --- a/packages/wasm-sdk/tests/test_proof.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - Test Proof Toggle - - - -

    WASM SDK Proof Functions Test

    - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test_proof.js b/packages/wasm-sdk/tests/test_proof.js deleted file mode 100644 index 244f85b68d4..00000000000 --- a/packages/wasm-sdk/tests/test_proof.js +++ /dev/null @@ -1,57 +0,0 @@ -import { readFileSync, existsSync } from 'fs'; -import { WASI } from 'wasi'; -import { argv, env } from 'process'; -import { join } from 'path'; - -// Create WASI instance -const wasi = new WASI({ - args: argv, - env, - preopens: {} -}); - -// Read and instantiate WASM module -let wasmBuffer, wasmModule, instance; - -// Check if WASM file exists before attempting to read it -const wasmPath = './pkg/wasm_sdk_bg.wasm'; -if (!existsSync(wasmPath)) { - console.error('Error: WASM file not found at:', wasmPath); - console.error('Please build the WASM module first by running: ./build.sh'); - process.exit(1); -} - -try { - wasmBuffer = readFileSync(wasmPath); - console.log('WASM file read successfully'); -} catch (error) { - console.error('Failed to read WASM file:', error.message); - console.error('Make sure you have built the WASM module with: ./build.sh'); - process.exit(1); -} - -try { - wasmModule = new WebAssembly.Module(wasmBuffer); - console.log('WASM module created successfully'); -} catch (error) { - console.error('Failed to create WebAssembly module:', error.message); - console.error('The WASM file may be corrupted. Try rebuilding with: ./build.sh'); - process.exit(1); -} - -try { - instance = new WebAssembly.Instance(wasmModule, { - wasi_snapshot_preview1: wasi.wasiImport - }); - console.log('WASM instance created successfully'); -} catch (error) { - console.error('Failed to instantiate WebAssembly module:', error.message); - console.error('There may be import/export mismatches or runtime issues'); - process.exit(1); -} - -// Initialize WASI -wasi.initialize(instance); - -console.log('WASM module loaded successfully!'); -console.log('Available exports:', Object.keys(instance.exports)); \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test_proof_result.html b/packages/wasm-sdk/tests/test_proof_result.html deleted file mode 100644 index f0695156793..00000000000 --- a/packages/wasm-sdk/tests/test_proof_result.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - Test Proof Result - - -

    Proof Result Test

    -
    
    -    
    -    
    -
    -
    \ No newline at end of file
    diff --git a/packages/wasm-sdk/tests/test_proof_split.html b/packages/wasm-sdk/tests/test_proof_split.html
    deleted file mode 100644
    index f4884214a48..00000000000
    --- a/packages/wasm-sdk/tests/test_proof_split.html
    +++ /dev/null
    @@ -1,144 +0,0 @@
    -
    -
    -
    -    Test Proof Split View
    -    
    -
    -
    -    

    Test Proof Split View

    - -
    -

    Mock Proof Response Display

    -

    This shows how a proof response should be displayed with the split view:

    - -
    -
    -
    Data
    -
    -
    {
    -  "id": "4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF",
    -  "balance": "1000000000",
    -  "publicKeys": [
    -    {
    -      "id": 0,
    -      "purpose": 0,
    -      "securityLevel": 0,
    -      "keyType": 0,
    -      "data": "AkVZTLXFDp3H8QVQV6p+XJpTXEJxDHSpP16hbWFqUt7g"
    -    }
    -  ],
    -  "revision": 0
    -}
    -
    -
    -
    -
    Proof & Metadata
    -
    -
    {
    -  "metadata": {
    -    "height": 12456,
    -    "coreChainLockedHeight": 987654,
    -    "epoch": 1234,
    -    "timeMs": 1738594800000,
    -    "protocolVersion": 1,
    -    "chainId": "dash-testnet-44"
    -  },
    -  "proof": {
    -    "grovedbProof": "0x0123456789abcdef...",
    -    "quorumHash": "0xfedcba9876543210...",
    -    "signature": "0xabcdef0123456789...",
    -    "round": 1,
    -    "blockIdHash": "0x1234567890abcdef...",
    -    "quorumType": 6
    -  }
    -}
    -
    -
    -
    -
    - -
    -

    Expected Behavior

    -
      -
    • When proof toggle is OFF: Shows normal single view with just the data
    • -
    • When proof toggle is ON and response has proof: Shows split view with data on top, proof/metadata on bottom
    • -
    • The split view divides the available space equally between data and proof sections
    • -
    • Each section has its own scroll if content overflows
    • -
    • Proof bytes should be displayed in hex format
    • -
    -
    - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/test_serialization.html b/packages/wasm-sdk/tests/test_serialization.html deleted file mode 100644 index e54599e9d5c..00000000000 --- a/packages/wasm-sdk/tests/test_serialization.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - Test Serialization - - - -

    Test Serialization

    - - - - - -
    - - - - \ No newline at end of file diff --git a/packages/wasm-sdk/tests/unit/address-validation.spec.mjs b/packages/wasm-sdk/tests/unit/address-validation.spec.mjs new file mode 100644 index 00000000000..d21118d01f4 --- /dev/null +++ b/packages/wasm-sdk/tests/unit/address-validation.spec.mjs @@ -0,0 +1,22 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Address validation', () => { + before(async () => { + await init(); + }); + + it('validates known malformed prefixes correctly', () => { + const mainnetAddress = 'XdRhagDMpNbHZSvgMXqkcCCWmrDYYty5Nh'; + const testnetAddress = 'yXdRhagDMpNbHZSvgMXqkcCCWmrDYYty5Nh'; + expect(sdk.WasmSdk.validateAddress(mainnetAddress, 'mainnet')).to.be.a('boolean'); + expect(sdk.WasmSdk.validateAddress(testnetAddress, 'testnet')).to.be.a('boolean'); + }); + + it('validates generated addresses for each network', () => { + const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + const kM = sdk.WasmSdk.deriveKeyFromSeedWithPath(mnemonic, undefined, "m/44'/5'/0'/0/0", 'mainnet'); + const kT = sdk.WasmSdk.deriveKeyFromSeedWithPath(mnemonic, undefined, "m/44'/1'/0'/0/0", 'testnet'); + expect(sdk.WasmSdk.validateAddress(kM.address, 'mainnet')).to.equal(true); + expect(sdk.WasmSdk.validateAddress(kT.address, 'testnet')).to.equal(true); + }); +}); diff --git a/packages/wasm-sdk/tests/unit/api-availability.spec.mjs b/packages/wasm-sdk/tests/unit/api-availability.spec.mjs new file mode 100644 index 00000000000..12ef5a09f9a --- /dev/null +++ b/packages/wasm-sdk/tests/unit/api-availability.spec.mjs @@ -0,0 +1,37 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('API availability (exports and methods)', () => { + before(async () => { + await init(); + }); + it('query methods are available on WasmSdk instance', () => { + const instanceFns = [ + // Identity + 'getIdentity', 'getIdentityUnproved', 'getIdentityKeys', 'getIdentityNonce', 'getIdentityContractNonce', 'getIdentityBalance', 'getIdentitiesBalances', 'getIdentityBalanceAndRevision', 'getIdentityByPublicKeyHash', 'getIdentityByNonUniquePublicKeyHash', 'getIdentitiesContractKeys', 'getIdentityTokenBalances', 'getIdentityTokenInfos', 'getIdentitiesTokenInfos', + // Documents / contracts + 'getDocuments', 'getDocument', 'getDataContract', 'getDataContractHistory', 'getDataContracts', + // Tokens + 'getTokenStatuses', 'getTokenDirectPurchasePrices', 'getTokenContractInfo', 'getTokenPerpetualDistributionLastClaim', 'getTokenTotalSupply', + // Epochs / System / Protocol + 'getEpochsInfo', 'getFinalizedEpochInfos', 'getCurrentEpoch', 'getEvonodesProposedEpochBlocksByIds', 'getEvonodesProposedEpochBlocksByRange', 'getProtocolVersionUpgradeState', 'getProtocolVersionUpgradeVoteStatus', 'getStatus', 'getCurrentQuorumsInfo', 'getTotalCreditsInPlatform', 'getPrefundedSpecializedBalance', 'getPathElements', + // Voting / Groups + 'getContestedResources', 'getContestedResourceVoteState', 'getContestedResourceVotersForIdentity', 'getContestedResourceIdentityVotes', 'getVotePollsByEndDate', 'getGroupInfo', 'getGroupInfos', 'getGroupMembers', 'getIdentityGroups', 'getGroupActions', 'getGroupActionSigners', 'getGroupsDataContracts', + // DPNS queries + 'dpnsRegisterName', 'dpnsIsNameAvailable', 'dpnsResolveName', 'getDpnsUsernameByName', 'getDpnsUsernames', 'getDpnsUsername', + // Utils + 'waitForStateTransitionResult', + ]; + for (const fn of instanceFns) { + expect(typeof sdk.WasmSdk.prototype[fn]).to.be.oneOf(['function', 'undefined']); + } + }); + + it('standalone verification helpers are exported', () => { + const moduleFns = [ + 'verifyIdentityResponse', 'verifyDataContract', 'verifyDocuments', + ]; + for (const fn of moduleFns) { + expect(typeof sdk[fn]).to.be.oneOf(['function', 'undefined']); + } + }); +}); diff --git a/packages/wasm-sdk/tests/unit/builder.spec.mjs b/packages/wasm-sdk/tests/unit/builder.spec.mjs new file mode 100644 index 00000000000..53a2ad12bd6 --- /dev/null +++ b/packages/wasm-sdk/tests/unit/builder.spec.mjs @@ -0,0 +1,35 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('WasmSdkBuilder', () => { + before(async () => { + await init(); + }); + + it('WasmSdkBuilder static methods exist', () => { + expect(sdk.WasmSdkBuilder).to.be.a('function'); + expect(sdk.WasmSdkBuilder.getLatestVersionNumber).to.be.a('function'); + expect(sdk.WasmSdkBuilder.mainnet).to.be.a('function'); + expect(sdk.WasmSdkBuilder.testnet).to.be.a('function'); + expect(sdk.WasmSdkBuilder.mainnetTrusted).to.be.a('function'); + expect(sdk.WasmSdkBuilder.testnetTrusted).to.be.a('function'); + }); + + it('builds testnet builder and sets version', async () => { + let builder = sdk.WasmSdkBuilder.testnet(); + expect(builder).to.be.ok(); + // note: builder methods consume and return a new builder + builder = builder.withVersion(1); + const built = await builder.build(); + expect(built).to.be.ok(); + built.free(); + }); + + it('applies custom settings (timeouts, retries, ban flag)', async () => { + // withSettings(connect_timeout_ms, timeout_ms, retries, ban_failed_address) + let builder = sdk.WasmSdkBuilder.testnet(); + builder = builder.withSettings(5000, 10000, 3, true); + const built = await builder.build(); + expect(built).to.be.ok(); + built.free(); + }); +}); diff --git a/packages/wasm-sdk/tests/unit/derivation.spec.mjs b/packages/wasm-sdk/tests/unit/derivation.spec.mjs new file mode 100644 index 00000000000..dc41e985d17 --- /dev/null +++ b/packages/wasm-sdk/tests/unit/derivation.spec.mjs @@ -0,0 +1,152 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Key derivation', () => { + before(async () => { + await init(); + }); + + describe('Path helpers (BIP44/DIP9/DIP13)', () => { + it('BIP44 mainnet/testnet', () => { + const m = sdk.WasmSdk.derivationPathBip44Mainnet(0, 0, 0); + expect(m.purpose).to.equal(44); + expect(m.coin_type).to.equal(5); + expect(m.account).to.equal(0); + expect(m.change).to.equal(0); + expect(m.index).to.equal(0); + const expectedMain = `m/${m.purpose}'/${m.coin_type}'/${m.account}'/${m.change}/${m.index}`; + expect(expectedMain).to.equal("m/44'/5'/0'/0/0"); + + const t = sdk.WasmSdk.derivationPathBip44Testnet(0, 0, 0); + expect(t.coin_type).to.equal(1); + const expectedTest = `m/${t.purpose}'/${t.coin_type}'/${t.account}'/${t.change}/${t.index}`; + expect(expectedTest).to.equal("m/44'/1'/0'/0/0"); + }); + + it('DIP9 mainnet/testnet', () => { + const m = sdk.WasmSdk.derivationPathDip9Mainnet(5, 0, 0); + expect(m.purpose).to.equal(9); + expect(m.coin_type).to.equal(5); + expect(m.account).to.equal(5); + const expectedMain = `m/${m.purpose}'/${m.coin_type}'/${m.account}'/${m.change}/${m.index}`; + expect(expectedMain).to.equal("m/9'/5'/5'/0/0"); + + const t = sdk.WasmSdk.derivationPathDip9Testnet(5, 0, 0); + expect(t.coin_type).to.equal(1); + const expectedTest = `m/${t.purpose}'/${t.coin_type}'/${t.account}'/${t.change}/${t.index}`; + expect(expectedTest).to.equal("m/9'/1'/5'/0/0"); + }); + + it('DIP13 mainnet/testnet', () => { + const m = sdk.WasmSdk.derivationPathDip13Mainnet(0); + expect(m.path).to.equal("m/9'/5'/0'"); + expect(m.purpose).to.equal(9); + expect(m.description).to.equal('DIP13 HD identity key path'); + + const t = sdk.WasmSdk.derivationPathDip13Testnet(0); + expect(t.path).to.equal("m/9'/1'/0'"); + }); + }); + + describe('Derive by path', () => { + const seed = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + + it('BIP44 mainnet key', () => { + const path = "m/44'/5'/0'/0/0"; + const r = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, undefined, path, 'mainnet'); + expect(r).to.exist(); + expect(r.path).to.equal(path); + expect(r.address.startsWith('X')).to.equal(true); + expect(r.network).to.equal('mainnet'); + }); + + it('DIP13 authentication key', () => { + const path = "m/9'/5'/5'/0'/0'/0'/0'"; + const r = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, undefined, path, 'mainnet'); + expect(r).to.exist(); + expect(r.path).to.equal(path); + expect(r.private_key_wif).to.be.a('string'); + expect(r.address).to.be.a('string'); + }); + + it('with passphrase produces different address', () => { + const path = "m/44'/5'/0'/0/0"; + const withPass = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, 'test passphrase', path, 'mainnet'); + const withoutPass = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, undefined, path, 'mainnet'); + expect(withPass.address).to.not.equal(withoutPass.address); + }); + + it('testnet address prefix', () => { + const path = "m/44'/1'/0'/0/0"; + const r = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, undefined, path, 'testnet'); + expect(r.network).to.equal('testnet'); + expect(r.address.startsWith('y')).to.equal(true); + }); + + it('DIP9 hardened vs non-hardened differ', () => { + const hardened = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, null, "m/9'/5'/5'/0/0", 'mainnet'); + const nonHardened = sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, null, 'm/9/5/5/0/0', 'mainnet'); + expect(hardened.address).to.not.equal(nonHardened.address); + }); + }); + + describe('DIP14 extended vectors', () => { + const mnemonic = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; + + it('Vector 1: mixed hardened/non-hardened', () => { + const path = "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b/0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'/0x4c4592ca670c983fc43397dfd21a6f427fac9b4ac53cb4dcdc6522ec51e81e79/0"; + const r = sdk.WasmSdk.deriveKeyFromSeedWithExtendedPath(mnemonic, null, path, 'testnet'); + expect(r.xprv).to.be.a('string'); + expect(r.xpub).to.be.a('string'); + }); + + it('Vector 2: multiple hardened with final non-hardened', () => { + const path = "m/9'/5'/15'/0'/0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0"; + const r = sdk.WasmSdk.deriveKeyFromSeedWithExtendedPath(mnemonic, null, path, 'testnet'); + expect(r.xprv).to.be.a('string'); + expect(r.xpub).to.be.a('string'); + }); + }); + + describe('DIP15 DashPay contact keys', () => { + const mnemonic = 'birth kingdom trash renew flavor utility donkey gasp regular alert pave layer'; + const sender = '0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'; + const receiver = '0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'; + + it('deterministic contact key for testnet', () => { + const r1 = sdk.WasmSdk.deriveDashpayContactKey(mnemonic, null, sender, receiver, 0, 0, 'testnet'); + const r2 = sdk.WasmSdk.deriveDashpayContactKey(mnemonic, null, sender, receiver, 0, 0, 'testnet'); + + expect(r1).to.be.ok(); + expect(r1).to.have.property('path'); + expect(r1).to.have.property('xprv'); + expect(r1).to.have.property('xpub'); + expect(r1).to.have.property('private_key_hex'); + expect(r1.private_key_hex).to.have.length(64); + + expect(r2.private_key_hex).to.equal(r1.private_key_hex); + expect(r2.xprv).to.equal(r1.xprv); + expect(r2.xpub).to.equal(r1.xpub); + + expect(r1.path).to.include("15'"); + expect(r1.path).to.include(sender); + expect(r1.path).to.include(receiver); + + expect(r1.xprv.startsWith('tprv')).to.equal(true); + expect(r1.xpub.startsWith('tpub')).to.equal(true); + }); + + it('changes when sender/receiver are swapped', () => { + const a = sdk.WasmSdk.deriveDashpayContactKey(mnemonic, null, sender, receiver, 0, 0, 'testnet'); + const b = sdk.WasmSdk.deriveDashpayContactKey(mnemonic, null, receiver, sender, 0, 0, 'testnet'); + expect(a.private_key_hex).to.not.equal(b.private_key_hex); + }); + + it('differs between networks (testnet vs mainnet)', () => { + const t = sdk.WasmSdk.deriveDashpayContactKey(mnemonic, null, sender, receiver, 0, 0, 'testnet'); + const m = sdk.WasmSdk.deriveDashpayContactKey(mnemonic, null, sender, receiver, 0, 0, 'mainnet'); + expect(m.xprv.startsWith('xprv')).to.equal(true); + expect(m.xpub.startsWith('xpub')).to.equal(true); + expect(m.private_key_hex).to.not.equal(t.private_key_hex); + }); + }); +}); diff --git a/packages/wasm-sdk/tests/unit/dpns-utils.spec.mjs b/packages/wasm-sdk/tests/unit/dpns-utils.spec.mjs new file mode 100644 index 00000000000..0fea785f52b --- /dev/null +++ b/packages/wasm-sdk/tests/unit/dpns-utils.spec.mjs @@ -0,0 +1,33 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('DPNS utils (homograph + validation)', () => { + before(async () => { + await init(); + }); + it('convert to homograph safe retains allowed chars', () => { + expect(sdk.WasmSdk.dpnsConvertToHomographSafe('test')).to.equal('test'); + expect(sdk.WasmSdk.dpnsConvertToHomographSafe('test123')).to.equal('test123'); + expect(sdk.WasmSdk.dpnsConvertToHomographSafe('test-name')).to.equal('test-name'); + expect(sdk.WasmSdk.dpnsConvertToHomographSafe('TestName')).to.equal('testname'); + }); + + it('homograph conversions for o,i,l to 0/1/1', () => { + const input = 'IlIooLi'; + expect(sdk.WasmSdk.dpnsConvertToHomographSafe(input)).to.equal('1110011'); + }); + + it('preserves non-homograph unicode (lowercased)', () => { + expect(sdk.WasmSdk.dpnsConvertToHomographSafe('tΠ΅st')).to.equal('tΠ΅st'); + }); + + it('username validation basic rules', () => { + expect(sdk.WasmSdk.dpnsIsValidUsername('alice')).to.equal(true); + expect(sdk.WasmSdk.dpnsIsValidUsername('alice123')).to.equal(true); + expect(sdk.WasmSdk.dpnsIsValidUsername('alice-bob')).to.equal(true); + expect(sdk.WasmSdk.dpnsIsValidUsername('ab')).to.equal(false); + expect(sdk.WasmSdk.dpnsIsValidUsername('a'.repeat(64))).to.equal(false); + expect(sdk.WasmSdk.dpnsIsValidUsername('-alice')).to.equal(false); + expect(sdk.WasmSdk.dpnsIsValidUsername('alice-')).to.equal(false); + expect(sdk.WasmSdk.dpnsIsValidUsername('alice--bob')).to.equal(false); + }); +}); diff --git a/packages/wasm-sdk/tests/unit/errors.spec.mjs b/packages/wasm-sdk/tests/unit/errors.spec.mjs new file mode 100644 index 00000000000..ee7d321fd88 --- /dev/null +++ b/packages/wasm-sdk/tests/unit/errors.spec.mjs @@ -0,0 +1,47 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('WasmSdkError shape (unit)', () => { + before(async () => { + await init(); + }); + + it('invalid network on generateKeyPair exposes InvalidArgument', () => { + try { + sdk.WasmSdk.generateKeyPair('devnet'); + expect.fail('expected to throw'); + } catch (e) { + // wasm-bindgen returns our WasmSdkError as an object, not necessarily instanceof Error + expect(e).to.be.instanceOf(sdk.WasmSdkError); + expect(e.name).to.equal('InvalidArgument'); + expect(e.message).to.match(/Invalid network/i); + expect(e.retriable).to.equal(false); + expect(e.code).to.equal(-1); + } + }); + + it('invalid hex on keyPairFromHex exposes InvalidArgument', () => { + try { + sdk.WasmSdk.keyPairFromHex('zzzz', 'mainnet'); + expect.fail('expected to throw'); + } catch (e) { + expect(e).to.be.instanceOf(sdk.WasmSdkError); + expect(e.name).to.equal('InvalidArgument'); + expect(e.retriable).to.equal(false); + // either length or content validation may trigger first + expect(e.message).to.match(/Invalid hex|must be exactly 64/i); + } + }); + + it('invalid derivation path network exposes InvalidArgument', () => { + const seed = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + const path = "m/44'/5'/0'/0/0"; + try { + sdk.WasmSdk.deriveKeyFromSeedWithPath(seed, undefined, path, 'bogus'); + expect.fail('expected to throw'); + } catch (e) { + expect(e).to.be.instanceOf(sdk.WasmSdkError); + expect(e.name).to.equal('InvalidArgument'); + expect(e.message).to.match(/Invalid network/i); + } + }); +}); diff --git a/packages/wasm-sdk/tests/unit/extended-keys.spec.mjs b/packages/wasm-sdk/tests/unit/extended-keys.spec.mjs new file mode 100644 index 00000000000..185c36ff9ae --- /dev/null +++ b/packages/wasm-sdk/tests/unit/extended-keys.spec.mjs @@ -0,0 +1,83 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Extended keys', () => { + const TEST_MNEMONIC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + + before(async () => { + await init(); + }); + + describe('deriveChildPublicKey - basic functionality', () => { + it('derives non-hardened child xpubs that differ by index', () => { + const master = sdk.WasmSdk.deriveKeyFromSeedWithExtendedPath( + TEST_MNEMONIC, + null, + "m/44'/5'/0'", + 'mainnet', + ); + const parentXpub = master.xpub; + + const child0 = sdk.WasmSdk.deriveChildPublicKey(parentXpub, 0, false); + expect(child0).to.be.a('string'); + expect(child0).to.not.equal(parentXpub); + + const child1 = sdk.WasmSdk.deriveChildPublicKey(parentXpub, 1, false); + expect(child1).to.be.a('string'); + expect(child1).to.not.equal(child0); + }); + }); + + describe('xprvToXpub - basic functionality', () => { + it('converts xprv to the expected xpub', () => { + const master = sdk.WasmSdk.deriveKeyFromSeedWithExtendedPath( + TEST_MNEMONIC, + null, + "m/44'/5'/0'", + 'mainnet', + ); + + const derivedXpub = sdk.WasmSdk.xprvToXpub(master.xprv); + expect(derivedXpub).to.be.a('string'); + expect(derivedXpub).to.equal(master.xpub); + }); + }); + + describe('deriveChildPublicKey - error handling', () => { + it('throws when hardened=true', () => { + const master = sdk.WasmSdk.deriveKeyFromSeedWithExtendedPath( + TEST_MNEMONIC, + null, + "m/44'/5'/0'", + 'mainnet', + ); + const parentXpub = master.xpub; + expect(() => sdk.WasmSdk.deriveChildPublicKey(parentXpub, 0, true)) + .to.throw('Cannot derive hardened child from extended public key'); + }); + + it('throws when index is in hardened range', () => { + const master = sdk.WasmSdk.deriveKeyFromSeedWithExtendedPath( + TEST_MNEMONIC, + null, + "m/44'/5'/0'", + 'mainnet', + ); + const parentXpub = master.xpub; + // 0x80000000 == 2^31 + expect(() => sdk.WasmSdk.deriveChildPublicKey(parentXpub, 0x80000000, false)) + .to.throw('Index is in hardened range'); + }); + + it('throws for invalid xpub input', () => { + expect(() => sdk.WasmSdk.deriveChildPublicKey('invalid_xpub', 0, false)) + .to.throw('Invalid extended public key'); + }); + }); + + describe('xprvToXpub - error handling', () => { + it('throws for invalid xprv input', () => { + expect(() => sdk.WasmSdk.xprvToXpub('invalid_xprv')) + .to.throw('Invalid extended private key'); + }); + }); +}); diff --git a/packages/wasm-sdk/tests/unit/key-generation.spec.mjs b/packages/wasm-sdk/tests/unit/key-generation.spec.mjs new file mode 100644 index 00000000000..9e8ebea54cf --- /dev/null +++ b/packages/wasm-sdk/tests/unit/key-generation.spec.mjs @@ -0,0 +1,62 @@ +import init, * as sdk from '../../dist/sdk.js'; + +describe('Keys and mnemonics', () => { + const TEST_MNEMONIC = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + + before(async () => { + await init(); + }); + + describe('mnemonic', () => { + it('generates 12 and 24 words and validates', () => { + const m12 = sdk.WasmSdk.generateMnemonic(12); + expect(m12.split(' ').length).to.equal(12); + expect(sdk.WasmSdk.validateMnemonic(m12)).to.equal(true); + + const m24 = sdk.WasmSdk.generateMnemonic(24); + expect(m24.split(' ').length).to.equal(24); + expect(sdk.WasmSdk.validateMnemonic(m24)).to.equal(true); + }); + + it('supports language wordlists', () => { + const langs = ['en', 'es', 'fr', 'it', 'ja', 'ko', 'pt', 'cs']; + for (const lang of langs) { + const m = sdk.WasmSdk.generateMnemonic(12, lang); + expect(sdk.WasmSdk.validateMnemonic(m, lang)).to.equal(true); + } + }); + + it('converts mnemonic to seed (with/without passphrase)', () => { + const seed = sdk.WasmSdk.mnemonicToSeed(TEST_MNEMONIC); + expect(seed && seed.length).to.equal(64); + + const seed2 = sdk.WasmSdk.mnemonicToSeed(TEST_MNEMONIC, 'passphrase'); + expect(seed2 && seed2.length).to.equal(64); + expect(Buffer.from(seed2).toString('hex')).to.not.equal(Buffer.from(seed).toString('hex')); + }); + }); + + describe('key pairs and addresses', () => { + it('generates key pairs for mainnet/testnet', () => { + const kpM = sdk.WasmSdk.generateKeyPair('mainnet'); + expect(kpM.address.startsWith('X')).to.equal(true); + const kpT = sdk.WasmSdk.generateKeyPair('testnet'); + expect(kpT.address.startsWith('y')).to.equal(true); + }); + + it('derives address from pubkey equals generated address', () => { + const kp = sdk.WasmSdk.generateKeyPair('mainnet'); + const addr = sdk.WasmSdk.pubkeyToAddress(kp.public_key, 'mainnet'); + expect(addr).to.equal(kp.address); + }); + + it('signs messages deterministically for same inputs', () => { + const kp = sdk.WasmSdk.generateKeyPair('mainnet'); + const msg = 'Hello, Dash!'; + const s1 = sdk.WasmSdk.signMessage(msg, kp.private_key_wif); + const s2 = sdk.WasmSdk.signMessage(msg, kp.private_key_wif); + expect(s1).to.be.a('string'); + expect(s1).to.equal(s2); + }); + }); +}); diff --git a/packages/wasm-sdk/tests/verify_proof_display.html b/packages/wasm-sdk/tests/verify_proof_display.html deleted file mode 100644 index e8bc258c629..00000000000 --- a/packages/wasm-sdk/tests/verify_proof_display.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - Verify Proof Display - - - -
    -

    βœ… Proof Display Implementation Complete

    - -

    What's been implemented:

    -
      -
    • Added proof versions for get_identity_nonce and get_identity_contract_nonce
    • -
    • Updated HTML to use proof functions when toggle is enabled
    • -
    • Split view display for proof data (data on top, proof/metadata on bottom)
    • -
    - -

    How to test:

    -
      -
    1. Open http://localhost:8888
    2. -
    3. Enable the Proof toggle at the top of the page
    4. -
    5. Select Identity Queries β†’ Get Identity Nonce
    6. -
    7. Enter an identity ID (e.g., 5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk)
    8. -
    9. Click Execute Query
    10. -
    - -

    Expected result with Proof toggle ON:

    -
      -
    • Right panel splits horizontally into two sections
    • -
    • Top section shows: { "nonce": "28492849625774" }
    • -
    • Bottom section shows metadata and proof data: -
        -
      • Metadata: height, coreChainLockedHeight, epoch, timeMs, protocolVersion, chainId
      • -
      • Proof: grovedbProof (base64), quorumHash (hex), signature (base64), round, blockIdHash (hex), quorumType
      • -
      -
    • -
    - -

    Other queries with proof support:

    -
      -
    • Identity: getIdentity, getIdentityNonce, getIdentityContractNonce
    • -
    • Data Contract: getDataContract
    • -
    • Documents: getDocument, getDocuments
    • -
    • DPNS: getDpnsUsernameByName
    • -
    • Epochs: getEpochsInfo, getCurrentEpoch
    • -
    • Tokens: getIdentitiesTokenBalances, getTokenStatuses, getTokenTotalSupply
    • -
    • System: getTotalCreditsInPlatform, getPrefundedSpecializedBalance, getPathElements
    • -
    • Protocol: getProtocolVersionUpgradeState
    • -
    • Groups: getGroupInfo, getGroupInfos
    • -
    -
    - - \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 30ea3ecfc3f..3d71f3eddd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1693,6 +1693,47 @@ __metadata: languageName: unknown linkType: soft +"@dashevo/evo-sdk@workspace:packages/js-evo-sdk": + version: 0.0.0-use.local + resolution: "@dashevo/evo-sdk@workspace:packages/js-evo-sdk" + dependencies: + "@dashevo/wasm-sdk": "workspace:*" + "@typescript-eslint/eslint-plugin": "npm:^5.55.0" + "@typescript-eslint/parser": "npm:^5.55.0" + assert: "npm:^2.0.0" + buffer: "npm:^6.0.3" + chai: "npm:^4.3.10" + chai-as-promised: "npm:^7.1.1" + dirty-chai: "npm:^2.0.1" + eslint: "npm:^8.53.0" + eslint-config-airbnb-base: "npm:^15.0.0" + eslint-config-airbnb-typescript: "npm:^17.0.0" + eslint-plugin-import: "npm:^2.29.0" + eslint-plugin-jsdoc: "npm:^46.9.0" + events: "npm:^3.3.0" + karma: "npm:^6.4.3" + karma-chai: "npm:^0.1.0" + karma-chrome-launcher: "npm:^3.1.0" + karma-firefox-launcher: "npm:^2.1.2" + karma-mocha: "npm:^2.0.1" + karma-mocha-reporter: "npm:^2.2.5" + karma-webpack: "npm:^5.0.0" + mocha: "npm:^11.1.0" + path-browserify: "npm:^1.0.1" + process: "npm:^0.11.10" + sinon: "npm:^17.0.1" + sinon-chai: "npm:^3.7.0" + string_decoder: "npm:^1.3.0" + terser-webpack-plugin: "npm:^5.3.11" + ts-loader: "npm:^9.5.0" + typescript: "npm:^3.9.5" + url: "npm:^0.11.3" + util: "npm:^0.12.4" + webpack: "npm:^5.94.0" + webpack-cli: "npm:^4.9.1" + languageName: unknown + linkType: soft + "@dashevo/feature-flags-contract@workspace:*, @dashevo/feature-flags-contract@workspace:packages/feature-flags-contract": version: 0.0.0-use.local resolution: "@dashevo/feature-flags-contract@workspace:packages/feature-flags-contract" @@ -2018,6 +2059,38 @@ __metadata: languageName: unknown linkType: soft +"@dashevo/wasm-sdk@workspace:*, @dashevo/wasm-sdk@workspace:packages/wasm-sdk": + version: 0.0.0-use.local + resolution: "@dashevo/wasm-sdk@workspace:packages/wasm-sdk" + dependencies: + assert: "npm:^2.0.0" + buffer: "npm:^6.0.3" + chai: "npm:^4.3.10" + chai-as-promised: "npm:^7.1.1" + dirty-chai: "npm:^2.0.1" + eslint: "npm:^8.53.0" + eslint-config-airbnb-base: "npm:^15.0.0" + eslint-plugin-import: "npm:^2.29.0" + eslint-plugin-jsdoc: "npm:^46.9.0" + events: "npm:^3.3.0" + karma: "npm:^6.4.3" + karma-chai: "npm:^0.1.0" + karma-chrome-launcher: "npm:^3.1.0" + karma-firefox-launcher: "npm:^2.1.2" + karma-mocha: "npm:^2.0.1" + karma-mocha-reporter: "npm:^2.2.5" + karma-webpack: "npm:^5.0.0" + mocha: "npm:^11.1.0" + path-browserify: "npm:^1.0.1" + process: "npm:^0.11.10" + string_decoder: "npm:^1.3.0" + url: "npm:^0.11.3" + util: "npm:^0.12.4" + webpack: "npm:^5.94.0" + webpack-cli: "npm:^4.9.1" + languageName: unknown + linkType: soft + "@dashevo/withdrawals-contract@workspace:*, @dashevo/withdrawals-contract@workspace:packages/withdrawals-contract": version: 0.0.0-use.local resolution: "@dashevo/withdrawals-contract@workspace:packages/withdrawals-contract"