diff --git a/.github/scripts/rosetta/setup.sh b/.github/scripts/rosetta/setup.sh index 308006ca289..b85acf125eb 100755 --- a/.github/scripts/rosetta/setup.sh +++ b/.github/scripts/rosetta/setup.sh @@ -7,12 +7,11 @@ echo "Install binaries" cargo install --locked --bin iota --path crates/iota cargo install --locked --bin iota-rosetta --path crates/iota-rosetta -echo "create dedicated config dir for IOTA genesis" -CONFIG_DIR="~/.iota/rosetta_config" -mkdir -p $CONFIG_DIR - echo "run IOTA genesis" -iota genesis -f --working-dir $CONFIG_DIR +CONFIG_DIR=~/.iota/iota_config +if ! [ -d "$CONFIG_DIR" ]; then + iota genesis +fi echo "generate rosetta configuration" iota-rosetta generate-rosetta-cli-config --online-url http://127.0.0.1:9002 --offline-url http://127.0.0.1:9003 diff --git a/.github/workflows/_move_tests.yml b/.github/workflows/_move_tests.yml index a98c7ad8f3f..54c8b002f08 100644 --- a/.github/workflows/_move_tests.yml +++ b/.github/workflows/_move_tests.yml @@ -26,4 +26,24 @@ jobs: tool: nextest - name: Run move tests run: | - cargo nextest run -p iota-framework-tests -- unit_tests:: + cargo nextest run -E + 'package(iota-framework-tests) + or (package(iota-core) and test(quorum_driver::)) + or package(iota-benchmark) + or test(move_tests::)' + + move-simtest: + timeout-minutes: 10 + runs-on: [self-hosted] + steps: + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: taiki-e/install-action@375e0c7f08a66b8c2ba7e7eef31a6f91043a81b0 # v2.44.38 + with: + tool: nextest + - name: Run move tests + run: | + scripts/simtest/cargo-simtest simtest --profile ci -E + 'package(iota-framework-tests) + or (package(iota-core) and test(quorum_driver::)) + or package(iota-benchmark) + or test(move_tests::)' diff --git a/.github/workflows/_rust_tests.yml b/.github/workflows/_rust_tests.yml index 75a96614c06..9aeccdffe2d 100644 --- a/.github/workflows/_rust_tests.yml +++ b/.github/workflows/_rust_tests.yml @@ -6,6 +6,9 @@ on: changedCrates: type: string required: false + runSimtest: + type: boolean + default: true concurrency: group: rust-tests-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -118,6 +121,7 @@ jobs: simtest: name: Simtest rust + if: inputs.runSimtest timeout-minutes: 45 runs-on: [self-hosted] env: diff --git a/.github/workflows/_vercel_deploy.yml b/.github/workflows/_vercel_deploy.yml index d85f6664fff..ffdb8fda37f 100644 --- a/.github/workflows/_vercel_deploy.yml +++ b/.github/workflows/_vercel_deploy.yml @@ -68,14 +68,16 @@ jobs: secrets: inherit with: isProd: false + isStaging: false - wallet-dashboard-prod: - name: Vercel Wallet Dashboard Production + wallet-dashboard-staging: + name: Vercel Wallet Dashboard Staging if: github.ref_name == 'develop' uses: ./.github/workflows/apps_wallet_dashboard_deploy.yml secrets: inherit with: - isProd: true + isProd: false + isStaging: true apps-backend-preview: name: Vercel apps-backend Preview diff --git a/.github/workflows/apps_wallet_dashboard_deploy.yml b/.github/workflows/apps_wallet_dashboard_deploy.yml index d3ae3f5f284..f533776b7bb 100644 --- a/.github/workflows/apps_wallet_dashboard_deploy.yml +++ b/.github/workflows/apps_wallet_dashboard_deploy.yml @@ -3,14 +3,25 @@ name: Deploy for Wallet Dashboard env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.WALLET_DASHBOARD_VERCEL_PROJECT_ID }} + WALLET_DASHBOARD_VERCEL_PROJECT_STAGING_URL: ${{ secrets.WALLET_DASHBOARD_VERCEL_PROJECT_STAGING_URL }} on: workflow_dispatch: + inputs: + isProd: + type: boolean + required: true + isStaging: + type: boolean + required: true workflow_call: inputs: isProd: type: boolean required: true + isStaging: + type: boolean + required: true jobs: deploy: @@ -64,8 +75,11 @@ jobs: id: deploy_url if: ${{ inputs.isProd == false }} run: echo "DEPLOY_URL=$(cat vercel_output.txt | awk 'END{print}')" >> $GITHUB_OUTPUT + - name: Alias Staging deploy + if: ${{ inputs.isStaging }} + run: vercel alias ${{ steps.deploy_url.outputs.DEPLOY_URL }} $WALLET_DASHBOARD_VERCEL_PROJECT_STAGING_URL --token=${{ secrets.VERCEL_TOKEN }} --scope=${{ secrets.VERCEL_SCOPE }} - name: Comment on pull request - if: ${{ inputs.isProd == false }} + if: ${{ inputs.isProd == false && inputs.isStaging == false }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 27268340ad9..3e02cfc233f 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -4,6 +4,17 @@ on: schedule: - cron: "0 0 * * *" # every day at midnight workflow_dispatch: + inputs: + iota_ref: + description: "Branch / commit to simtest" + type: string + required: true + default: develop + test_num: + description: "MSIM_TEST_NUM (test iterations)" + type: string + required: false + default: "30" env: BINARY_LIST_FILE: "./binary-build-list.json" @@ -27,6 +38,8 @@ env: RUSTUP_MAX_RETRIES: 10 # Don't emit giant backtraces in the CI logs. RUST_BACKTRACE: short + IOTA_REF: "${{ github.event.inputs.iota_ref || 'develop' }}" + TEST_NUM: "${{ github.event.inputs.test_num || '30' }}" jobs: release: @@ -61,6 +74,9 @@ jobs: tests: uses: ./.github/workflows/_rust_tests.yml + with: + # simtest job below runs a superset of these tests + runSimtest: false external-tests: uses: ./.github/workflows/_external_rust_tests.yml @@ -84,3 +100,17 @@ jobs: split-cluster: uses: ./.github/workflows/split_cluster.yml + + simtest: + timeout-minutes: 240 + runs-on: [self-hosted] + + steps: + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + with: + ref: ${{ env.IOTA_REF }} + - uses: taiki-e/install-action@375e0c7f08a66b8c2ba7e7eef31a6f91043a81b0 # v2.44.38 + with: + tool: nextest + - name: Run simtest + run: scripts/simtest/simtest-run.sh diff --git a/.github/workflows/simulator_nightly.yml b/.github/workflows/simulator_nightly.yml deleted file mode 100644 index 234a9013788..00000000000 --- a/.github/workflows/simulator_nightly.yml +++ /dev/null @@ -1,178 +0,0 @@ -name: Simulator Tests - -on: - schedule: - - cron: "0 9 * * *" # UTC timing is every day at 1am PST - workflow_dispatch: - inputs: - iota_ref: - description: "Branch / commit to test" - type: string - required: true - default: main - test_num: - description: "MSIM_TEST_NUM (test iterations)" - type: string - required: false - default: "30" - -concurrency: - group: ${{ github.workflow }} - -env: - IOTA_REF: "${{ github.event.inputs.iota_ref || 'main' }}" - TEST_NUM: "${{ github.event.inputs.test_num || '30' }}" - -jobs: - simtest: - timeout-minutes: 240 - permissions: - # The "id-token: write" permission is required or Machine ID will not be - # able to authenticate with the cluster. - id-token: write - contents: read - runs-on: self-hosted - - steps: - - name: Install Teleport - uses: teleport-actions/setup@176c25dfcd19cd31a252f275d579822b243e7b9c # v1.0.6 - with: - version: 11.3.1 - - name: Authorize against Teleport - id: auth - uses: teleport-actions/auth@685adaf480dc79262a99220eb158a92136d5abd9 # v2.0.3 - with: - # Specify the publically accessible address of your Teleport proxy. - proxy: proxy.iota-int.com:443 - # Specify the name of the join token for your bot. - token: iota-simtest-token - # Specify the length of time that the generated credentials should be - # valid for. This is optional and defaults to "1h" - certificate-ttl: 2h - - # Cargo clean and git restore on any left over files from git checkout, and deletes all remote tracking branches - - name: Environment clean - run: | - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 5 ssh ubuntu@simtest-01 "source ~/.bashrc && source ~/.cargo/env && rm -rf ~/iota" - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 5 ssh ubuntu@simtest-01 "source ~/.bashrc && source ~/.cargo/env && cd ~/ && git clone git@github.com:iotaledger/iota.git" - - # Deleting files in tmpfs that usually fill up pretty quickly after each run. Cargo clean to free up space as well. - - name: Tmpfs and cargo clean - run: | - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 5 ssh ubuntu@simtest-01 "sudo rm -rf /tmp/*" - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 5 ssh ubuntu@simtest-01 "source ~/.bashrc && source ~/.cargo/env && cd ~/iota && cargo clean" - - # Checkout out the latest iota repo - - name: Checkout iota repo - run: | - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 10 ssh ubuntu@simtest-01 "source ~/.bashrc && source ~/.cargo/env && cd ~/iota && git fetch origin && git checkout ${{ env.IOTA_REF }}" - - # Setting up cargo and simtest - - name: Install simtest - run: | - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 10 ssh ubuntu@simtest-01 "source ~/.bashrc && source ~/.cargo/env && cd ~/iota && ./scripts/simtest/install.sh" - - # Run simulator tests - - name: Run simtest - run: | - tsh -i ${{ steps.auth.outputs.identity-file }} --ttl 120 ssh ubuntu@simtest-01 "source ~/.bashrc && source ~/.cargo/env && cd ~/iota && RUSTUP_MAX_RETRIES=10 CARGO_TERM_COLOR=always CARGO_INCREMENTAL=0 CARGO_NET_RETRY=10 RUST_BACKTRACE=short RUST_LOG=off NUM_CPUS=24 TEST_NUM=${{ env.TEST_NUM }} ./scripts/simtest/simtest-run.sh" - - notify: - name: Notify - needs: [simtest] - runs-on: self-hosted - if: github.event_name == 'schedule' && failure() - - steps: - - uses: technote-space/workflow-conclusion-action@45ce8e0eb155657ab8ccf346ade734257fd196a5 # v3.0.3 - - - name: Checkout iota repo develop branch - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - - - name: Get iota commit - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - export iota_sha=$(git rev-parse HEAD) - echo "iota_sha=${iota_sha}" >> $GITHUB_ENV - - - name: Get link to logs - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh_job_link=$(gh api -X GET 'repos/iotaledger/iota/actions/runs/${{ github.run_id }}/jobs' --jq '.jobs.[0].html_url') - echo "gh_job_link=${gh_job_link}" >> $GITHUB_ENV - - - name: Get current oncall - run: | - export current_oncall=$(curl -s --request GET \ - --url 'https://api.pagerduty.com/oncalls?schedule_ids[]=PGCQ3YS' \ - --header 'Accept: application/json' \ - --header 'Authorization: Token token=${{ secrets.PAGERDUTY_ACCESS_KEY }}' \ - --header 'Content-Type: application/json' \ - | jq '.oncalls[].user.summary' | tr -d '"') - echo "current_oncall=$(echo ${current_oncall})" >> $GITHUB_ENV - - export oncall_name=$(curl -s --request GET \ - --url 'https://api.pagerduty.com/oncalls?schedule_ids[]=PGCQ3YS' \ - --header 'Accept: application/json' \ - --header 'Authorization: Token token=${{ secrets.PAGERDUTY_ACCESS_KEY }}' \ - --header 'Content-Type: application/json' \ - | jq '.oncalls[].escalation_policy.summary' | tr -d '"') - echo "oncall_name=$(echo ${oncall_name})" >> $GITHUB_ENV - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-west-2 - - - name: Get slack id for the oncall - run: | - export slack_id=$(aws s3 cp s3://iota-employees-dir/employees.json - | jq --arg ONCALL "${{ env.current_oncall }}" '.[] | if .name == $ONCALL then .slack_id else empty end') - echo "slack_id=$(echo ${slack_id} | tr -d '"')" >> $GITHUB_ENV - - - name: Post to slack - uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0 - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - IOTA_SHA: ${{ env.iota_sha }} - GH_JOB_LINK: ${{ env.gh_job_link }} - SLACK_ID: ${{ env.slack_id }} - ONCALL_NAME: ${{ env.oncall_name }} - with: - channel-id: "simtest-nightly" - payload: | - { - "text": "*${{ github.workflow }}* workflow status: `${{ env.WORKFLOW_CONCLUSION }}`", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*${{ github.workflow }}* workflow status: `${{ env.WORKFLOW_CONCLUSION }}`" - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "IOTA commit: \nRun: <${{ env.GH_JOB_LINK }}|${{ github.run_id }}>" - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "<@${{ env.SLACK_ID }}>, current `${{ env.ONCALL_NAME }}` oncall, please debug failures: `tsh ssh ubuntu@simtest-01` and look in the `/home/ubuntu/simtest_logs/{date}` folder for test results" - } - } - ] - } diff --git a/Cargo.lock b/Cargo.lock index c0b1c6e29bf..550aa462be0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -803,7 +803,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -5500,6 +5500,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -5508,12 +5626,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -8275,7 +8404,7 @@ version = "0.8.0-alpha" dependencies = [ "proc-macro2 1.0.86", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -8876,7 +9005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -8972,6 +9101,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -10066,7 +10201,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -11040,9 +11175,9 @@ dependencies = [ [[package]] name = "passkey-authenticator" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017d27e98940a98358b43a3fe19cb3d7b7c821c3b35634d8087230e92445579" +checksum = "f9b065ce31354bcf23a333003c77f0d71f00eb95761b3390a069546e078a7a5b" dependencies = [ "async-trait", "coset", @@ -11054,9 +11189,9 @@ dependencies = [ [[package]] name = "passkey-client" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14d42b14749cc7927add34a9932b3b3cc5349a633384850baa67183061439dd" +checksum = "5080bfafe23d139ae8be8b907453aee0b8af3ca7cf25d1f8d7bfcf7b079d3412" dependencies = [ "ciborium", "coset", @@ -11072,14 +11207,16 @@ dependencies = [ [[package]] name = "passkey-types" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "499cff8432e71c5f8784d9645aac0f9fca604d67f59b68a606170b5e229c6538" +checksum = "77144664f6aac5f629d7efa815f5098a054beeeca6ccafee5ec453fd2b0c53f9" dependencies = [ "bitflags 2.6.0", "ciborium", "coset", "data-encoding", + "getrandom 0.2.15", + "hmac", "indexmap 2.5.0", "rand 0.8.5", "serde", @@ -11087,6 +11224,7 @@ dependencies = [ "sha2 0.10.8", "strum 0.25.0", "typeshare", + "zeroize", ] [[package]] @@ -14337,6 +14475,17 @@ dependencies = [ "unicode-xid 0.2.6", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "sysinfo" version = "0.31.4" @@ -14710,6 +14859,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -15338,7 +15497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] @@ -15466,12 +15625,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.13" @@ -15563,9 +15716,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -15585,6 +15738,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[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" @@ -16218,6 +16383,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -16328,6 +16505,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", + "synstructure 0.13.1", +] + [[package]] name = "yup-oauth2" version = "8.3.2" @@ -16376,6 +16577,27 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", + "synstructure 0.13.1", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -16397,6 +16619,28 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 277ac3a5492..9dbfc1385f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -284,9 +284,9 @@ object_store = { version = "0.10", features = ["aws", "gcp", "azure", "http"] } once_cell = "1.18.0" p256 = { version = "0.13.2", features = ["ecdsa"] } parking_lot = "0.12.1" -passkey-authenticator = { version = "0.2.0" } -passkey-client = { version = "0.2.0" } -passkey-types = { version = "0.2.0" } +passkey-authenticator = "0.4.0" +passkey-client = "0.4.0" +passkey-types = "0.4.0" pretty_assertions = "1.3.0" proc-macro2 = "1.0.47" prometheus = "0.13.3" diff --git a/apps/apps-backend/src/features/features.controller.ts b/apps/apps-backend/src/features/features.controller.ts index c3947236ec5..a3f3dbbdd3f 100644 --- a/apps/apps-backend/src/features/features.controller.ts +++ b/apps/apps-backend/src/features/features.controller.ts @@ -134,10 +134,10 @@ export class FeaturesController { defaultValue: false, }, [Feature.StardustMigration]: { - defaultValue: false, + defaultValue: true, }, [Feature.SupplyIncreaseVesting]: { - defaultValue: false, + defaultValue: true, }, }, dateUpdated: new Date().toISOString(), diff --git a/apps/explorer/src/components/header/ThemeSwitcher.tsx b/apps/core/src/components/buttons/ThemeSwitcher.tsx similarity index 89% rename from apps/explorer/src/components/header/ThemeSwitcher.tsx rename to apps/core/src/components/buttons/ThemeSwitcher.tsx index b947cafe76c..922deb78957 100644 --- a/apps/explorer/src/components/header/ThemeSwitcher.tsx +++ b/apps/core/src/components/buttons/ThemeSwitcher.tsx @@ -3,7 +3,8 @@ import { Button, ButtonType } from '@iota/apps-ui-kit'; import { DarkMode, LightMode } from '@iota/ui-icons'; -import { useTheme, Theme, ThemePreference } from '@iota/core'; +import { Theme, ThemePreference } from '../../enums'; +import { useTheme } from '../../hooks'; export function ThemeSwitcher(): React.JSX.Element { const { theme, themePreference, setThemePreference } = useTheme(); diff --git a/apps/core/src/components/buttons/index.ts b/apps/core/src/components/buttons/index.ts index b7638e371ce..95938f61610 100644 --- a/apps/core/src/components/buttons/index.ts +++ b/apps/core/src/components/buttons/index.ts @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 export * from './ViewTxnOnExplorerButton'; +export * from './ThemeSwitcher'; diff --git a/apps/core/src/components/stake/StakedCard.tsx b/apps/core/src/components/stake/StakedCard.tsx index 58bc5c82f20..92f6b0e8184 100644 --- a/apps/core/src/components/stake/StakedCard.tsx +++ b/apps/core/src/components/stake/StakedCard.tsx @@ -37,7 +37,7 @@ export function StakedCard({ // For inactive validator, show principal + rewards const [principalStaked, symbol] = useFormatCoin( - inactiveValidator ? principal + rewards : principal, + inactiveValidator ? BigInt(principal) + rewards : principal, IOTA_TYPE_ARG, ); diff --git a/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx b/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx index bb69cf69226..94e120821d3 100644 --- a/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx +++ b/apps/core/src/components/transaction/info/UnstakeTransactionInfo.tsx @@ -27,21 +27,21 @@ export function UnstakeTransactionInfo({ renderExplorerLink, }: UnstakeTransactionInfoProps) { const json = event.parsedJson as { - principal_amount?: number; - reward_amount?: number; + principal_amount?: string; + reward_amount?: string; validator_address?: string; }; - const principalAmount = json?.principal_amount || 0; - const rewardAmount = json?.reward_amount || 0; + const principalAmount = json?.principal_amount || '0'; + const rewardAmount = json?.reward_amount || '0'; const validatorAddress = json?.validator_address; - const totalAmount = Number(principalAmount) + Number(rewardAmount); + const totalAmount = BigInt(principalAmount) + BigInt(rewardAmount); const [formatPrinciple, symbol] = useFormatCoin(principalAmount, IOTA_TYPE_ARG); const [formatRewards] = useFormatCoin(rewardAmount || 0, IOTA_TYPE_ARG); return (
{validatorAddress && } - {totalAmount && ( + {totalAmount !== 0n && ( )} diff --git a/apps/core/src/hooks/index.ts b/apps/core/src/hooks/index.ts index 89aced3f57f..1602541e57b 100644 --- a/apps/core/src/hooks/index.ts +++ b/apps/core/src/hooks/index.ts @@ -47,5 +47,6 @@ export * from './useOwnedNFT'; export * from './useNftDetails'; export * from './useCountdownByTimestamp'; export * from './useStakeRewardStatus'; +export * from './useRecognizedPackages'; export * from './stake'; diff --git a/apps/core/src/hooks/useCountdownByTimestamp.ts b/apps/core/src/hooks/useCountdownByTimestamp.ts index 3b79b69b915..18a5a598292 100644 --- a/apps/core/src/hooks/useCountdownByTimestamp.ts +++ b/apps/core/src/hooks/useCountdownByTimestamp.ts @@ -9,7 +9,17 @@ import { MILLISECONDS_PER_SECOND, } from '../constants'; -export function useCountdownByTimestamp(initialTimestamp: number | null): string { +interface FormatCountdownOptions { + showSeconds?: boolean; + showMinutes?: boolean; + showHours?: boolean; + showDays?: boolean; +} + +export function useCountdownByTimestamp( + initialTimestamp: number | null, + options?: FormatCountdownOptions, +): string { const [timeRemainingMs, setTimeRemainingMs] = useState(0); useEffect(() => { @@ -22,11 +32,19 @@ export function useCountdownByTimestamp(initialTimestamp: number | null): string return () => clearInterval(interval); }, [initialTimestamp]); - const formattedCountdown = formatCountdown(timeRemainingMs); + const formattedCountdown = formatCountdown(timeRemainingMs, options); return formattedCountdown; } -function formatCountdown(totalMilliseconds: number) { +function formatCountdown( + totalMilliseconds: number, + { + showSeconds = true, + showMinutes = true, + showHours = true, + showDays = true, + }: FormatCountdownOptions = {}, +) { const days = Math.floor(totalMilliseconds / MILLISECONDS_PER_DAY); const hours = Math.floor((totalMilliseconds % MILLISECONDS_PER_DAY) / MILLISECONDS_PER_HOUR); const minutes = Math.floor( @@ -36,11 +54,11 @@ function formatCountdown(totalMilliseconds: number) { (totalMilliseconds % MILLISECONDS_PER_MINUTE) / MILLISECONDS_PER_SECOND, ); - const timeUnits = []; - if (days > 0) timeUnits.push(`${days}d`); - if (hours > 0) timeUnits.push(`${hours}h`); - if (minutes > 0) timeUnits.push(`${minutes}m`); - if (seconds > 0 || timeUnits.length === 0) timeUnits.push(`${seconds}s`); + const timeUnits: string[] = []; + if (showDays && days > 0) timeUnits.push(`${days}d`); + if (showHours && hours > 0) timeUnits.push(`${hours}h`); + if (showMinutes && minutes > 0) timeUnits.push(`${minutes}m`); + if (showSeconds && (seconds > 0 || timeUnits.length === 0)) timeUnits.push(`${seconds}s`); return timeUnits.join(' '); } diff --git a/apps/explorer/src/hooks/useRecognizedPackages.ts b/apps/core/src/hooks/useRecognizedPackages.ts similarity index 61% rename from apps/explorer/src/hooks/useRecognizedPackages.ts rename to apps/core/src/hooks/useRecognizedPackages.ts index 811c2abfdf4..15a62c80447 100644 --- a/apps/explorer/src/hooks/useRecognizedPackages.ts +++ b/apps/core/src/hooks/useRecognizedPackages.ts @@ -2,18 +2,11 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Feature } from '@iota/core'; import { useFeatureValue } from '@growthbook/growthbook-react'; -import { IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS } from '@iota/iota-sdk/utils'; import { Network } from '@iota/iota-sdk/client'; +import { Feature, DEFAULT_RECOGNIZED_PACKAGES } from '../../'; -import { useNetwork } from './'; - -const DEFAULT_RECOGNIZED_PACKAGES = [IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS]; - -export function useRecognizedPackages(): string[] { - const [network] = useNetwork(); - +export function useRecognizedPackages(network: Network): string[] { const recognizedPackages = useFeatureValue( Feature.RecognizedPackages, DEFAULT_RECOGNIZED_PACKAGES, diff --git a/apps/core/src/utils/getStakeIotaByIotaId.ts b/apps/core/src/utils/getStakeIotaByIotaId.ts index 5bdaa2a01ee..ba2e8837c99 100644 --- a/apps/core/src/utils/getStakeIotaByIotaId.ts +++ b/apps/core/src/utils/getStakeIotaByIotaId.ts @@ -10,7 +10,7 @@ export function getStakeIotaByIotaId(allDelegation: DelegatedStake[], stakeIotaI allDelegation.reduce((acc, curr) => { const total = BigInt( curr.stakes.find(({ stakedIotaId }) => stakedIotaId === stakeIotaId)?.principal || - 0, + 0n, ); return total + acc; }, 0n) || 0n diff --git a/apps/core/src/utils/migration/createMigrationTransaction.ts b/apps/core/src/utils/migration/createMigrationTransaction.ts index 8519a950831..c5ac17fa556 100644 --- a/apps/core/src/utils/migration/createMigrationTransaction.ts +++ b/apps/core/src/utils/migration/createMigrationTransaction.ts @@ -1,7 +1,7 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { IotaClient, IotaObjectData } from '@iota/iota-sdk/client'; +import { DynamicFieldInfo, IotaClient, IotaObjectData } from '@iota/iota-sdk/client'; import { Transaction } from '@iota/iota-sdk/transactions'; import { STARDUST_PACKAGE_ID } from '../../constants/migration.constants'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; @@ -17,21 +17,28 @@ type NestedResultType = { NestedResult: [number, number]; }; -export async function getNativeTokenTypesFromBag( - bagId: string, - client: IotaClient, -): Promise { +export async function getNativeTokensFromBag(bagId: string, client: IotaClient) { const nativeTokenDynamicFields = await client.getDynamicFields({ parentId: bagId, }); - const nativeTokenTypes: string[] = []; + const nativeTokenTypes: DynamicFieldInfo[] = []; for (const nativeToken of nativeTokenDynamicFields.data) { - nativeTokenTypes.push(nativeToken?.name?.value as string); + nativeTokenTypes.push(nativeToken); } return nativeTokenTypes; } +export async function getNativeTokenTypesFromBag( + bagId: string, + client: IotaClient, +): Promise { + const nativeTokenDynamicFields = await client.getDynamicFields({ + parentId: bagId, + }); + return nativeTokenDynamicFields.data.map(({ name }) => name.value as string); +} + export function validateBasicOutputObject(outputObject: IotaObjectData): BasicOutputObject { if (outputObject.content?.dataType !== 'moveObject') { throw new Error('Invalid basic output object'); diff --git a/apps/core/src/utils/parseObjectDetails.ts b/apps/core/src/utils/parseObjectDetails.ts index 76a9884c5d8..fefa3c717e1 100644 --- a/apps/core/src/utils/parseObjectDetails.ts +++ b/apps/core/src/utils/parseObjectDetails.ts @@ -9,10 +9,16 @@ type ObjectChangeWithObjectType = Extract< { objectType: string } >; +type PackageId = string; +type ModuleName = string; +type TypeName = string; export function parseObjectChangeDetails( objectChange: ObjectChangeWithObjectType, -): [string, string, string] { - const [packageId, moduleName, typeName] = - objectChange.objectType?.split('<')[0]?.split('::') || []; - return [packageId, moduleName, typeName]; +): [PackageId, ModuleName, TypeName] { + return extractObjectTypeStruct(objectChange.objectType); +} + +export function extractObjectTypeStruct(objectType: string): [PackageId, ModuleName, TypeName] { + const [packageId, moduleName, functionName] = objectType?.split('<')[0]?.split('::') || []; + return [packageId, moduleName, functionName]; } diff --git a/apps/explorer/src/components/header/Header.tsx b/apps/explorer/src/components/header/Header.tsx index a3b0cbcb00b..a2f95b165d5 100644 --- a/apps/explorer/src/components/header/Header.tsx +++ b/apps/explorer/src/components/header/Header.tsx @@ -5,7 +5,8 @@ import { NetworkSelector } from '../network'; import Search from '../search/Search'; import { LinkWithQuery } from '~/components/ui'; -import { ThemeSwitcher, ThemedIotaLogo } from '~/components'; +import { ThemedIotaLogo } from '~/components'; +import { ThemeSwitcher } from '@iota/core'; function Header(): JSX.Element { return ( diff --git a/apps/explorer/src/components/header/index.ts b/apps/explorer/src/components/header/index.ts index b23c1fae65a..8a717a10a0b 100644 --- a/apps/explorer/src/components/header/index.ts +++ b/apps/explorer/src/components/header/index.ts @@ -2,4 +2,3 @@ // SPDX-License-Identifier: Apache-2.0 export * from './Header'; -export * from './ThemeSwitcher'; diff --git a/apps/explorer/src/components/home-metrics/OnTheNetwork.tsx b/apps/explorer/src/components/home-metrics/OnTheNetwork.tsx index d98dc4b126f..35bd6468232 100644 --- a/apps/explorer/src/components/home-metrics/OnTheNetwork.tsx +++ b/apps/explorer/src/components/home-metrics/OnTheNetwork.tsx @@ -98,7 +98,7 @@ export function OnTheNetwork(): JSX.Element { size={LabelTextSize.Large} label="Reference Gas Price" text={gasPriceFormatted ?? '-'} - supportingLabel={gasPriceFormatted !== null ? 'IOTA' : undefined} + supportingLabel={gasPriceFormatted !== null ? 'nano' : undefined} tooltipPosition={TooltipPosition.Top} tooltipText="The reference gas price in the current epoch." /> diff --git a/apps/explorer/src/components/network/NetworkSelector.tsx b/apps/explorer/src/components/network/NetworkSelector.tsx index 34da8635f62..deb20e35e9b 100644 --- a/apps/explorer/src/components/network/NetworkSelector.tsx +++ b/apps/explorer/src/components/network/NetworkSelector.tsx @@ -99,11 +99,12 @@ export function NetworkSelector(): JSX.Element { key={idx} onClick={() => handleNetworkSwitch(network.id)} hideBottomBorder + isHighlighted={network === selectedNetwork} >
diff --git a/apps/explorer/src/components/owned-coins/OwnedCoins.tsx b/apps/explorer/src/components/owned-coins/OwnedCoins.tsx index 8df810f8f0c..619fd8e9864 100644 --- a/apps/explorer/src/components/owned-coins/OwnedCoins.tsx +++ b/apps/explorer/src/components/owned-coins/OwnedCoins.tsx @@ -2,14 +2,14 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { getCoinSymbol } from '@iota/core'; +import { getCoinSymbol, useRecognizedPackages } from '@iota/core'; import { useIotaClientQuery } from '@iota/dapp-kit'; -import { type CoinBalance } from '@iota/iota-sdk/client'; +import { type CoinBalance, type Network } from '@iota/iota-sdk/client'; +import { useNetwork } from '~/hooks'; import { normalizeIotaAddress } from '@iota/iota-sdk/utils'; import { FilterList, Warning } from '@iota/ui-icons'; import { useMemo, useState } from 'react'; import OwnedCoinView from './OwnedCoinView'; -import { useRecognizedPackages } from '~/hooks/useRecognizedPackages'; import { Button, ButtonType, @@ -47,7 +47,8 @@ export function OwnedCoins({ id }: OwnerCoinsProps): JSX.Element { const { isPending, data, isError } = useIotaClientQuery('getAllBalances', { owner: normalizeIotaAddress(id), }); - const recognizedPackages = useRecognizedPackages(); + const [network] = useNetwork(); + const recognizedPackages = useRecognizedPackages(network as Network); const balances: Record = useMemo(() => { const balanceData = data?.reduce( diff --git a/apps/explorer/src/hooks/index.ts b/apps/explorer/src/hooks/index.ts index 9fbe82e3302..a52c31b12d6 100644 --- a/apps/explorer/src/hooks/index.ts +++ b/apps/explorer/src/hooks/index.ts @@ -17,7 +17,6 @@ export * from './useInitialPageView'; export * from './useMediaQuery'; export * from './useNetwork'; export * from './useNormalizedMoveModule'; -export * from './useRecognizedPackages'; export * from './useResolveVideo'; export * from './useSearch'; export * from './useVerifiedSourceCode'; diff --git a/apps/explorer/src/hooks/useResolveVideo.ts b/apps/explorer/src/hooks/useResolveVideo.ts index 19d7c871d23..a40622340a3 100644 --- a/apps/explorer/src/hooks/useResolveVideo.ts +++ b/apps/explorer/src/hooks/useResolveVideo.ts @@ -2,12 +2,14 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { type IotaObjectResponse } from '@iota/iota-sdk/client'; +import { type IotaObjectResponse, type Network } from '@iota/iota-sdk/client'; -import { useRecognizedPackages } from './useRecognizedPackages'; +import { useRecognizedPackages } from '@iota/core'; +import { useNetwork } from '~/hooks'; export function useResolveVideo(object: IotaObjectResponse): string | undefined | null { - const recognizedPackages = useRecognizedPackages(); + const [network] = useNetwork(); + const recognizedPackages = useRecognizedPackages(network as Network); const objectType = (object.data?.type ?? object?.data?.content?.dataType === 'package') ? 'package' diff --git a/apps/explorer/src/pages/transaction-result/TransactionData.tsx b/apps/explorer/src/pages/transaction-result/TransactionData.tsx index de3d78e5890..17617d7363a 100644 --- a/apps/explorer/src/pages/transaction-result/TransactionData.tsx +++ b/apps/explorer/src/pages/transaction-result/TransactionData.tsx @@ -2,13 +2,14 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { useTransactionSummary } from '@iota/core'; +import { useTransactionSummary, useRecognizedPackages } from '@iota/core'; import { type ProgrammableTransaction, type IotaTransactionBlockResponse, + type Network, } from '@iota/iota-sdk/client'; import { GasBreakdown } from '~/components'; -import { useRecognizedPackages } from '~/hooks/useRecognizedPackages'; +import { useNetwork } from '~/hooks/useNetwork'; import { InputsCard } from '~/pages/transaction-result/programmable-transaction-view/InputsCard'; import { TransactionsCard } from '~/pages/transaction-result/programmable-transaction-view/TransactionsCard'; @@ -17,7 +18,8 @@ interface TransactionDataProps { } export function TransactionData({ transaction }: TransactionDataProps): JSX.Element { - const recognizedPackagesList = useRecognizedPackages(); + const [network] = useNetwork(); + const recognizedPackagesList = useRecognizedPackages(network as Network); const summary = useTransactionSummary({ transaction, recognizedPackagesList, diff --git a/apps/explorer/src/pages/transaction-result/TransactionView.tsx b/apps/explorer/src/pages/transaction-result/TransactionView.tsx index 06b5f3d6c15..73af5407edf 100644 --- a/apps/explorer/src/pages/transaction-result/TransactionView.tsx +++ b/apps/explorer/src/pages/transaction-result/TransactionView.tsx @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { type IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; +import { type Network, type IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; import clsx from 'clsx'; import { useState } from 'react'; import { ErrorBoundary, SplitPanes } from '~/components'; @@ -11,8 +11,8 @@ import { TransactionData } from '~/pages/transaction-result/TransactionData'; import { TransactionSummary } from '~/pages/transaction-result/transaction-summary'; import { Signatures } from './Signatures'; import { TransactionDetails } from './transaction-summary/TransactionDetails'; -import { useTransactionSummary } from '@iota/core'; -import { useBreakpoint, useRecognizedPackages } from '~/hooks'; +import { useTransactionSummary, useRecognizedPackages } from '@iota/core'; +import { useBreakpoint, useNetwork } from '~/hooks'; import { ButtonSegment, ButtonSegmentType, @@ -42,7 +42,8 @@ export function TransactionView({ transaction }: TransactionViewProps): JSX.Elem const transactionKindName = transaction.transaction?.data.transaction?.kind; const isProgrammableTransaction = transactionKindName === 'ProgrammableTransaction'; - const recognizedPackagesList = useRecognizedPackages(); + const [network] = useNetwork(); + const recognizedPackagesList = useRecognizedPackages(network as Network); const summary = useTransactionSummary({ transaction, recognizedPackagesList, diff --git a/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts b/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts index 92e2e32d939..e239ce3a86e 100644 --- a/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts +++ b/apps/explorer/src/pages/transaction-result/programmable-transaction-view/utils.ts @@ -24,10 +24,11 @@ export function flattenIotaArguments(data: (IotaArgument | IotaArgument[])[]): s } else if ('NestedResult' in value) { return `NestedResult(${value.NestedResult[0]}, ${value.NestedResult[1]})`; } + } else if (typeof value === 'string') { + return value; } else { throw new Error('Not a correct flattenable data'); } - return ''; }) .join(', '); } diff --git a/apps/explorer/src/pages/transaction-result/transaction-summary/index.tsx b/apps/explorer/src/pages/transaction-result/transaction-summary/index.tsx index 7ea356fbcb2..ea5d48cbab6 100644 --- a/apps/explorer/src/pages/transaction-result/transaction-summary/index.tsx +++ b/apps/explorer/src/pages/transaction-result/transaction-summary/index.tsx @@ -2,20 +2,20 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { useTransactionSummary } from '@iota/core'; -import { type IotaTransactionBlockResponse } from '@iota/iota-sdk/client'; - +import { useTransactionSummary, useRecognizedPackages } from '@iota/core'; +import { type IotaTransactionBlockResponse, type Network } from '@iota/iota-sdk/client'; import { BalanceChanges } from './BalanceChanges'; import { ObjectChanges } from './ObjectChanges'; import { UpgradedSystemPackages } from './UpgradedSystemPackages'; -import { useRecognizedPackages } from '~/hooks/useRecognizedPackages'; +import { useNetwork } from '~/hooks'; interface TransactionSummaryProps { transaction: IotaTransactionBlockResponse; } export function TransactionSummary({ transaction }: TransactionSummaryProps): JSX.Element { - const recognizedPackagesList = useRecognizedPackages(); + const [network] = useNetwork(); + const recognizedPackagesList = useRecognizedPackages(network as Network); const summary = useTransactionSummary({ transaction, recognizedPackagesList, diff --git a/apps/ui-kit/package.json b/apps/ui-kit/package.json index ff323347fea..89e9785c466 100644 --- a/apps/ui-kit/package.json +++ b/apps/ui-kit/package.json @@ -66,7 +66,7 @@ "tailwindcss": "^3.3.3", "typescript": "^5.5.3", "vite": "^5.3.3", - "vite-plugin-dts": "^3.9.1", + "vite-plugin-dts": "^4.3.0", "vite-tsconfig-paths": "^4.2.0" } } diff --git a/apps/ui-kit/src/lib/components/atoms/index.ts b/apps/ui-kit/src/lib/components/atoms/index.ts index 373f611cd1e..12a58426391 100644 --- a/apps/ui-kit/src/lib/components/atoms/index.ts +++ b/apps/ui-kit/src/lib/components/atoms/index.ts @@ -19,3 +19,4 @@ export * from './snackbar'; export * from './visual-asset-card'; export * from './loading-indicator'; export * from './placeholder'; +export * from './skeleton'; diff --git a/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx b/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx index 569054372f2..d15da2af5de 100644 --- a/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx +++ b/apps/ui-kit/src/lib/components/atoms/key-value-info/KeyValueInfo.tsx @@ -57,6 +57,10 @@ interface KeyValueProps { * Reverse the KeyValue (optional). */ isReverse?: boolean; + /** + * Text shown on value hover. + */ + valueHoverTitle?: string; } export function KeyValueInfo({ @@ -72,6 +76,7 @@ export function KeyValueInfo({ onCopyError, fullwidth, isReverse = false, + valueHoverTitle, }: KeyValueProps): React.JSX.Element { const flexDirectionClass = isReverse ? 'flex-row-reverse' : 'flex-row'; async function handleCopyClick(event: React.MouseEvent) { @@ -119,6 +124,7 @@ export function KeyValueInfo({ })} > ): React.JSX.Element { function handleKeyDown(event: React.KeyboardEvent) { if ((event.key === 'Enter' || event.key === ' ') && !isDisabled && onClick) { @@ -63,7 +68,10 @@ export function ListItem({ className={cx( 'relative flex flex-row items-center justify-between px-md py-sm text-neutral-10 dark:text-neutral-92', !isDisabled && onClick ? 'cursor-pointer' : 'cursor-default', - { 'state-layer': !isDisabled }, + { + 'bg-shader-primary-dark-16 dark:bg-shader-inverted-dark-16': isHighlighted, + 'state-layer': !isDisabled, + }, )} > {children} diff --git a/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx b/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx index 6a0c8d5df45..86160c55771 100644 --- a/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx +++ b/apps/ui-kit/src/lib/components/atoms/loading-indicator/LoadingIndicator.tsx @@ -18,16 +18,21 @@ export interface LoadingIndicatorProps { * The text to display next to the loading indicator. */ text?: string; + /** + * The 'data-testid' attribute value (used in e2e tests) + */ + testId?: string; } export function LoadingIndicator({ color = 'text-primary-30 dark:text-primary-80', size = 'w-5 h-5', text, + testId, }: LoadingIndicatorProps): React.JSX.Element { return (
- + {text && {text}}
); diff --git a/apps/ui-kit/src/lib/components/atoms/skeleton/Skeleton.tsx b/apps/ui-kit/src/lib/components/atoms/skeleton/Skeleton.tsx new file mode 100644 index 00000000000..9504693f036 --- /dev/null +++ b/apps/ui-kit/src/lib/components/atoms/skeleton/Skeleton.tsx @@ -0,0 +1,47 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import cx from 'classnames'; + +interface SkeletonLoaderProps { + /** + * Width class for the skeleton div. + */ + widthClass?: string; + /** + * Height class for the skeleton div. + */ + heightClass?: string; + /** + * If true, the skeleton will use darker neutral colors. + */ + hasSecondaryColors?: boolean; + /** + * Whether the class `rounded-full` should be applied. Defaults to true. + */ + isRounded?: boolean; +} + +export function Skeleton({ + children, + widthClass = 'w-full', + heightClass = 'h-3', + hasSecondaryColors, + isRounded = true, +}: React.PropsWithChildren): React.JSX.Element { + return ( +
+ {children} +
+ ); +} diff --git a/apps/wallet-dashboard/contexts/index.ts b/apps/ui-kit/src/lib/components/atoms/skeleton/index.ts similarity index 70% rename from apps/wallet-dashboard/contexts/index.ts rename to apps/ui-kit/src/lib/components/atoms/skeleton/index.ts index d356e2485ed..4540f454c5f 100644 --- a/apps/wallet-dashboard/contexts/index.ts +++ b/apps/ui-kit/src/lib/components/atoms/skeleton/index.ts @@ -1,4 +1,4 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './PopupContext'; +export * from './Skeleton'; diff --git a/apps/ui-kit/src/storybook/stories/atoms/Skeleton.stories.tsx b/apps/ui-kit/src/storybook/stories/atoms/Skeleton.stories.tsx new file mode 100644 index 00000000000..cb611c8cf08 --- /dev/null +++ b/apps/ui-kit/src/storybook/stories/atoms/Skeleton.stories.tsx @@ -0,0 +1,33 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import type { Meta, StoryObj } from '@storybook/react'; +import { Card, CardImage, ImageShape, Skeleton } from '@/components'; + +const meta: Meta = { + component: Skeleton, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const SkeletonCard: Story = { + render: () => ( + + +
+ + +
+ + +
+
+ + +
+ + ), +}; diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index ed96fe0a519..7007cc86c5c 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -29,7 +29,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { const [selectedAsset, setSelectedAsset] = useState(null); const [selectedCategory, setSelectedCategory] = useState(AssetCategory.Visual); const account = useCurrentAccount(); - const { data, isFetching, fetchNextPage, hasNextPage } = useGetOwnedObjects( + const { data, isFetching, fetchNextPage, hasNextPage, refetch } = useGetOwnedObjects( account?.address, undefined, OBJECTS_PER_REQ, @@ -79,7 +79,11 @@ export default function AssetsDashboardPage(): React.JSX.Element { fetchNextPage={fetchNextPage} /> {selectedAsset && ( - setSelectedAsset(null)} asset={selectedAsset} /> + setSelectedAsset(null)} + asset={selectedAsset} + refetchAssets={refetch} + /> )}
diff --git a/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx b/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx index 8dd5f01ca2b..3b802387ca8 100644 --- a/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx +++ b/apps/wallet-dashboard/app/(protected)/components/top-nav/TopNav.tsx @@ -1,16 +1,43 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { SettingsDialog, useSettingsDialog } from '@/components'; import { Badge, BadgeType, Button, ButtonType } from '@iota/apps-ui-kit'; -import { ConnectButton } from '@iota/dapp-kit'; +import { ConnectButton, useIotaClientContext } from '@iota/dapp-kit'; +import { getNetwork, Network } from '@iota/iota-sdk/client'; +import { ThemeSwitcher } from '@iota/core'; import { Settings } from '@iota/ui-icons'; export function TopNav() { + const { network } = useIotaClientContext(); + const { name: networkName } = getNetwork(network); + const { + isSettingsDialogOpen, + settingsDialogView, + setSettingsDialogView, + onCloseSettingsDialogClick, + onOpenSettingsDialogClick, + } = useSettingsDialog(); + return (
- + -
); } diff --git a/apps/wallet-dashboard/app/(protected)/home/page.tsx b/apps/wallet-dashboard/app/(protected)/home/page.tsx index 61f55475702..d435d44f630 100644 --- a/apps/wallet-dashboard/app/(protected)/home/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/home/page.tsx @@ -8,6 +8,7 @@ import { TransactionsOverview, StakingOverview, MigrationOverview, + SupplyIncreaseVestingOverview, } from '@/components'; import { useFeature } from '@growthbook/growthbook-react'; import { Feature } from '@iota/core'; @@ -18,12 +19,13 @@ function HomeDashboardPage(): JSX.Element { const account = useCurrentAccount(); const stardustMigrationEnabled = useFeature(Feature.StardustMigration).value; + const supplyIncreaseVestingEnabled = useFeature(Feature.SupplyIncreaseVesting).value; return (
{connectionStatus === 'connected' && account && ( <> -
+
@@ -31,12 +33,10 @@ function HomeDashboardPage(): JSX.Element {
{stardustMigrationEnabled && } -
+
-
- Vesting -
+ {supplyIncreaseVestingEnabled && }
diff --git a/apps/wallet-dashboard/app/(protected)/layout.tsx b/apps/wallet-dashboard/app/(protected)/layout.tsx index a77c8922ab9..72fa80c67cc 100644 --- a/apps/wallet-dashboard/app/(protected)/layout.tsx +++ b/apps/wallet-dashboard/app/(protected)/layout.tsx @@ -4,21 +4,9 @@ import { Notifications } from '@/components/index'; import React, { type PropsWithChildren } from 'react'; -import { Button } from '@iota/apps-ui-kit'; import { Sidebar, TopNav } from './components'; -import { ThemePreference, useTheme } from '@iota/core'; function DashboardLayout({ children }: PropsWithChildren): JSX.Element { - const { theme, themePreference, setThemePreference } = useTheme(); - - const toggleTheme = () => { - const newTheme = - themePreference === ThemePreference.Light - ? ThemePreference.Dark - : ThemePreference.Light; - setThemePreference(newTheme); - }; - return (
@@ -34,14 +22,6 @@ function DashboardLayout({ children }: PropsWithChildren): JSX.Element {
{children}
- -
-
-
); diff --git a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx index 544f352e963..e2cd767e663 100644 --- a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx @@ -1,10 +1,13 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 + 'use client'; -import MigratePopup from '@/components/Popup/Popups/MigratePopup'; -import { usePopups } from '@/hooks'; -import { summarizeMigratableObjectValues } from '@/lib/utils'; +import { useState, useMemo, useCallback } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; +import clsx from 'clsx'; +import { useGetStardustMigratableObjects } from '@/hooks'; +import { summarizeMigratableObjectValues, summarizeTimelockedObjectValues } from '@/lib/utils'; import { Button, ButtonSize, @@ -16,119 +19,156 @@ import { Panel, Title, } from '@iota/apps-ui-kit'; +import { Assets, Clock, IotaLogoMark, Tokens } from '@iota/ui-icons'; import { useCurrentAccount, useIotaClient } from '@iota/dapp-kit'; import { STARDUST_BASIC_OUTPUT_TYPE, STARDUST_NFT_OUTPUT_TYPE, useFormatCoin } from '@iota/core'; -import { useGetStardustMigratableObjects } from '@/hooks'; -import { useQueryClient } from '@tanstack/react-query'; -import { Assets, Clock, IotaLogoMark, Tokens } from '@iota/ui-icons'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; - -interface MigrationDisplayCard { - title: string; - subtitle: string; - icon: React.FC; -} +import { StardustOutputMigrationStatus } from '@/lib/enums'; +import { MigrationObjectsPanel, MigrationDialog } from '@/components'; +import { useRouter } from 'next/navigation'; function MigrationDashboardPage(): JSX.Element { const account = useCurrentAccount(); const address = account?.address || ''; - const { openPopup, closePopup } = usePopups(); const queryClient = useQueryClient(); const iotaClient = useIotaClient(); + const router = useRouter(); + const [isMigrationDialogOpen, setIsMigrationDialogOpen] = useState(false); + const [selectedStardustObjectsCategory, setSelectedStardustObjectsCategory] = useState< + StardustOutputMigrationStatus | undefined + >(undefined); + const { data: stardustMigrationObjects, isPlaceholderData } = + useGetStardustMigratableObjects(address); const { migratableBasicOutputs, - unmigratableBasicOutputs, migratableNftOutputs, - unmigratableNftOutputs, - } = useGetStardustMigratableObjects(address); + timelockedBasicOutputs, + timelockedNftOutputs, + } = stardustMigrationObjects || {}; + + const { + totalIotaAmount, + totalNativeTokens: migratableNativeTokens, + totalVisualAssets: migratableVisualAssets, + } = summarizeMigratableObjectValues({ + basicOutputs: migratableBasicOutputs, + nftOutputs: migratableNftOutputs, + address, + }); + const { totalTimelockedObjects } = summarizeTimelockedObjectValues({ + basicOutputs: timelockedBasicOutputs, + nftOutputs: timelockedNftOutputs, + }); const hasMigratableObjects = - migratableBasicOutputs.length > 0 || migratableNftOutputs.length > 0; - - function handleOnSuccess(digest: string): void { - iotaClient - .waitForTransaction({ - digest, - }) - .then(() => { + (migratableBasicOutputs?.length || 0) > 0 && (migratableNftOutputs?.length || 0) > 0; + + const [timelockedIotaTokens, symbol] = useFormatCoin(totalIotaAmount, IOTA_TYPE_ARG); + + const handleOnSuccess = useCallback( + (digest: string) => { + iotaClient.waitForTransaction({ digest }).then(() => { queryClient.invalidateQueries({ queryKey: [ 'get-all-owned-objects', address, - { - StructType: STARDUST_BASIC_OUTPUT_TYPE, - }, + { StructType: STARDUST_BASIC_OUTPUT_TYPE }, ], }); queryClient.invalidateQueries({ queryKey: [ 'get-all-owned-objects', address, - { - StructType: STARDUST_NFT_OUTPUT_TYPE, - }, + { StructType: STARDUST_NFT_OUTPUT_TYPE }, ], }); }); - } - function openMigratePopup(): void { - openPopup( - , - ); - } - - const { - accumulatedIotaAmount: accumulatedTimelockedIotaAmount, - totalNativeTokens, - totalVisualAssets, - } = summarizeMigratableObjectValues({ - migratableBasicOutputs, - migratableNftOutputs, - address, - }); - - const [timelockedIotaTokens, symbol] = useFormatCoin( - accumulatedTimelockedIotaAmount, - IOTA_TYPE_ARG, + }, + [iotaClient, queryClient, address], ); - const MIGRATION_CARDS: MigrationDisplayCard[] = [ + const MIGRATION_CARDS: MigrationDisplayCardProps[] = [ { title: `${timelockedIotaTokens} ${symbol}`, subtitle: 'IOTA Tokens', icon: IotaLogoMark, }, { - title: `${totalNativeTokens}`, + title: `${migratableNativeTokens}`, subtitle: 'Native Tokens', icon: Tokens, }, { - title: `${totalVisualAssets}`, + title: `${migratableVisualAssets}`, subtitle: 'Visual Assets', icon: Assets, }, ]; - const timelockedAssetsAmount = unmigratableBasicOutputs.length + unmigratableNftOutputs.length; - const TIMELOCKED_ASSETS_CARDS: MigrationDisplayCard[] = [ + const TIMELOCKED_ASSETS_CARDS: MigrationDisplayCardProps[] = [ { - title: `${timelockedAssetsAmount}`, + title: `${totalTimelockedObjects}`, subtitle: 'Time-locked', icon: Clock, }, ]; + const selectedObjects = useMemo(() => { + if (stardustMigrationObjects) { + if (selectedStardustObjectsCategory === StardustOutputMigrationStatus.Migratable) { + return [ + ...stardustMigrationObjects.migratableBasicOutputs, + ...stardustMigrationObjects.migratableNftOutputs, + ]; + } else if ( + selectedStardustObjectsCategory === StardustOutputMigrationStatus.TimeLocked + ) { + return [ + ...stardustMigrationObjects.timelockedBasicOutputs, + ...stardustMigrationObjects.timelockedNftOutputs, + ]; + } + } + return []; + }, [selectedStardustObjectsCategory, stardustMigrationObjects]); + + function openMigrationDialog(): void { + setIsMigrationDialogOpen(true); + } + + function handleCloseDetailsPanel() { + setSelectedStardustObjectsCategory(undefined); + } + + function handleMigrationDialogClose() { + setIsMigrationDialogOpen(false); + router.push('/'); + } + return (
-
+
+ {isMigrationDialogOpen && ( + + )} } /> <div className="flex flex-col gap-xs p-md--rs"> {MIGRATION_CARDS.map((card) => ( - <Card key={card.subtitle}> - <CardImage shape={ImageShape.SquareRounded}> - <card.icon /> - </CardImage> - <CardBody title={card.title} subtitle={card.subtitle} /> - </Card> + <MigrationDisplayCard + key={card.subtitle} + isPlaceholder={isPlaceholderData} + {...card} + /> ))} - <Button text="See All" type={ButtonType.Ghost} fullWidth /> + <Button + text="See All" + type={ButtonType.Ghost} + fullWidth + disabled={ + selectedStardustObjectsCategory === + StardustOutputMigrationStatus.Migratable || + !hasMigratableObjects + } + onClick={() => + setSelectedStardustObjectsCategory( + StardustOutputMigrationStatus.Migratable, + ) + } + /> </div> </Panel> @@ -158,20 +211,64 @@ function MigrationDashboardPage(): JSX.Element { <Title title="Time-locked Assets" /> <div className="flex flex-col gap-xs p-md--rs"> {TIMELOCKED_ASSETS_CARDS.map((card) => ( - <Card key={card.subtitle}> - <CardImage shape={ImageShape.SquareRounded}> - <card.icon /> - </CardImage> - <CardBody title={card.title} subtitle={card.subtitle} /> - </Card> + <MigrationDisplayCard + key={card.subtitle} + isPlaceholder={isPlaceholderData} + {...card} + /> ))} - <Button text="See All" type={ButtonType.Ghost} fullWidth /> + <Button + text="See All" + type={ButtonType.Ghost} + fullWidth + disabled={ + selectedStardustObjectsCategory === + StardustOutputMigrationStatus.TimeLocked || + !totalTimelockedObjects + } + onClick={() => + setSelectedStardustObjectsCategory( + StardustOutputMigrationStatus.TimeLocked, + ) + } + /> </div> </Panel> </div> + + <MigrationObjectsPanel + selectedObjects={selectedObjects} + onClose={handleCloseDetailsPanel} + isTimelocked={ + selectedStardustObjectsCategory === StardustOutputMigrationStatus.TimeLocked + } + /> </div> </div> ); } +interface MigrationDisplayCardProps { + title: string; + subtitle: string; + icon: React.ComponentType; + isPlaceholder?: boolean; +} + +function MigrationDisplayCard({ + title, + subtitle, + icon: Icon, + isPlaceholder, +}: MigrationDisplayCardProps): React.JSX.Element { + return ( + <Card> + <CardImage shape={ImageShape.SquareRounded}> + <Icon /> + </CardImage> + <CardBody title={isPlaceholder ? '--' : title} subtitle={subtitle} /> + </Card> + ); +} + export default MigrationDashboardPage; diff --git a/apps/wallet-dashboard/app/(protected)/staking/page.tsx b/apps/wallet-dashboard/app/(protected)/staking/page.tsx index e33b1253b95..87f69eccffa 100644 --- a/apps/wallet-dashboard/app/(protected)/staking/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/staking/page.tsx @@ -3,7 +3,6 @@ 'use client'; -import { StartStaking } from '@/components/staking-overview/StartStaking'; import { Button, ButtonSize, @@ -16,7 +15,15 @@ import { Title, TitleSize, } from '@iota/apps-ui-kit'; -import { StakeDialog, StakeDialogView } from '@/components'; +import { + StakeDialog, + StakeDialogView, + UnstakeDialog, + useUnstakeDialog, + UnstakeDialogView, + useStakeDialog, + StartStaking, +} from '@/components'; import { ExtendedDelegatedStake, formatDelegatedStake, @@ -32,8 +39,8 @@ import { useCurrentAccount, useIotaClient, useIotaClientQuery } from '@iota/dapp import { IotaSystemStateSummary } from '@iota/iota-sdk/client'; import { Info } from '@iota/ui-icons'; import { useMemo } from 'react'; -import { useStakeDialog } from '@/components/Dialogs/Staking/hooks/useStakeDialog'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard'; function StakingDashboardPage(): React.JSX.Element { const account = useCurrentAccount(); @@ -52,6 +59,14 @@ function StakingDashboardPage(): React.JSX.Element { handleCloseStakeDialog, handleNewStake, } = useStakeDialog(); + const { + isOpen: isUnstakeDialogOpen, + openUnstakeDialog, + defaultDialogProps, + handleClose: handleCloseUnstakeDialog, + setView: setUnstakeDialogView, + setTxDigest, + } = useUnstakeDialog(); const { data: delegatedStakeData, refetch: refetchDelegatedStakes } = useGetDelegatedStake({ address: account?.address || '', @@ -100,6 +115,34 @@ function StakingDashboardPage(): React.JSX.Element { .then(() => refetchDelegatedStakes()); } + function handleUnstakeClick() { + setStakeDialogView(undefined); + openUnstakeDialog(); + } + + function handleUnstakeDialogBack() { + setStakeDialogView(StakeDialogView.Details); + handleCloseUnstakeDialog(); + } + + function handleOnUnstakeBack(view: UnstakeDialogView): (() => void) | undefined { + if (view === UnstakeDialogView.Unstake) { + return handleUnstakeDialogBack; + } + } + + function handleOnUnstakeSuccess(tx: IotaSignAndExecuteTransactionOutput): void { + setUnstakeDialogView(UnstakeDialogView.TransactionDetails); + iotaClient + .waitForTransaction({ + digest: tx.digest, + }) + .then((tx) => { + refetchDelegatedStakes(); + setTxDigest(tx.digest); + }); + } + return ( <div className="flex justify-center"> <div className="w-3/4"> @@ -171,16 +214,27 @@ function StakingDashboardPage(): React.JSX.Element { </div> </div> </div> - <StakeDialog - stakedDetails={selectedStake} - onSuccess={handleOnStakeSuccess} - isOpen={isDialogStakeOpen} - handleClose={handleCloseStakeDialog} - view={stakeDialogView} - setView={setStakeDialogView} - selectedValidator={selectedValidator} - setSelectedValidator={setSelectedValidator} - /> + {isDialogStakeOpen && ( + <StakeDialog + stakedDetails={selectedStake} + onSuccess={handleOnStakeSuccess} + handleClose={handleCloseStakeDialog} + view={stakeDialogView} + setView={setStakeDialogView} + selectedValidator={selectedValidator} + setSelectedValidator={setSelectedValidator} + onUnstakeClick={handleUnstakeClick} + /> + )} + + {isUnstakeDialogOpen && selectedStake && ( + <UnstakeDialog + extendedStake={selectedStake} + onBack={handleOnUnstakeBack} + onSuccess={handleOnUnstakeSuccess} + {...defaultDialogProps} + /> + )} </Panel> ) : ( <div className="flex h-[270px] p-lg"> diff --git a/apps/wallet-dashboard/app/(protected)/vesting/page.tsx b/apps/wallet-dashboard/app/(protected)/vesting/page.tsx index b925d57e97c..e29d94a7fef 100644 --- a/apps/wallet-dashboard/app/(protected)/vesting/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/vesting/page.tsx @@ -6,22 +6,15 @@ import { Banner, StakeDialog, - StakeDialogView, - TimelockedUnstakePopup, useStakeDialog, VestingScheduleDialog, + UnstakeDialog, + StakeDialogView, } from '@/components'; -import { useGetCurrentEpochStartTimestamp, useNotifications, usePopups } from '@/hooks'; -import { - buildSupplyIncreaseVestingSchedule, - formatDelegatedTimelockedStake, - getLatestOrEarliestSupplyIncreaseVestingPayout, - getVestingOverview, - groupTimelockedStakedObjects, - isTimelockedUnlockable, - mapTimelockObjects, - TimelockedStakedObjectsGrouped, -} from '@/lib/utils'; +import { UnstakeDialogView } from '@/components/Dialogs/unstake/enums'; +import { useUnstakeDialog } from '@/components/Dialogs/unstake/hooks'; +import { useGetSupplyIncreaseVestingObjects, useNotifications } from '@/hooks'; +import { groupTimelockedStakedObjects, TimelockedStakedObjectsGrouped } from '@/lib/utils'; import { NotificationType } from '@/stores/notificationStore'; import { useFeature } from '@growthbook/growthbook-react'; import { @@ -44,13 +37,9 @@ import { } from '@iota/apps-ui-kit'; import { Theme, - TIMELOCK_IOTA_TYPE, useFormatCoin, useGetActiveValidatorsInfo, - useGetAllOwnedObjects, - useGetTimelockedStakedObjects, useTheme, - useUnlockTimelockedObjectsTransaction, useCountdownByTimestamp, Feature, } from '@iota/core'; @@ -63,29 +52,22 @@ import { import { IotaValidatorSummary } from '@iota/iota-sdk/client'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { Calendar, StarHex } from '@iota/ui-icons'; -import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { StakedTimelockObject } from '@/components'; +import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard'; -function VestingDashboardPage(): JSX.Element { +export default function VestingDashboardPage(): JSX.Element { + const [timelockedObjectsToUnstake, setTimelockedObjectsToUnstake] = + useState<TimelockedStakedObjectsGrouped | null>(null); const account = useCurrentAccount(); - const queryClient = useQueryClient(); + const address = account?.address || ''; const iotaClient = useIotaClient(); const router = useRouter(); const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); const [isVestingScheduleDialogOpen, setIsVestingScheduleDialogOpen] = useState(false); const { addNotification } = useNotifications(); - const { openPopup, closePopup } = usePopups(); - const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); const { data: activeValidators } = useGetActiveValidatorsInfo(); - const { data: timelockedObjects } = useGetAllOwnedObjects(account?.address || '', { - StructType: TIMELOCK_IOTA_TYPE, - }); - - const { data: timelockedStakedObjects, isLoading: istimelockedStakedObjectsLoading } = - useGetTimelockedStakedObjects(account?.address || ''); - const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); const { theme } = useTheme(); @@ -96,16 +78,19 @@ function VestingDashboardPage(): JSX.Element { const supplyIncreaseVestingEnabled = useFeature<boolean>(Feature.SupplyIncreaseVesting).value; - const timelockedMapped = mapTimelockObjects(timelockedObjects || []); - const timelockedstakedMapped = formatDelegatedTimelockedStake(timelockedStakedObjects || []); + const { + nextPayout, + supplyIncreaseVestingPortfolio, + supplyIncreaseVestingSchedule, + supplyIncreaseVestingMapped, + supplyIncreaseVestingStakedMapped, + isTimelockedStakedObjectsLoading, + unlockAllSupplyIncreaseVesting, + refreshStakeList, + } = useGetSupplyIncreaseVestingObjects(address); const timelockedStakedObjectsGrouped: TimelockedStakedObjectsGrouped[] = - groupTimelockedStakedObjects(timelockedstakedMapped || []); - - const vestingSchedule = getVestingOverview( - [...timelockedMapped, ...timelockedstakedMapped], - Number(currentEpochMs), - ); + groupTimelockedStakedObjects(supplyIncreaseVestingStakedMapped || []); const { isDialogStakeOpen, @@ -118,37 +103,30 @@ function VestingDashboardPage(): JSX.Element { handleNewStake, } = useStakeDialog(); - const nextPayout = getLatestOrEarliestSupplyIncreaseVestingPayout( - [...timelockedMapped, ...timelockedstakedMapped], - Number(currentEpochMs), - false, - ); - - const lastPayout = getLatestOrEarliestSupplyIncreaseVestingPayout( - [...timelockedMapped, ...timelockedstakedMapped], - Number(currentEpochMs), - true, - ); - - const vestingPortfolio = - lastPayout && buildSupplyIncreaseVestingSchedule(lastPayout, Number(currentEpochMs)); + const { + isOpen: isUnstakeDialogOpen, + openUnstakeDialog, + defaultDialogProps, + setTxDigest, + setView: setUnstakeDialogView, + } = useUnstakeDialog(); const formattedLastPayoutExpirationTime = useCountdownByTimestamp( Number(nextPayout?.expirationTimestampMs), ); const [formattedTotalVested, vestedSymbol] = useFormatCoin( - vestingSchedule.totalVested, + supplyIncreaseVestingSchedule.totalVested, IOTA_TYPE_ARG, ); const [formattedTotalLocked, lockedSymbol] = useFormatCoin( - vestingSchedule.totalLocked, + supplyIncreaseVestingSchedule.totalLocked, IOTA_TYPE_ARG, ); const [formattedAvailableClaiming, availableClaimingSymbol] = useFormatCoin( - vestingSchedule.availableClaiming, + supplyIncreaseVestingSchedule.availableClaiming, IOTA_TYPE_ARG, ); @@ -164,54 +142,33 @@ function VestingDashboardPage(): JSX.Element { } const [totalStakedFormatted, totalStakedSymbol] = useFormatCoin( - vestingSchedule.totalStaked, + supplyIncreaseVestingSchedule.totalStaked, IOTA_TYPE_ARG, ); const [totalEarnedFormatted, totalEarnedSymbol] = useFormatCoin( - vestingSchedule.totalEarned, + supplyIncreaseVestingSchedule.totalEarned, IOTA_TYPE_ARG, ); - const unlockedTimelockedObjects = timelockedMapped?.filter((timelockedObject) => - isTimelockedUnlockable(timelockedObject, Number(currentEpochMs)), - ); - const unlockedTimelockedObjectIds: string[] = - unlockedTimelockedObjects.map((timelocked) => timelocked.id.id) || []; - const { data: unlockAllTimelockedObjects } = useUnlockTimelockedObjectsTransaction( - account?.address || '', - unlockedTimelockedObjectIds, - ); - function handleOnSuccess(digest: string): void { + setTimelockedObjectsToUnstake(null); + iotaClient .waitForTransaction({ digest, }) - .then(() => { - queryClient.invalidateQueries({ - queryKey: ['get-timelocked-staked-objects', account?.address], - }); - queryClient.invalidateQueries({ - queryKey: [ - 'get-all-owned-objects', - account?.address, - { - StructType: TIMELOCK_IOTA_TYPE, - }, - ], - }); - }); + .then(refreshStakeList); } const handleCollect = () => { - if (!unlockAllTimelockedObjects?.transactionBlock) { + if (!unlockAllSupplyIncreaseVesting?.transactionBlock) { addNotification('Failed to create a Transaction', NotificationType.Error); return; } signAndExecuteTransaction( { - transaction: unlockAllTimelockedObjects.transactionBlock, + transaction: unlockAllSupplyIncreaseVesting.transactionBlock, }, { onSuccess: (tx) => { @@ -228,34 +185,29 @@ function VestingDashboardPage(): JSX.Element { }; function handleUnstake(delegatedTimelockedStake: TimelockedStakedObjectsGrouped): void { - const validatorInfo = getValidatorByAddress(delegatedTimelockedStake.validatorAddress); - if (!account || !validatorInfo) { - addNotification('Cannot create transaction', NotificationType.Error); - return; - } - - openPopup( - <TimelockedUnstakePopup - accountAddress={account.address} - delegatedStake={delegatedTimelockedStake} - validatorInfo={validatorInfo} - closePopup={closePopup} - onSuccess={handleOnSuccess} - />, - ); + setTimelockedObjectsToUnstake(delegatedTimelockedStake); + openUnstakeDialog(UnstakeDialogView.TimelockedUnstake); } - function openReceiveTokenPopup(): void { + function openReceiveTokenDialog(): void { setIsVestingScheduleDialogOpen(true); } + function handleOnSuccessUnstake(tx: IotaSignAndExecuteTransactionOutput): void { + setUnstakeDialogView(UnstakeDialogView.TransactionDetails); + iotaClient.waitForTransaction({ digest: tx.digest }).then((tx) => { + refreshStakeList(); + setTxDigest(tx.digest); + }); + } + useEffect(() => { if (!supplyIncreaseVestingEnabled) { router.push('/'); } }, [router, supplyIncreaseVestingEnabled]); - if (istimelockedStakedObjectsLoading) { + if (isTimelockedStakedObjectsLoading) { return ( <div className="flex w-full max-w-4xl items-start justify-center justify-self-center"> <LoadingIndicator /> @@ -264,152 +216,177 @@ function VestingDashboardPage(): JSX.Element { } return ( - <div className="flex w-full max-w-4xl flex-col items-stretch justify-center gap-lg justify-self-center md:flex-row"> - <div className="flex w-full flex-col gap-lg md:w-1/2"> - <Panel> - <Title title="Vesting" size={TitleSize.Medium} /> - <div className="flex flex-col gap-md p-lg pt-sm"> - <div className="flex h-24 flex-row gap-4"> - <DisplayStats - label="Total Vested" - value={formattedTotalVested} - supportingLabel={vestedSymbol} - /> - <DisplayStats - label="Total Locked" - value={formattedTotalLocked} - supportingLabel={lockedSymbol} - tooltipText="Total amount of IOTA that is still locked in your account." - tooltipPosition={TooltipPosition.Right} - /> - </div> - <Card type={CardType.Outlined}> - <CardImage type={ImageType.BgSolid} shape={ImageShape.SquareRounded}> - <StarHex className="h-5 w-5 text-primary-30 dark:text-primary-80" /> - </CardImage> - <CardBody - title={`${formattedAvailableClaiming} ${availableClaimingSymbol}`} - subtitle="Available Rewards" - /> - <CardAction - type={CardActionType.Button} - onClick={handleCollect} - title="Collect" - buttonType={ButtonType.Primary} - buttonDisabled={ - !vestingSchedule.availableClaiming || - vestingSchedule.availableClaiming === 0n - } - /> - </Card> - <Card type={CardType.Outlined}> - <CardImage type={ImageType.BgSolid} shape={ImageShape.SquareRounded}> - <Calendar className="h-5 w-5 text-primary-30 dark:text-primary-80" /> - </CardImage> - <CardBody - title={`${formattedNextPayout} ${nextPayoutSymbol}`} - subtitle={`Next payout ${ - nextPayout?.expirationTimestampMs - ? formattedLastPayoutExpirationTime - : '' - }`} - /> - <CardAction - type={CardActionType.Button} - onClick={openReceiveTokenPopup} - title="See All" - buttonType={ButtonType.Secondary} - buttonDisabled={!vestingPortfolio} - /> - </Card> - {vestingPortfolio && ( - <VestingScheduleDialog - open={isVestingScheduleDialogOpen} - setOpen={setIsVestingScheduleDialogOpen} - vestingPortfolio={vestingPortfolio} - /> - )} - </div> - </Panel> - - {timelockedstakedMapped.length === 0 ? ( - <Banner - videoSrc={videoSrc} - title="Stake Vested Tokens" - subtitle="Earn Rewards" - onButtonClick={() => handleNewStake()} - buttonText="Stake" - /> - ) : null} - </div> - - {timelockedstakedMapped.length !== 0 ? ( - <div className="flex w-full md:w-1/2"> + <> + <div className="flex w-full max-w-4xl flex-col items-stretch justify-center gap-lg justify-self-center md:flex-row"> + <div className="flex w-full flex-col gap-lg md:w-1/2"> <Panel> - <Title - title="Staked Vesting" - trailingElement={ - <Button - type={ButtonType.Primary} - text="Stake" - onClick={() => { - setStakeDialogView(StakeDialogView.SelectValidator); - }} - /> - } - /> - - <div className="flex flex-col px-lg py-sm"> - <div className="flex flex-row gap-md"> + <Title title="Vesting" size={TitleSize.Medium} /> + <div className="flex flex-col gap-md p-lg pt-sm"> + <div className="flex h-24 flex-row gap-4"> <DisplayStats - label="Your stake" - value={`${totalStakedFormatted} ${totalStakedSymbol}`} + label="Total Vested" + value={formattedTotalVested} + supportingLabel={vestedSymbol} /> <DisplayStats - label="Earned" - value={`${totalEarnedFormatted} ${totalEarnedSymbol}`} + label="Total Locked" + value={formattedTotalLocked} + supportingLabel={lockedSymbol} + tooltipText="Total amount of IOTA that is still locked in your account." + tooltipPosition={TooltipPosition.Right} /> </div> - </div> - <div className="flex flex-col px-lg py-sm"> - <div className="flex w-full flex-col items-center justify-center space-y-4 pt-4"> - {system && - timelockedStakedObjectsGrouped?.map( - (timelockedStakedObject) => { - return ( - <StakedTimelockObject - key={ - timelockedStakedObject.validatorAddress + - timelockedStakedObject.stakeRequestEpoch + - timelockedStakedObject.label - } - getValidatorByAddress={getValidatorByAddress} - timelockedStakedObject={timelockedStakedObject} - handleUnstake={handleUnstake} - currentEpoch={Number(system.epoch)} - /> - ); - }, - )} - </div> + <Card type={CardType.Outlined}> + <CardImage + type={ImageType.BgSolid} + shape={ImageShape.SquareRounded} + > + <StarHex className="h-5 w-5 text-primary-30 dark:text-primary-80" /> + </CardImage> + <CardBody + title={`${formattedAvailableClaiming} ${availableClaimingSymbol}`} + subtitle="Available Rewards" + /> + <CardAction + type={CardActionType.Button} + onClick={handleCollect} + title="Collect" + buttonType={ButtonType.Primary} + buttonDisabled={ + !supplyIncreaseVestingSchedule.availableClaiming || + supplyIncreaseVestingSchedule.availableClaiming === 0n + } + /> + </Card> + <Card type={CardType.Outlined}> + <CardImage + type={ImageType.BgSolid} + shape={ImageShape.SquareRounded} + > + <Calendar className="h-5 w-5 text-primary-30 dark:text-primary-80" /> + </CardImage> + <CardBody + title={`${formattedNextPayout} ${nextPayoutSymbol}`} + subtitle={`Next payout ${ + nextPayout?.expirationTimestampMs + ? formattedLastPayoutExpirationTime + : '' + }`} + /> + <CardAction + type={CardActionType.Button} + onClick={openReceiveTokenDialog} + title="See All" + buttonType={ButtonType.Secondary} + buttonDisabled={!supplyIncreaseVestingPortfolio} + /> + </Card> + {supplyIncreaseVestingPortfolio && ( + <VestingScheduleDialog + open={isVestingScheduleDialogOpen} + setOpen={setIsVestingScheduleDialogOpen} + vestingPortfolio={supplyIncreaseVestingPortfolio} + /> + )} </div> </Panel> + + {supplyIncreaseVestingMapped.length === 0 ? ( + <Banner + videoSrc={videoSrc} + title="Stake Vested Tokens" + subtitle="Earn Rewards" + onButtonClick={() => handleNewStake()} + buttonText="Stake" + /> + ) : null} </div> - ) : null} - <StakeDialog - isTimelockedStaking - stakedDetails={selectedStake} - onSuccess={handleOnSuccess} - isOpen={isDialogStakeOpen} - handleClose={handleCloseStakeDialog} - view={stakeDialogView} - setView={setStakeDialogView} - selectedValidator={selectedValidator} - setSelectedValidator={setSelectedValidator} - maxStakableTimelockedAmount={BigInt(vestingSchedule.availableStaking)} - /> - </div> + + {supplyIncreaseVestingMapped.length !== 0 ? ( + <div className="flex w-full md:w-1/2"> + <Panel> + <Title + title="Staked Vesting" + trailingElement={ + <Button + type={ButtonType.Primary} + text="Stake" + disabled={ + supplyIncreaseVestingSchedule.availableStaking === 0n + } + onClick={() => { + setStakeDialogView(StakeDialogView.SelectValidator); + }} + /> + } + /> + + <div className="flex flex-col px-lg py-sm"> + <div className="flex flex-row gap-md"> + <DisplayStats + label="Your stake" + value={`${totalStakedFormatted} ${totalStakedSymbol}`} + /> + <DisplayStats + label="Earned" + value={`${totalEarnedFormatted} ${totalEarnedSymbol}`} + /> + </div> + </div> + <div className="flex flex-col px-lg py-sm"> + <div className="flex w-full flex-col items-center justify-center space-y-4 pt-4"> + {system && + timelockedStakedObjectsGrouped?.map( + (timelockedStakedObject) => { + return ( + <StakedTimelockObject + key={ + timelockedStakedObject.validatorAddress + + timelockedStakedObject.stakeRequestEpoch + + timelockedStakedObject.label + } + getValidatorByAddress={ + getValidatorByAddress + } + timelockedStakedObject={ + timelockedStakedObject + } + handleUnstake={handleUnstake} + currentEpoch={Number(system.epoch)} + /> + ); + }, + )} + </div> + </div> + </Panel> + </div> + ) : null} + + {isDialogStakeOpen && ( + <StakeDialog + isTimelockedStaking + stakedDetails={selectedStake} + onSuccess={handleOnSuccess} + handleClose={handleCloseStakeDialog} + view={stakeDialogView} + setView={setStakeDialogView} + selectedValidator={selectedValidator} + setSelectedValidator={setSelectedValidator} + maxStakableTimelockedAmount={BigInt( + supplyIncreaseVestingSchedule.availableStaking, + )} + /> + )} + + {isUnstakeDialogOpen && timelockedObjectsToUnstake && ( + <UnstakeDialog + groupedTimelockedObjects={timelockedObjectsToUnstake} + onSuccess={handleOnSuccessUnstake} + {...defaultDialogProps} + /> + )} + </div> + </> ); } - -export default VestingDashboardPage; diff --git a/apps/wallet-dashboard/app/globals.css b/apps/wallet-dashboard/app/globals.css index 04c6811f389..69ca23f41f2 100644 --- a/apps/wallet-dashboard/app/globals.css +++ b/apps/wallet-dashboard/app/globals.css @@ -24,7 +24,6 @@ body { 'balance' 'staking' 'coins' - 'vesting' 'activity'; & @@ -36,13 +35,29 @@ body { height: 200px; } } - .home-page-grid-container:has(.with-migration) { + .home-page-grid-container:has(.with-vesting):not(:has(.with-migration)) { + grid-template-areas: + 'balance' + 'staking' + 'vesting' + 'coins' + 'activity'; + } + .home-page-grid-container:has(.with-migration):not(:has(.with-vesting)) { grid-template-areas: 'balance' 'staking' 'migration' 'coins' + 'activity'; + } + .home-page-grid-container:has(.with-migration):has(.with-vesting) { + grid-template-areas: + 'balance' + 'staking' + 'migration' 'vesting' + 'coins' 'activity'; } @@ -53,28 +68,56 @@ body { 'balance balance' 'staking staking' 'coins coins' + 'activity activity'; + } + .home-page-grid-container:has(.with-vesting):not(:has(.with-migration)) { + grid-template-areas: + 'balance balance' + 'staking staking' 'vesting vesting' + 'coins coins' 'activity activity'; } - .home-page-grid-container:has(.with-migration) { + .home-page-grid-container:has(.with-migration):not(:has(.with-vesting)) { grid-template-areas: 'balance balance' 'staking migration' 'coins coins' + 'activity activity'; + } + .home-page-grid-container:has(.with-migration):has(.with-vesting) { + grid-template-areas: + 'balance balance' + 'staking migration' 'vesting vesting' + 'coins coins' 'activity activity'; } } @screen md { .home-page-grid-container { + min-height: 700px; + height: calc(100vh - 140px); @apply grid-cols-3; + grid-template-areas: + 'balance staking staking' + 'coins activity activity'; + } + .home-page-grid-container:has(.with-vesting):not(:has(.with-migration)) { grid-template-areas: 'balance staking staking' 'coins vesting vesting' 'coins activity activity'; } - .home-page-grid-container:has(.with-migration) { + + .home-page-grid-container:has(.with-migration):not(:has(.with-vesting)) { + grid-template-areas: + 'balance staking migration' + 'coins activity activity'; + } + + .home-page-grid-container:has(.with-migration):has(.with-vesting) { grid-template-areas: 'balance staking migration' 'coins vesting vesting' diff --git a/apps/wallet-dashboard/app/page.tsx b/apps/wallet-dashboard/app/page.tsx index 8901eec400a..3392c231d31 100644 --- a/apps/wallet-dashboard/app/page.tsx +++ b/apps/wallet-dashboard/app/page.tsx @@ -7,7 +7,7 @@ import { ConnectButton, useCurrentWallet, useAutoConnectWallet } from '@iota/dap import { redirect } from 'next/navigation'; import { IotaLogoWeb } from '@iota/ui-icons'; import { HOMEPAGE_ROUTE } from '@/lib/constants/routes.constants'; -import { Theme, useTheme } from '@iota/core'; +import { Theme, ThemeSwitcher, useTheme } from '@iota/core'; import { LoadingIndicator } from '@iota/apps-ui-kit'; function HomeDashboardPage(): JSX.Element { @@ -44,7 +44,10 @@ function HomeDashboardPage(): JSX.Element { disableRemotePlayback ></video> </div> - <div className="flex h-full w-full flex-col items-center justify-between p-md sm:p-2xl"> + <div className="relative flex h-full w-full flex-col items-center justify-between p-md sm:p-2xl"> + <div className="absolute right-2 top-2 sm:right-8 sm:top-8"> + <ThemeSwitcher /> + </div> <IotaLogoWeb width={130} height={32} /> <div className="flex max-w-sm flex-col items-center gap-8 text-center"> <div className="flex flex-col items-center gap-4"> @@ -60,8 +63,10 @@ function HomeDashboardPage(): JSX.Element { <ConnectButton connectText="Connect" /> </div> </div> - <div className="text-body-lg text-neutral-60"> + <div className="text-center text-body-lg text-neutral-60"> © IOTA Foundation {CURRENT_YEAR} + <br /> + {process.env.NEXT_PUBLIC_DASHBOARD_REV} </div> </div> </> diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index ec3d8d38d11..c71390a33aa 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -4,17 +4,20 @@ import React, { useState } from 'react'; import { Dialog } from '@iota/apps-ui-kit'; import { FormikProvider, useFormik } from 'formik'; -import { useCurrentAccount } from '@iota/dapp-kit'; +import { useIotaClient, useCurrentAccount } from '@iota/dapp-kit'; import { createNftSendValidationSchema } from '@iota/core'; import { DetailsView, SendView } from './views'; import { IotaObjectData } from '@iota/iota-sdk/client'; import { AssetsDialogView } from './constants'; import { useCreateSendAssetTransaction, useNotifications } from '@/hooks'; import { NotificationType } from '@/stores/notificationStore'; +import { TransactionDetailsView } from '../SendToken'; +import { DialogLayout } from '../layout'; interface AssetsDialogProps { onClose: () => void; asset: IotaObjectData; + refetchAssets: () => void; } interface FormValues { @@ -25,12 +28,14 @@ const INITIAL_VALUES: FormValues = { to: '', }; -export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element { +export function AssetDialog({ onClose, asset, refetchAssets }: AssetsDialogProps): JSX.Element { const [view, setView] = useState<AssetsDialogView>(AssetsDialogView.Details); const account = useCurrentAccount(); + const [digest, setDigest] = useState<string>(''); const activeAddress = account?.address ?? ''; const objectId = asset?.objectId ?? ''; const { addNotification } = useNotifications(); + const iotaClient = useIotaClient(); const validationSchema = createNftSendValidationSchema(activeAddress, objectId); const { mutation: sendAsset } = useCreateSendAssetTransaction(objectId); @@ -44,10 +49,16 @@ export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element async function onSubmit(values: FormValues) { try { - await sendAsset.mutateAsync(values.to); + const executed = await sendAsset.mutateAsync(values.to); + + const tx = await iotaClient.waitForTransaction({ + digest: executed.digest, + }); + + setDigest(tx.digest); + refetchAssets(); addNotification('Transfer transaction successful', NotificationType.Success); - onClose(); - setView(AssetsDialogView.Details); + setView(AssetsDialogView.TransactionDetails); } catch { addNotification('Transfer transaction failed', NotificationType.Error); } @@ -66,16 +77,26 @@ export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element } return ( <Dialog open onOpenChange={onOpenChange}> - <FormikProvider value={formik}> + <DialogLayout> <> {view === AssetsDialogView.Details && ( <DetailsView asset={asset} onClose={onOpenChange} onSend={onDetailsSend} /> )} {view === AssetsDialogView.Send && ( - <SendView asset={asset} onClose={onOpenChange} onBack={onSendViewBack} /> + <FormikProvider value={formik}> + <SendView + asset={asset} + onClose={onOpenChange} + onBack={onSendViewBack} + /> + </FormikProvider> )} + + {view === AssetsDialogView.TransactionDetails && !!digest ? ( + <TransactionDetailsView digest={digest} onClose={onOpenChange} /> + ) : null} </> - </FormikProvider> + </DialogLayout> </Dialog> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts b/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts index 88cb34c15b1..242da33c2c3 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts @@ -4,4 +4,5 @@ export enum AssetsDialogView { Details = 'Details', Send = 'Send', + TransactionDetails = 'TransactionDetails', } diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx index 3929ba0062f..8520d62a484 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -13,7 +13,7 @@ import { } from '@iota/apps-ui-kit'; import Link from 'next/link'; import { formatAddress } from '@iota/iota-sdk/utils'; -import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout'; +import { DialogLayoutBody, DialogLayoutFooter } from '../../layout'; import { IotaObjectData } from '@iota/iota-sdk/client'; import { ExplorerLink } from '@/components/ExplorerLink'; import { useCurrentAccount } from '@iota/dapp-kit'; @@ -55,7 +55,7 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { } return ( - <DialogLayout> + <> <Header title="Asset" onClose={onClose} titleCentered /> <DialogLayoutBody> <div className="flex w-full flex-col items-center justify-center gap-xs"> @@ -195,6 +195,6 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { )} </div> </DialogLayoutFooter> - </DialogLayout> + </> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx index b5757582c7b..3d0350478ac 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { AddressInput, useNftDetails } from '@iota/core'; import { useFormikContext } from 'formik'; -import { DialogLayout, DialogLayoutFooter, DialogLayoutBody } from '../../layout'; +import { DialogLayoutFooter, DialogLayoutBody } from '../../layout'; import { Button, ButtonHtmlType, @@ -33,7 +33,7 @@ export function SendView({ asset, onClose, onBack }: SendViewProps) { const { nftName, nftImageUrl } = useNftDetails(objectId, senderAddress); return ( - <DialogLayout> + <> <Header title="Send asset" onClose={onClose} titleCentered onBack={onBack} /> <DialogLayoutBody> <div className="flex w-full flex-col items-center justify-center gap-xs"> @@ -65,6 +65,6 @@ export function SendView({ asset, onClose, onBack }: SendViewProps) { onClick={submitForm} /> </DialogLayoutFooter> - </DialogLayout> + </> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx b/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx index da417f66147..a33f018a09c 100644 --- a/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/SendToken/SendTokenDialog.tsx @@ -2,18 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import React, { useState } from 'react'; -import { EnterValuesFormView, ReviewValuesFormView } from './views'; +import { EnterValuesFormView, ReviewValuesFormView, TransactionDetailsView } from './views'; import { CoinBalance } from '@iota/iota-sdk/client'; import { useSendCoinTransaction, useNotifications } from '@/hooks'; -import { useSignAndExecuteTransaction } from '@iota/dapp-kit'; import { NotificationType } from '@/stores/notificationStore'; import { CoinFormat, useFormatCoin, useGetAllCoins } from '@iota/core'; -import { Dialog, DialogBody, DialogContent, DialogPosition, Header } from '@iota/apps-ui-kit'; +import { Dialog, DialogContent, DialogPosition } from '@iota/apps-ui-kit'; import { FormDataValues } from './interfaces'; import { INITIAL_VALUES } from './constants'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { useTransferTransactionMutation } from '@/hooks'; -interface SendCoinPopupProps { +interface SendCoinDialogProps { coin: CoinBalance; activeAddress: string; setOpen: (bool: boolean) => void; @@ -23,22 +23,21 @@ interface SendCoinPopupProps { enum FormStep { EnterValues, ReviewValues, + TransactionDetails, } function SendTokenDialogBody({ coin, activeAddress, setOpen, -}: SendCoinPopupProps): React.JSX.Element { +}: SendCoinDialogProps): React.JSX.Element { const [step, setStep] = useState<FormStep>(FormStep.EnterValues); const [selectedCoin, setSelectedCoin] = useState<CoinBalance>(coin); const [formData, setFormData] = useState<FormDataValues>(INITIAL_VALUES); const [fullAmount] = useFormatCoin(formData.amount, selectedCoin.coinType, CoinFormat.FULL); - const { addNotification } = useNotifications(); - const { data: coinsData } = useGetAllCoins(selectedCoin.coinType, activeAddress); - const { mutateAsync: signAndExecuteTransaction, isPending } = useSignAndExecuteTransaction(); + const { addNotification } = useNotifications(); const isPayAllIota = selectedCoin.totalBalance === formData.amount && selectedCoin.coinType === IOTA_TYPE_ARG; @@ -51,25 +50,28 @@ function SendTokenDialogBody({ isPayAllIota, ); - function handleTransfer() { + const { + mutate: transfer, + data, + isPending: isLoadingTransfer, + } = useTransferTransactionMutation(); + + async function handleTransfer() { if (!transaction) { addNotification('There was an error with the transaction', NotificationType.Error); return; - } else { - signAndExecuteTransaction({ - transaction, - }) - .then(() => { - setOpen(false); - addNotification('Transfer transaction has been sent'); - }) - .catch(handleTransactionError); } - } - function handleTransactionError() { - setOpen(false); - addNotification('There was an error with the transaction', NotificationType.Error); + transfer(transaction, { + onSuccess: () => { + setStep(FormStep.TransactionDetails); + addNotification('Transfer transaction has been sent', NotificationType.Success); + }, + onError: () => { + setOpen(false); + addNotification('Transfer transaction failed', NotificationType.Error); + }, + }); } function onNext(): void { @@ -87,40 +89,43 @@ function SendTokenDialogBody({ return ( <> - <Header - title={step === FormStep.EnterValues ? 'Send' : 'Review & Send'} - onClose={() => setOpen(false)} - onBack={step === FormStep.ReviewValues ? onBack : undefined} - /> - <div className="h-full [&>div]:h-full"> - <DialogBody> - {step === FormStep.EnterValues && ( - <EnterValuesFormView - coin={selectedCoin} - activeAddress={activeAddress} - setSelectedCoin={setSelectedCoin} - onNext={onNext} - setFormData={setFormData} - initialFormValues={formData} - /> - )} - {step === FormStep.ReviewValues && ( - <ReviewValuesFormView - formData={formData} - executeTransfer={handleTransfer} - senderAddress={activeAddress} - isPending={isPending} - coinType={selectedCoin.coinType} - isPayAllIota={isPayAllIota} - /> - )} - </DialogBody> - </div> + {step === FormStep.EnterValues && ( + <EnterValuesFormView + coin={selectedCoin} + activeAddress={activeAddress} + setSelectedCoin={setSelectedCoin} + onNext={onNext} + onClose={() => setOpen(false)} + setFormData={setFormData} + initialFormValues={formData} + /> + )} + {step === FormStep.ReviewValues && ( + <ReviewValuesFormView + formData={formData} + executeTransfer={handleTransfer} + senderAddress={activeAddress} + isPending={isLoadingTransfer} + coinType={selectedCoin.coinType} + isPayAllIota={isPayAllIota} + onClose={() => setOpen(false)} + onBack={onBack} + /> + )} + {step === FormStep.TransactionDetails && data?.digest && ( + <TransactionDetailsView + digest={data.digest} + onClose={() => { + setOpen(false); + setStep(FormStep.EnterValues); + }} + /> + )} </> ); } -export function SendTokenDialog(props: SendCoinPopupProps): React.JSX.Element { +export function SendTokenDialog(props: SendCoinDialogProps) { return ( <Dialog open={props.open} onOpenChange={props.setOpen}> <DialogContent containerId="overlay-portal-container" position={DialogPosition.Right}> diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/views/EnterValuesFormView.tsx b/apps/wallet-dashboard/components/Dialogs/SendToken/views/EnterValuesFormView.tsx index f38c0d95cfa..f33ddbc8fef 100644 --- a/apps/wallet-dashboard/components/Dialogs/SendToken/views/EnterValuesFormView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/SendToken/views/EnterValuesFormView.tsx @@ -24,14 +24,16 @@ import { Button, InfoBoxStyle, LoadingIndicator, + Header, } from '@iota/apps-ui-kit'; import { useIotaClientQuery } from '@iota/dapp-kit'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { Form, Formik, FormikProps } from 'formik'; +import { Form, FormikProvider, useFormik, useFormikContext } from 'formik'; import { Exclamation } from '@iota/ui-icons'; import { UseQueryResult } from '@tanstack/react-query'; import { FormDataValues } from '../interfaces'; import { INITIAL_VALUES } from '../constants'; +import { DialogLayoutBody, DialogLayoutFooter } from '../../layout'; interface EnterValuesFormProps { coin: CoinBalance; @@ -40,9 +42,10 @@ interface EnterValuesFormProps { setFormData: React.Dispatch<React.SetStateAction<FormDataValues>>; setSelectedCoin: React.Dispatch<React.SetStateAction<CoinBalance>>; onNext: () => void; + onClose: () => void; } -interface FormInputsProps extends FormikProps<FormDataValues> { +interface FormInputsProps { coinType: string; coinDecimals: number; coinBalance: bigint; @@ -52,6 +55,9 @@ interface FormInputsProps extends FormikProps<FormDataValues> { activeAddress: string; coins: CoinStruct[]; queryResult: UseQueryResult<CoinMetadata | null>; + formattedAmount: bigint; + hasEnoughBalance: boolean; + isPayAllIota: boolean; } function totalBalance(coins: CoinStruct[]): bigint { @@ -62,29 +68,18 @@ function getBalanceFromCoinStruct(coin: CoinStruct): bigint { } function FormInputs({ - isValid, - isSubmitting, - setFieldValue, - values, - submitForm, - coinType, coinDecimals, coinBalance, - iotaBalance, formattedTokenBalance, symbol, activeAddress, coins, queryResult, + formattedAmount, + hasEnoughBalance, + isPayAllIota, }: FormInputsProps): React.JSX.Element { - const formattedAmount = parseAmount(values.amount, coinDecimals); - const isPayAllIota = formattedAmount === coinBalance && coinType === IOTA_TYPE_ARG; - - const hasEnoughBalance = - isPayAllIota || - iotaBalance > - BigInt(values.gasBudgetEst ?? '0') + - (coinType === IOTA_TYPE_ARG ? formattedAmount : 0n); + const { setFieldValue, values } = useFormikContext<FormDataValues>(); async function onMaxTokenButtonClick() { await setFieldValue('amount', formattedTokenBalance); @@ -94,46 +89,31 @@ function FormInputs({ formattedAmount === coinBalance || queryResult.isPending || !coinBalance; return ( - <div className="flex h-full w-full flex-col"> - <Form autoComplete="off" noValidate className="flex-1"> - <div className="flex h-full w-full flex-col gap-md"> - {!hasEnoughBalance && ( - <InfoBox - type={InfoBoxType.Error} - supportingText="Insufficient IOTA to cover transaction" - style={InfoBoxStyle.Elevated} - icon={<Exclamation />} - /> - )} - - <SendTokenFormInput - name="amount" - to={values.to} - symbol={symbol} - coins={coins} - coinDecimals={coinDecimals} - activeAddress={activeAddress} - onActionClick={onMaxTokenButtonClick} - isMaxActionDisabled={isMaxActionDisabled} - isPayAllIota={isPayAllIota} + <Form autoComplete="off" noValidate className="flex-1"> + <div className="flex h-full w-full flex-col gap-md"> + {!hasEnoughBalance && ( + <InfoBox + type={InfoBoxType.Error} + supportingText="Insufficient IOTA to cover transaction" + style={InfoBoxStyle.Elevated} + icon={<Exclamation />} /> - <AddressInput name="to" placeholder="Enter Address" /> - </div> - </Form> + )} - <div className="pt-xs"> - <Button - onClick={submitForm} - htmlType={ButtonHtmlType.Submit} - type={ButtonType.Primary} - disabled={ - !isValid || isSubmitting || !hasEnoughBalance || values.gasBudgetEst === '' - } - text="Review" - fullWidth + <SendTokenFormInput + name="amount" + to={values.to} + symbol={symbol} + coins={coins} + coinDecimals={coinDecimals} + activeAddress={activeAddress} + onActionClick={onMaxTokenButtonClick} + isMaxActionDisabled={isMaxActionDisabled} + isPayAllIota={isPayAllIota} /> + <AddressInput name="to" placeholder="Enter Address" /> </div> - </div> + </Form> ); } @@ -144,6 +124,7 @@ export function EnterValuesFormView({ setSelectedCoin, onNext, initialFormValues, + onClose, }: EnterValuesFormProps): JSX.Element { // Get all coins of the type const { data: coinsData, isPending: coinsIsPending } = useGetAllCoins( @@ -188,13 +169,14 @@ export function EnterValuesFormView({ const formattedTokenBalance = tokenBalance.replace(/,/g, ''); - if (coinsBalanceIsPending || coinsIsPending || iotaCoinsIsPending) { - return ( - <div className="flex h-full w-full items-center justify-center"> - <LoadingIndicator /> - </div> - ); - } + const formik = useFormik({ + initialValues: initialFormValues, + validationSchema: validationSchemaStepOne, + enableReinitialize: true, + validateOnChange: false, + validateOnBlur: false, + onSubmit: handleFormSubmit, + }); async function handleFormSubmit({ to, amount, gasBudgetEst }: FormDataValues) { const formattedAmount = parseAmount(amount, coinDecimals).toString(); @@ -207,41 +189,72 @@ export function EnterValuesFormView({ onNext(); } + const coinType = coin.coinType; + const formattedAmount = parseAmount(formik.values.amount, coinDecimals); + const isPayAllIota = formattedAmount === coinBalance && coinType === IOTA_TYPE_ARG; + + const hasEnoughBalance = + isPayAllIota || + iotaBalance > + BigInt(formik.values.gasBudgetEst ?? '0') + + (coinType === IOTA_TYPE_ARG ? formattedAmount : 0n); + + if (coinsBalanceIsPending || coinsIsPending || iotaCoinsIsPending) { + return ( + <div className="flex h-full w-full items-center justify-center"> + <LoadingIndicator /> + </div> + ); + } + return ( - <div className="flex h-full w-full flex-col gap-md"> - <CoinSelector - activeCoinType={coin.coinType} - coins={coinsBalance ?? []} - onClick={(coinType) => { - setFormData(INITIAL_VALUES); - const coin = coinsBalance?.find((coin) => coin.coinType === coinType); - setSelectedCoin(coin!); - }} - /> - - <Formik - initialValues={initialFormValues} - validationSchema={validationSchemaStepOne} - enableReinitialize - validateOnChange={false} - validateOnBlur={false} - onSubmit={handleFormSubmit} - > - {(props: FormikProps<FormDataValues>) => ( - <FormInputs - {...props} - coinType={coin.coinType} - coinDecimals={coinDecimals} - coinBalance={coinBalance} - iotaBalance={iotaBalance} - formattedTokenBalance={formattedTokenBalance} - symbol={symbol} - activeAddress={activeAddress} - coins={coins ?? []} - queryResult={queryResult} - /> - )} - </Formik> - </div> + <FormikProvider value={formik}> + <Header title={'Send'} onClose={onClose} /> + <DialogLayoutBody> + <CoinSelector + activeCoinType={coin.coinType} + coins={coinsBalance ?? []} + onClick={(coinType) => { + setFormData(INITIAL_VALUES); + const selectedCoin = coinsBalance?.find( + (coinBalance) => coinBalance.coinType === coinType, + ); + if (selectedCoin) { + setSelectedCoin(selectedCoin); + } + }} + /> + + <FormInputs + hasEnoughBalance={hasEnoughBalance} + formattedAmount={formattedAmount} + isPayAllIota={isPayAllIota} + coinType={coin.coinType} + coinDecimals={coinDecimals} + coinBalance={coinBalance} + iotaBalance={iotaBalance} + formattedTokenBalance={formattedTokenBalance} + symbol={symbol} + activeAddress={activeAddress} + coins={coins ?? []} + queryResult={queryResult} + /> + </DialogLayoutBody> + <DialogLayoutFooter> + <Button + onClick={formik.submitForm} + htmlType={ButtonHtmlType.Submit} + type={ButtonType.Primary} + disabled={ + !formik.isValid || + formik.isSubmitting || + !hasEnoughBalance || + formik.values.gasBudgetEst === '' + } + text="Review" + fullWidth + /> + </DialogLayoutFooter> + </FormikProvider> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx b/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx index 58d6e21e10b..d4d82d1ccad 100644 --- a/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx @@ -16,11 +16,13 @@ import { KeyValueInfo, Divider, ButtonType, + Header, } from '@iota/apps-ui-kit'; import { formatAddress, IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { CoinIcon, ImageIconSize, useFormatCoin, ExplorerLinkType, CoinFormat } from '@iota/core'; import { Loader } from '@iota/ui-icons'; import { ExplorerLink } from '@/components'; +import { DialogLayoutBody, DialogLayoutFooter } from '../../layout'; interface ReviewValuesFormProps { formData: FormDataValues; @@ -29,6 +31,8 @@ interface ReviewValuesFormProps { executeTransfer: () => void; coinType: string; isPayAllIota?: boolean; + onClose: () => void; + onBack: () => void; } export function ReviewValuesFormView({ @@ -38,78 +42,75 @@ export function ReviewValuesFormView({ executeTransfer, coinType, isPayAllIota, + onClose, + onBack, }: ReviewValuesFormProps): JSX.Element { const [roundedAmount, symbol] = useFormatCoin(amount, coinType, CoinFormat.ROUNDED); const [gasEstimated, gasSymbol] = useFormatCoin(gasBudgetEst, IOTA_TYPE_ARG); return ( - <div className="flex h-full flex-col"> - <div className="flex h-full w-full flex-col gap-md"> - <div className="flex h-full flex-col justify-between"> - <div className="h-full flex-1"> - <div className="flex w-full flex-col gap-md"> - {Number(amount) !== 0 ? ( - <Card type={CardType.Filled}> - <CardImage type={ImageType.BgSolid}> - <CoinIcon - coinType={coinType} - rounded - size={ImageIconSize.Small} - /> - </CardImage> - <CardBody - title={`${isPayAllIota ? '~' : ''}${roundedAmount} ${symbol}`} - subtitle="Amount" - /> - <CardAction type={CardActionType.SupportingText} /> - </Card> - ) : null} - <div className="flex flex-col gap-md--rs p-sm--rs"> - <KeyValueInfo - keyText={'From'} - value={ - <ExplorerLink - type={ExplorerLinkType.Address} - address={senderAddress} - > - {formatAddress(senderAddress)} - </ExplorerLink> - } - fullwidth - /> + <> + <Header title="Review & Send" onClose={onClose} onBack={onBack} /> + <DialogLayoutBody> + <div className="flex w-full flex-col gap-md"> + {Number(amount) !== 0 ? ( + <Card type={CardType.Filled}> + <CardImage type={ImageType.BgSolid}> + <CoinIcon coinType={coinType} rounded size={ImageIconSize.Small} /> + </CardImage> + <CardBody + title={`${isPayAllIota ? '~' : ''}${roundedAmount} ${symbol}`} + subtitle="Amount" + /> + <CardAction type={CardActionType.SupportingText} /> + </Card> + ) : null} + <div className="flex flex-col gap-md--rs p-sm--rs"> + <KeyValueInfo + keyText={'From'} + value={ + <ExplorerLink + type={ExplorerLinkType.Address} + address={senderAddress} + > + {formatAddress(senderAddress)} + </ExplorerLink> + } + fullwidth + /> - <Divider /> - <KeyValueInfo - keyText={'To'} - value={ - <ExplorerLink type={ExplorerLinkType.Address} address={to}> - {formatAddress(to || '')} - </ExplorerLink> - } - fullwidth - /> + <Divider /> + <KeyValueInfo + keyText={'To'} + value={ + <ExplorerLink type={ExplorerLinkType.Address} address={to}> + {formatAddress(to || '')} + </ExplorerLink> + } + fullwidth + /> - <Divider /> - <KeyValueInfo - keyText={'Est. Gas Fees'} - value={gasEstimated} - supportingLabel={gasSymbol} - fullwidth - /> - </div> - </div> + <Divider /> + <KeyValueInfo + keyText={'Est. Gas Fees'} + value={gasEstimated} + supportingLabel={gasSymbol} + fullwidth + /> </div> - <Button - type={ButtonType.Primary} - onClick={executeTransfer} - text="Send Now" - disabled={coinType === null || isPending} - fullWidth - icon={isPending ? <Loader className="animate-spin" /> : undefined} - iconAfterText - /> </div> - </div> - </div> + </DialogLayoutBody> + <DialogLayoutFooter> + <Button + type={ButtonType.Primary} + onClick={executeTransfer} + text="Send Now" + disabled={coinType === null || isPending} + fullWidth + icon={isPending ? <Loader className="animate-spin" /> : undefined} + iconAfterText + /> + </DialogLayoutFooter> + </> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/views/TransactionDetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/SendToken/views/TransactionDetailsView.tsx new file mode 100644 index 00000000000..f534f72e387 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/SendToken/views/TransactionDetailsView.tsx @@ -0,0 +1,47 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useGetTransaction } from '@iota/core'; +import { InfoBoxType, InfoBox, InfoBoxStyle } from '@iota/apps-ui-kit'; +import { Warning, Loader } from '@iota/ui-icons'; +import { getExtendedTransaction } from '@/lib/utils'; +import { useCurrentAccount } from '@iota/dapp-kit'; +import { TransactionDetailsLayout } from '../../transaction'; + +interface TransactionDetailsViewProps { + digest?: string; + onClose: () => void; +} + +export function TransactionDetailsView({ digest, onClose }: TransactionDetailsViewProps) { + const currentAccount = useCurrentAccount(); + const { data, isError, error, isFetching } = useGetTransaction(digest || ''); + + if (isError) { + return ( + <InfoBox + type={InfoBoxType.Error} + title="Error getting transaction info" + supportingText={ + error?.message ?? 'An error occurred when getting the transaction info' + } + icon={<Warning />} + style={InfoBoxStyle.Default} + /> + ); + } + + if (isFetching) { + return ( + <div className="flex h-full w-full items-center justify-center"> + <Loader className="h-6 w-6 animate-spin" /> + </div> + ); + } + + const transaction = data && getExtendedTransaction(data, currentAccount?.address || ''); + + return transaction ? ( + <TransactionDetailsLayout transaction={transaction} onClose={onClose} /> + ) : null; +} diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/views/index.ts b/apps/wallet-dashboard/components/Dialogs/SendToken/views/index.ts index bc7f7fd90e7..4938ff14337 100644 --- a/apps/wallet-dashboard/components/Dialogs/SendToken/views/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/SendToken/views/index.ts @@ -3,3 +3,4 @@ export * from './EnterValuesFormView'; export * from './ReviewValuesFormView'; +export * from './TransactionDetailsView'; diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx index 2c9f72a2911..6829097f6c3 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/StakeDialog.tsx @@ -3,32 +3,20 @@ import React, { useMemo, useState } from 'react'; import { EnterAmountView, EnterTimelockedAmountView, SelectValidatorView } from './views'; -import { - useNotifications, - useNewStakeTransaction, - useGetCurrentEpochStartTimestamp, -} from '@/hooks'; import { ExtendedDelegatedStake, - GroupedTimelockObject, parseAmount, - TIMELOCK_IOTA_TYPE, useCoinMetadata, - useGetAllOwnedObjects, useGetValidatorsApy, useBalance, createValidationSchema, MIN_NUMBER_IOTA_TO_STAKE, } from '@iota/core'; import { FormikProvider, useFormik } from 'formik'; -import type { FormikHelpers } from 'formik'; -import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { useCurrentAccount } from '@iota/dapp-kit'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { NotificationType } from '@/stores/notificationStore'; -import { prepareObjectsForTimelockedStakingTransaction } from '@/lib/utils'; import { Dialog } from '@iota/apps-ui-kit'; -import { DetailsView, UnstakeView } from './views'; -import { FormValues } from './views/EnterAmountView'; +import { DetailsView } from './views'; import { TransactionDialogView } from '../TransactionDialog'; import { StakeDialogView } from './enums/view.enums'; @@ -37,7 +25,6 @@ const INITIAL_VALUES = { }; interface StakeDialogProps { - isOpen: boolean; handleClose: () => void; view: StakeDialogView | undefined; setView: (view: StakeDialogView) => void; @@ -47,12 +34,12 @@ interface StakeDialogProps { onSuccess?: (digest: string) => void; selectedValidator?: string; setSelectedValidator?: (validator: string) => void; + onUnstakeClick?: () => void; } export function StakeDialog({ onSuccess, isTimelockedStaking, - isOpen, handleClose, view, setView, @@ -60,6 +47,7 @@ export function StakeDialog({ maxStakableTimelockedAmount, selectedValidator = '', setSelectedValidator, + onUnstakeClick, }: StakeDialogProps): JSX.Element { const account = useCurrentAccount(); const senderAddress = account?.address ?? ''; @@ -78,114 +66,69 @@ export function StakeDialog({ maxStakableTimelockedAmount ?? coinBalance, coinSymbol, coinDecimals, - view === StakeDialogView.Unstake, + false, minimumStake, ), - [maxStakableTimelockedAmount, coinBalance, coinSymbol, coinDecimals, view, minimumStake], + [maxStakableTimelockedAmount, coinBalance, coinSymbol, coinDecimals, minimumStake], ); const formik = useFormik({ initialValues: INITIAL_VALUES, validationSchema: validationSchema, - onSubmit: onSubmit, + onSubmit: () => undefined, validateOnMount: true, }); const amount = formik.values.amount || `${MIN_NUMBER_IOTA_TO_STAKE}`; const amountWithoutDecimals = parseAmount(amount, coinDecimals); - const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); - const { data: timelockedObjects } = useGetAllOwnedObjects(senderAddress, { - StructType: TIMELOCK_IOTA_TYPE, - }); - let groupedTimelockObjects: GroupedTimelockObject[] = []; - if (isTimelockedStaking && timelockedObjects && currentEpochMs) { - groupedTimelockObjects = prepareObjectsForTimelockedStakingTransaction( - timelockedObjects, - amountWithoutDecimals, - currentEpochMs, - ); - } - const { data: newStakeData, isLoading: isTransactionLoading } = useNewStakeTransaction( - selectedValidator, - amountWithoutDecimals, - senderAddress, - isTimelockedStaking, - groupedTimelockObjects, - ); - - const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); - const { addNotification } = useNotifications(); const { data: rollingAverageApys } = useGetValidatorsApy(); const validators = Object.keys(rollingAverageApys ?? {}) ?? []; function handleBack(): void { - setView?.(StakeDialogView.SelectValidator); + setView(StakeDialogView.SelectValidator); } function handleValidatorSelect(validator: string): void { setSelectedValidator?.(validator); } + function setViewBasedOnStakingType() { + setView( + isTimelockedStaking + ? StakeDialogView.EnterTimelockedAmount + : StakeDialogView.EnterAmount, + ); + } + function selectValidatorHandleNext(): void { if (selectedValidator) { - setView?.( - isTimelockedStaking - ? StakeDialogView.EnterTimelockedAmount - : StakeDialogView.EnterAmount, - ); + setViewBasedOnStakingType(); } } - function detailsHandleUnstake() { - setView?.(StakeDialogView.Unstake); - } - function detailsHandleStake() { - setView?.(StakeDialogView.SelectValidator); - } - - function handleStake(): void { - if (isTimelockedStaking && groupedTimelockObjects.length === 0) { - addNotification('Invalid stake amount. Please try again.', NotificationType.Error); - return; - } - if (!newStakeData?.transaction) { - addNotification('Stake transaction was not created', NotificationType.Error); - return; + if (stakedDetails) { + setSelectedValidator?.(stakedDetails.validatorAddress); + setViewBasedOnStakingType(); } - signAndExecuteTransaction( - { - transaction: newStakeData?.transaction, - }, - { - onSuccess: (tx) => { - onSuccess?.(tx.digest); - addNotification('Stake transaction has been sent'); - setTxDigest(tx.digest); - setView?.(StakeDialogView.TransactionDetails); - }, - onError: () => { - addNotification('Stake transaction was not sent', NotificationType.Error); - }, - }, - ); } - function onSubmit(_: FormValues, { resetForm }: FormikHelpers<FormValues>) { - handleStake(); - resetForm(); + function handleTransactionSuccess(digest: string) { + onSuccess?.(digest); + setTxDigest(digest); + setView(StakeDialogView.TransactionDetails); } return ( - <Dialog open={isOpen} onOpenChange={() => handleClose()}> + <Dialog open onOpenChange={() => handleClose()}> <FormikProvider value={formik}> <> {view === StakeDialogView.Details && stakedDetails && ( <DetailsView handleStake={detailsHandleStake} - handleUnstake={detailsHandleUnstake} + handleUnstake={onUnstakeClick} stakedDetails={stakedDetails} handleClose={handleClose} /> @@ -204,28 +147,20 @@ export function StakeDialog({ selectedValidator={selectedValidator} handleClose={handleClose} onBack={handleBack} - onStake={handleStake} - gasBudget={newStakeData?.gasBudget} - isTransactionLoading={isTransactionLoading} + amountWithoutDecimals={amountWithoutDecimals} + senderAddress={senderAddress} + onSuccess={handleTransactionSuccess} /> )} {view === StakeDialogView.EnterTimelockedAmount && ( <EnterTimelockedAmountView selectedValidator={selectedValidator} maxStakableTimelockedAmount={maxStakableTimelockedAmount ?? BigInt(0)} - hasGroupedTimelockObjects={groupedTimelockObjects.length > 0} handleClose={handleClose} onBack={handleBack} - onStake={handleStake} - gasBudget={newStakeData?.gasBudget} - isTransactionLoading={isTransactionLoading} - /> - )} - {view === StakeDialogView.Unstake && stakedDetails && ( - <UnstakeView - extendedStake={stakedDetails} - handleClose={handleClose} - showActiveStatus + senderAddress={senderAddress} + onSuccess={handleTransactionSuccess} + amountWithoutDecimals={amountWithoutDecimals} /> )} {view === StakeDialogView.TransactionDetails && ( diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/enums/view.enums.ts b/apps/wallet-dashboard/components/Dialogs/Staking/enums/view.enums.ts index 1bdb66a93ed..eb4f6cf20f5 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/enums/view.enums.ts +++ b/apps/wallet-dashboard/components/Dialogs/Staking/enums/view.enums.ts @@ -5,7 +5,6 @@ export enum StakeDialogView { Details = 'Details', SelectValidator = 'SelectValidator', EnterAmount = 'EnterAmount', - Unstake = 'Unstake', EnterTimelockedAmount = 'EnterTimelockedAmount', TransactionDetails = 'TransactionDetails', } diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/index.ts b/apps/wallet-dashboard/components/Dialogs/Staking/index.ts index bf9af6d49a4..e32885d1475 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Staking/index.ts @@ -2,5 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 export * from './enums'; -export * from './StakeDialog'; export * from './hooks'; + +export * from './StakeDialog'; diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx index 0cdba0031e1..48ebf1b4f3c 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/DetailsView.tsx @@ -33,11 +33,11 @@ import { formatAddress, IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { DialogLayout, DialogLayoutFooter, DialogLayoutBody } from '../../layout'; interface StakeDialogProps { - stakedDetails: ExtendedDelegatedStake; - showActiveStatus?: boolean; handleClose: () => void; - handleUnstake: () => void; handleStake: () => void; + stakedDetails: ExtendedDelegatedStake; + showActiveStatus?: boolean; + handleUnstake?: () => void; } export function DetailsView({ diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountDialogLayout.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountDialogLayout.tsx new file mode 100644 index 00000000000..9ec233b1838 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountDialogLayout.tsx @@ -0,0 +1,166 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { useFormatCoin, useStakeTxnInfo } from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { + Button, + ButtonType, + KeyValueInfo, + Panel, + Divider, + Input, + InputType, + Header, + InfoBoxType, + InfoBoxStyle, + InfoBox, +} from '@iota/apps-ui-kit'; +import { Field, type FieldProps, useFormikContext } from 'formik'; +import { Exclamation, Loader } from '@iota/ui-icons'; +import { useIotaClientQuery } from '@iota/dapp-kit'; + +import { Validator } from './Validator'; +import { StakedInfo } from './StakedInfo'; +import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout'; + +export interface FormValues { + amount: string; +} + +interface EnterAmountDialogLayoutProps { + selectedValidator: string; + senderAddress: string; + caption: string; + showInfo: boolean; + infoMessage: string; + isLoading: boolean; + onBack: () => void; + handleClose: () => void; + handleStake: () => void; + isStakeDisabled?: boolean; + gasBudget?: string | number | null; +} + +function EnterAmountDialogLayout({ + selectedValidator, + gasBudget, + senderAddress, + caption, + showInfo, + infoMessage, + isLoading, + isStakeDisabled, + onBack, + handleClose, + handleStake, +}: EnterAmountDialogLayoutProps): JSX.Element { + const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); + const { values, errors } = useFormikContext<FormValues>(); + const amount = values.amount; + + const [gas, symbol] = useFormatCoin(gasBudget ?? 0, IOTA_TYPE_ARG); + + const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo( + system?.epoch, + ); + + return ( + <DialogLayout> + <Header title="Enter amount" onClose={handleClose} onBack={onBack} titleCentered /> + <DialogLayoutBody> + <div className="flex w-full flex-col justify-between"> + <div> + <div className="mb-md"> + <Validator address={selectedValidator} isSelected showAction={false} /> + </div> + <StakedInfo + validatorAddress={selectedValidator} + accountAddress={senderAddress} + /> + <div className="my-md w-full"> + <Field name="amount"> + {({ + field: { onChange, ...field }, + form: { setFieldValue }, + meta, + }: FieldProps<FormValues>) => { + return ( + <Input + {...field} + onValueChange={({ value }) => { + setFieldValue('amount', value, true); + }} + type={InputType.NumericFormat} + label="Amount" + value={amount} + suffix={` ${symbol}`} + placeholder="Enter amount to stake" + errorMessage={ + values.amount && meta.error ? meta.error : undefined + } + caption={caption} + /> + ); + }} + </Field> + {showInfo ? ( + <div className="mt-md"> + <InfoBox + type={InfoBoxType.Error} + supportingText={infoMessage} + style={InfoBoxStyle.Elevated} + icon={<Exclamation />} + /> + </div> + ) : null} + </div> + + <Panel hasBorder> + <div className="flex flex-col gap-y-sm p-md"> + <KeyValueInfo + keyText="Staking Rewards Start" + value={stakedRewardsStartEpoch} + fullwidth + /> + <KeyValueInfo + keyText="Redeem Rewards" + value={timeBeforeStakeRewardsRedeemableAgoDisplay} + fullwidth + /> + <Divider /> + <KeyValueInfo + keyText="Gas fee" + value={gas || '--'} + supportingLabel={symbol} + fullwidth + /> + </div> + </Panel> + </div> + </div> + </DialogLayoutBody> + <DialogLayoutFooter> + <div className="flex w-full justify-between gap-sm"> + <Button fullWidth type={ButtonType.Secondary} onClick={onBack} text="Back" /> + <Button + fullWidth + type={ButtonType.Primary} + disabled={!amount || !!errors?.amount || isLoading || isStakeDisabled} + onClick={handleStake} + text="Stake" + icon={ + isLoading ? ( + <Loader className="animate-spin" data-testid="loading-indicator" /> + ) : null + } + iconAfterText + /> + </div> + </DialogLayoutFooter> + </DialogLayout> + ); +} + +export default EnterAmountDialogLayout; diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx index b3869faed2b..b90de664794 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterAmountView.tsx @@ -2,35 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import { - useFormatCoin, - useBalance, - CoinFormat, - parseAmount, - useCoinMetadata, - useStakeTxnInfo, -} from '@iota/core'; +import { useFormatCoin, useBalance, CoinFormat, parseAmount, useCoinMetadata } from '@iota/core'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { - Button, - ButtonType, - KeyValueInfo, - Panel, - Divider, - Input, - InputType, - Header, - InfoBoxType, - InfoBoxStyle, - InfoBox, -} from '@iota/apps-ui-kit'; -import { Field, type FieldProps, useFormikContext } from 'formik'; -import { Exclamation } from '@iota/ui-icons'; -import { useCurrentAccount, useIotaClientQuery } from '@iota/dapp-kit'; - -import { Validator } from './Validator'; -import { StakedInfo } from './StakedInfo'; -import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout'; +import { useFormikContext } from 'formik'; +import { useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { useNewStakeTransaction, useNotifications } from '@/hooks'; +import { NotificationType } from '@/stores/notificationStore'; +import EnterAmountDialogLayout from './EnterAmountDialogLayout'; export interface FormValues { amount: string; @@ -39,38 +17,39 @@ export interface FormValues { interface EnterAmountViewProps { selectedValidator: string; onBack: () => void; - onStake: () => void; showActiveStatus?: boolean; - gasBudget?: string | number | null; handleClose: () => void; - isTransactionLoading?: boolean; + amountWithoutDecimals: bigint; + senderAddress: string; + onSuccess: (digest: string) => void; } function EnterAmountView({ - selectedValidator: selectedValidatorAddress, + selectedValidator, onBack, - onStake, - gasBudget = 0, handleClose, - isTransactionLoading, + amountWithoutDecimals, + senderAddress, + onSuccess, }: EnterAmountViewProps): JSX.Element { + const { addNotification } = useNotifications(); + const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); + const { values, resetForm } = useFormikContext<FormValues>(); + const coinType = IOTA_TYPE_ARG; const { data: metadata } = useCoinMetadata(coinType); const decimals = metadata?.decimals ?? 0; - const account = useCurrentAccount(); - const accountAddress = account?.address; - - const { values, errors } = useFormikContext<FormValues>(); - const amount = values.amount; - - const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); - const { data: iotaBalance } = useBalance(accountAddress!); + const { data: iotaBalance } = useBalance(senderAddress); const coinBalance = BigInt(iotaBalance?.totalBalance || 0); - const gasBudgetBigInt = BigInt(gasBudget ?? 0); - const [gas, symbol] = useFormatCoin(gasBudget, IOTA_TYPE_ARG); + const { data: newStakeData, isLoading: isTransactionLoading } = useNewStakeTransaction( + selectedValidator, + amountWithoutDecimals, + senderAddress, + ); + const gasBudgetBigInt = BigInt(newStakeData?.gasBudget ?? 0); const maxTokenBalance = coinBalance - gasBudgetBigInt; const [maxTokenFormatted, maxTokenFormattedSymbol] = useFormatCoin( maxTokenBalance, @@ -78,109 +57,47 @@ function EnterAmountView({ CoinFormat.FULL, ); - const caption = isTransactionLoading - ? '--' - : `${maxTokenFormatted} ${maxTokenFormattedSymbol} Available`; - - const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo( - system?.epoch, - ); - + const caption = `${maxTokenFormatted} ${maxTokenFormattedSymbol} Available`; + const infoMessage = + 'You have selected an amount that will leave you with insufficient funds to pay for gas fees for unstaking or any other transactions.'; const hasEnoughRemaingBalance = maxTokenBalance > parseAmount(values.amount, decimals) + BigInt(2) * gasBudgetBigInt; - return ( - <DialogLayout> - <Header title="Enter amount" onClose={handleClose} onBack={onBack} titleCentered /> - <DialogLayoutBody> - <div className="flex w-full flex-col justify-between"> - <div> - <div className="mb-md"> - <Validator - address={selectedValidatorAddress} - isSelected - showAction={false} - /> - </div> - <StakedInfo - validatorAddress={selectedValidatorAddress} - accountAddress={accountAddress!} - /> - <div className="my-md w-full"> - <Field name="amount"> - {({ - field: { onChange, ...field }, - form: { setFieldValue }, - meta, - }: FieldProps<FormValues>) => { - return ( - <Input - {...field} - onValueChange={({ value }) => { - setFieldValue('amount', value, true); - }} - type={InputType.NumericFormat} - label="Amount" - value={amount} - suffix={` ${symbol}`} - placeholder="Enter amount to stake" - errorMessage={ - values.amount && meta.error ? meta.error : undefined - } - caption={coinBalance ? caption : ''} - /> - ); - }} - </Field> - {!hasEnoughRemaingBalance ? ( - <div className="mt-md"> - <InfoBox - type={InfoBoxType.Error} - supportingText="You have selected an amount that will leave you with insufficient funds to pay for gas fees for unstaking or any other transactions." - style={InfoBoxStyle.Elevated} - icon={<Exclamation />} - /> - </div> - ) : null} - </div> + function handleStake(): void { + if (!newStakeData?.transaction) { + addNotification('Stake transaction was not created', NotificationType.Error); + return; + } + signAndExecuteTransaction( + { + transaction: newStakeData?.transaction, + }, + { + onSuccess: (tx) => { + onSuccess(tx.digest); + addNotification('Stake transaction has been sent'); + resetForm(); + }, + onError: () => { + addNotification('Stake transaction was not sent', NotificationType.Error); + }, + }, + ); + } - <Panel hasBorder> - <div className="flex flex-col gap-y-sm p-md"> - <KeyValueInfo - keyText="Staking Rewards Start" - value={stakedRewardsStartEpoch} - fullwidth - /> - <KeyValueInfo - keyText="Redeem Rewards" - value={timeBeforeStakeRewardsRedeemableAgoDisplay} - fullwidth - /> - <Divider /> - <KeyValueInfo - keyText="Gas fee" - value={gas || '--'} - supportingLabel={symbol} - fullwidth - /> - </div> - </Panel> - </div> - </div> - </DialogLayoutBody> - <DialogLayoutFooter> - <div className="flex w-full justify-between gap-sm"> - <Button fullWidth type={ButtonType.Secondary} onClick={onBack} text="Back" /> - <Button - fullWidth - type={ButtonType.Primary} - onClick={onStake} - disabled={!amount || !!errors?.amount} - text="Stake" - /> - </div> - </DialogLayoutFooter> - </DialogLayout> + return ( + <EnterAmountDialogLayout + selectedValidator={selectedValidator} + gasBudget={newStakeData?.gasBudget} + senderAddress={senderAddress} + caption={caption} + showInfo={!hasEnoughRemaingBalance} + infoMessage={infoMessage} + isLoading={isTransactionLoading} + onBack={onBack} + handleClose={handleClose} + handleStake={handleStake} + /> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterTimelockedAmountView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterTimelockedAmountView.tsx index 9eaeb41b6a2..b21a6ad3bd1 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterTimelockedAmountView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/EnterTimelockedAmountView.tsx @@ -1,29 +1,25 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import React from 'react'; -import { useFormatCoin, CoinFormat, useStakeTxnInfo } from '@iota/core'; +import React, { useEffect, useState } from 'react'; +import { + useFormatCoin, + CoinFormat, + GroupedTimelockObject, + useGetAllOwnedObjects, + TIMELOCK_IOTA_TYPE, +} from '@iota/core'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { useFormikContext } from 'formik'; +import { useSignAndExecuteTransaction } from '@iota/dapp-kit'; import { - Button, - ButtonType, - KeyValueInfo, - Panel, - Divider, - Input, - InputType, - Header, - InfoBoxType, - InfoBoxStyle, - InfoBox, -} from '@iota/apps-ui-kit'; -import { Field, type FieldProps, useFormikContext } from 'formik'; -import { Exclamation, Loader } from '@iota/ui-icons'; -import { useCurrentAccount, useIotaClientQuery } from '@iota/dapp-kit'; - -import { Validator } from './Validator'; -import { StakedInfo } from './StakedInfo'; -import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout'; + useGetCurrentEpochStartTimestamp, + useNewStakeTimelockedTransaction, + useNotifications, +} from '@/hooks'; +import { NotificationType } from '@/stores/notificationStore'; +import { prepareObjectsForTimelockedStakingTransaction } from '@/lib/utils'; +import EnterAmountDialogLayout from './EnterAmountDialogLayout'; export interface FormValues { amount: string; @@ -32,32 +28,49 @@ export interface FormValues { interface EnterTimelockedAmountViewProps { selectedValidator: string; maxStakableTimelockedAmount: bigint; + amountWithoutDecimals: bigint; + senderAddress: string; onBack: () => void; - onStake: () => void; - gasBudget?: string | number | null; handleClose: () => void; - hasGroupedTimelockObjects?: boolean; - isTransactionLoading?: boolean; + onSuccess: (digest: string) => void; } function EnterTimelockedAmountView({ - selectedValidator: selectedValidatorAddress, + selectedValidator, maxStakableTimelockedAmount, - hasGroupedTimelockObjects, + amountWithoutDecimals, + senderAddress, onBack, - onStake, - gasBudget, handleClose, - isTransactionLoading, + onSuccess, }: EnterTimelockedAmountViewProps): JSX.Element { - const account = useCurrentAccount(); - const accountAddress = account?.address; + const { addNotification } = useNotifications(); + const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); + const { resetForm } = useFormikContext<FormValues>(); + + const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); + const { data: timelockedObjects } = useGetAllOwnedObjects(senderAddress, { + StructType: TIMELOCK_IOTA_TYPE, + }); + const [groupedTimelockObjects, setGroupedTimelockObjects] = useState<GroupedTimelockObject[]>( + [], + ); - const { values, errors } = useFormikContext<FormValues>(); - const amount = values.amount; + const { data: newStakeData, isLoading: isTransactionLoading } = + useNewStakeTimelockedTransaction(selectedValidator, senderAddress, groupedTimelockObjects); - const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); - const [gas, symbol] = useFormatCoin(gasBudget ?? 0, IOTA_TYPE_ARG); + useEffect(() => { + if (timelockedObjects && currentEpochMs) { + const groupedTimelockObjects = prepareObjectsForTimelockedStakingTransaction( + timelockedObjects, + amountWithoutDecimals, + currentEpochMs, + ); + setGroupedTimelockObjects(groupedTimelockObjects); + } + }, [timelockedObjects, currentEpochMs, amountWithoutDecimals]); + + const hasGroupedTimelockObjects = groupedTimelockObjects.length > 0; const [maxTokenFormatted, maxTokenFormattedSymbol] = useFormatCoin( maxStakableTimelockedAmount, @@ -66,114 +79,49 @@ function EnterTimelockedAmountView({ ); const caption = `${maxTokenFormatted} ${maxTokenFormattedSymbol} Available`; + const infoMessage = + 'It is not possible to combine timelocked objects to stake the entered amount. Please try a different amount.'; - const { stakedRewardsStartEpoch, timeBeforeStakeRewardsRedeemableAgoDisplay } = useStakeTxnInfo( - system?.epoch, - ); + function handleStake(): void { + if (groupedTimelockObjects.length === 0) { + addNotification('Invalid stake amount. Please try again.', NotificationType.Error); + return; + } + if (!newStakeData?.transaction) { + addNotification('Stake transaction was not created', NotificationType.Error); + return; + } + signAndExecuteTransaction( + { + transaction: newStakeData?.transaction, + }, + { + onSuccess: (tx) => { + onSuccess?.(tx.digest); + addNotification('Stake transaction has been sent'); + resetForm(); + }, + onError: () => { + addNotification('Stake transaction was not sent', NotificationType.Error); + }, + }, + ); + } return ( - <DialogLayout> - <Header title="Enter amount" onClose={handleClose} onBack={onBack} titleCentered /> - <DialogLayoutBody> - <div className="flex w-full flex-col justify-between"> - <div> - <div className="mb-md"> - <Validator - address={selectedValidatorAddress} - isSelected - showAction={false} - /> - </div> - <StakedInfo - validatorAddress={selectedValidatorAddress} - accountAddress={accountAddress!} - /> - <div className="my-md w-full"> - <Field name="amount"> - {({ - field: { onChange, ...field }, - form: { setFieldValue }, - meta, - }: FieldProps<FormValues>) => { - return ( - <Input - {...field} - onValueChange={({ value }) => { - setFieldValue('amount', value, true); - }} - type={InputType.NumericFormat} - label="Amount" - value={amount} - suffix={` ${symbol}`} - placeholder="Enter amount to stake" - errorMessage={ - values.amount && meta.error ? meta.error : undefined - } - caption={caption} - /> - ); - }} - </Field> - {!hasGroupedTimelockObjects && !isTransactionLoading ? ( - <div className="mt-md"> - <InfoBox - type={InfoBoxType.Error} - supportingText="It is not possible to combine timelocked objects to stake the entered amount. Please try a different amount." - style={InfoBoxStyle.Elevated} - icon={<Exclamation />} - /> - </div> - ) : null} - </div> - - <Panel hasBorder> - <div className="flex flex-col gap-y-sm p-md"> - <KeyValueInfo - keyText="Staking Rewards Start" - value={stakedRewardsStartEpoch} - fullwidth - /> - <KeyValueInfo - keyText="Redeem Rewards" - value={timeBeforeStakeRewardsRedeemableAgoDisplay} - fullwidth - /> - <Divider /> - <KeyValueInfo - keyText="Gas fee" - value={gas || '--'} - supportingLabel={symbol} - fullwidth - /> - </div> - </Panel> - </div> - </div> - </DialogLayoutBody> - <DialogLayoutFooter> - <div className="flex w-full justify-between gap-sm"> - <Button fullWidth type={ButtonType.Secondary} onClick={onBack} text="Back" /> - <Button - fullWidth - type={ButtonType.Primary} - disabled={ - !amount || - !!errors?.amount || - isTransactionLoading || - !hasGroupedTimelockObjects - } - onClick={onStake} - text="Stake" - icon={ - isTransactionLoading ? ( - <Loader className="animate-spin" data-testid="loading-indicator" /> - ) : null - } - iconAfterText - /> - </div> - </DialogLayoutFooter> - </DialogLayout> + <EnterAmountDialogLayout + selectedValidator={selectedValidator} + gasBudget={newStakeData?.gasBudget} + senderAddress={senderAddress} + caption={caption} + showInfo={!hasGroupedTimelockObjects} + infoMessage={infoMessage} + isLoading={isTransactionLoading} + isStakeDisabled={!hasGroupedTimelockObjects} + onBack={onBack} + handleClose={handleClose} + handleStake={handleStake} + /> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/UnstakeView.tsx b/apps/wallet-dashboard/components/Dialogs/Staking/views/UnstakeView.tsx deleted file mode 100644 index 671184c9cea..00000000000 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/UnstakeView.tsx +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { - Header, - Button, - KeyValueInfo, - Divider, - ButtonType, - Panel, - LoadingIndicator, - InfoBoxType, - InfoBoxStyle, - InfoBox, -} from '@iota/apps-ui-kit'; -import { - createUnstakeTransaction, - ExtendedDelegatedStake, - GAS_SYMBOL, - TimeUnit, - useFormatCoin, - useGetTimeBeforeEpochNumber, - useGetStakingValidatorDetails, - useTimeAgo, - useTransactionGasBudget, -} from '@iota/core'; -import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; -import { useMemo } from 'react'; -import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit'; -import { Loader, Warning } from '@iota/ui-icons'; -import { useUnstakeTransaction } from '@/hooks'; -import { ValidatorStakingData } from '@/components'; -import { DialogLayout, DialogLayoutFooter, DialogLayoutBody } from '../../layout'; - -interface UnstakeDialogProps { - extendedStake: ExtendedDelegatedStake; - handleClose: () => void; - showActiveStatus?: boolean; -} - -export function UnstakeView({ - extendedStake, - handleClose, - showActiveStatus, -}: UnstakeDialogProps): JSX.Element { - const stakingReward = BigInt(extendedStake.estimatedReward ?? '').toString(); - const [rewards, rewardSymbol] = useFormatCoin(stakingReward, IOTA_TYPE_ARG); - const activeAddress = useCurrentAccount()?.address ?? null; - - const { - totalStake: [tokenBalance], - totalStakeOriginal, - epoch, - systemDataResult, - delegatedStakeDataResult, - } = useGetStakingValidatorDetails({ - accountAddress: activeAddress, - validatorAddress: extendedStake.validatorAddress, - stakeId: extendedStake.stakedIotaId, - unstake: true, - }); - - const { isLoading: loadingValidators, error: errorValidators } = systemDataResult; - const { - isLoading: isLoadingDelegatedStakeData, - isError, - error: delegatedStakeDataError, - } = delegatedStakeDataResult; - - const delegationId = extendedStake?.status === 'Active' && extendedStake?.stakedIotaId; - - const [totalIota] = useFormatCoin( - BigInt(stakingReward || 0) + totalStakeOriginal, - IOTA_TYPE_ARG, - ); - - const transaction = useMemo( - () => createUnstakeTransaction(extendedStake.stakedIotaId), - [extendedStake], - ); - const { data: gasBudget } = useTransactionGasBudget(activeAddress, transaction); - - const { data: currentEpochEndTime } = useGetTimeBeforeEpochNumber(epoch + 1 || 0); - const currentEpochEndTimeAgo = useTimeAgo({ - timeFrom: currentEpochEndTime, - endLabel: '--', - shortedTimeLabel: false, - shouldEnd: true, - maxTimeUnit: TimeUnit.ONE_HOUR, - }); - - const { data: unstakeData } = useUnstakeTransaction( - extendedStake.stakedIotaId, - activeAddress || '', - ); - const { mutateAsync: signAndExecuteTransaction, isPending } = useSignAndExecuteTransaction(); - - async function handleUnstake(): Promise<void> { - if (!unstakeData) return; - await signAndExecuteTransaction({ - transaction: unstakeData.transaction, - }); - handleClose(); - } - - const currentEpochEndTimeFormatted = - currentEpochEndTime > 0 ? currentEpochEndTimeAgo : `Epoch #${epoch}`; - - if (isLoadingDelegatedStakeData || loadingValidators) { - return ( - <div className="flex h-full w-full items-center justify-center p-2"> - <LoadingIndicator /> - </div> - ); - } - - if (isError || errorValidators) { - return ( - <div className="mb-2 flex h-full w-full items-center justify-center p-2"> - <InfoBox - title="Something went wrong" - supportingText={delegatedStakeDataError?.message ?? 'An error occurred'} - style={InfoBoxStyle.Default} - type={InfoBoxType.Error} - icon={<Warning />} - /> - </div> - ); - } - - return ( - <DialogLayout> - <Header title="Unstake" onClose={handleClose} onBack={handleClose} titleCentered /> - <DialogLayoutBody> - <div className="flex flex-col gap-y-md"> - <ValidatorStakingData - validatorAddress={extendedStake.validatorAddress} - stakeId={extendedStake.stakedIotaId} - isUnstake - /> - - <Panel hasBorder> - <div className="flex flex-col gap-y-sm p-md"> - <KeyValueInfo - keyText="Current Epoch Ends" - value={currentEpochEndTimeFormatted} - fullwidth - /> - <Divider /> - <KeyValueInfo - keyText="Your Stake" - value={tokenBalance} - supportingLabel={GAS_SYMBOL} - fullwidth - /> - <KeyValueInfo - keyText="Rewards Earned" - value={rewards} - supportingLabel={rewardSymbol} - fullwidth - /> - <Divider /> - <KeyValueInfo - keyText="Total unstaked IOTA" - value={totalIota} - supportingLabel={GAS_SYMBOL} - fullwidth - /> - </div> - </Panel> - - <Panel hasBorder> - <div className="flex flex-col gap-y-sm p-md"> - <KeyValueInfo - keyText="Gas Fees" - value={gasBudget || '-'} - supportingLabel={GAS_SYMBOL} - fullwidth - /> - </div> - </Panel> - </div> - </DialogLayoutBody> - - <DialogLayoutFooter> - <Button - type={ButtonType.Secondary} - fullWidth - onClick={handleUnstake} - disabled={isPending || !delegationId} - text="Unstake" - icon={ - isPending ? ( - <Loader className="animate-spin" data-testid="loading-indicator" /> - ) : null - } - iconAfterText - /> - </DialogLayoutFooter> - </DialogLayout> - ); -} diff --git a/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts b/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts index 8ed3cfb4dee..5a0ffed2be6 100644 --- a/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Staking/views/index.ts @@ -3,6 +3,6 @@ export { default as EnterAmountView } from './EnterAmountView'; export { default as EnterTimelockedAmountView } from './EnterTimelockedAmountView'; +export { default as EnterAmountDialogLayout } from './EnterAmountDialogLayout'; export { default as SelectValidatorView } from './SelectValidatorView'; export * from './DetailsView'; -export * from './UnstakeView'; diff --git a/apps/wallet-dashboard/components/Dialogs/TransactionDialog.tsx b/apps/wallet-dashboard/components/Dialogs/TransactionDialog.tsx index c1b6a845b01..8b78f53d190 100644 --- a/apps/wallet-dashboard/components/Dialogs/TransactionDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/TransactionDialog.tsx @@ -46,7 +46,9 @@ export function TransactionDialogView({ renderValidatorLogo={Validator} /> ) : ( - <LoadingIndicator /> + <div className="flex h-full w-full justify-center"> + <LoadingIndicator /> + </div> )} </DialogLayoutBody> <DialogLayoutFooter> diff --git a/apps/wallet-dashboard/components/Dialogs/index.ts b/apps/wallet-dashboard/components/Dialogs/index.ts index 97ee2fabbad..1475376b5ec 100644 --- a/apps/wallet-dashboard/components/Dialogs/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/index.ts @@ -4,4 +4,7 @@ export * from './SendToken'; export * from './ReceiveFundsDialog'; export * from './Staking'; +export * from './unstake'; export * from './vesting'; +export * from './settings'; +export * from './migration'; diff --git a/apps/wallet-dashboard/components/Dialogs/migration/MigrationDialog.tsx b/apps/wallet-dashboard/components/Dialogs/migration/MigrationDialog.tsx new file mode 100644 index 00000000000..3fd91b0eb35 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/migration/MigrationDialog.tsx @@ -0,0 +1,88 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React, { useState } from 'react'; +import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { IotaObjectData } from '@iota/iota-sdk/client'; +import { useMigrationTransaction } from '@/hooks/useMigrationTransaction'; +import { Dialog } from '@iota/apps-ui-kit'; +import toast from 'react-hot-toast'; +import { TransactionDialogView } from '../TransactionDialog'; +import { MigrationDialogView } from './enums'; +import { ConfirmMigrationView } from './views'; + +interface MigrationDialogProps { + handleClose: () => void; + basicOutputObjects: IotaObjectData[] | undefined; + nftOutputObjects: IotaObjectData[] | undefined; + onSuccess: (digest: string) => void; + setOpen: (bool: boolean) => void; + open: boolean; + isTimelocked: boolean; +} + +export function MigrationDialog({ + handleClose, + basicOutputObjects = [], + nftOutputObjects = [], + onSuccess, + open, + setOpen, + isTimelocked, +}: MigrationDialogProps): JSX.Element { + const account = useCurrentAccount(); + const [txDigest, setTxDigest] = useState<string>(''); + const [view, setView] = useState<MigrationDialogView>(MigrationDialogView.Confirmation); + + const { + data: migrateData, + isPending: isMigrationPending, + isError: isMigrationError, + } = useMigrationTransaction(account?.address || '', basicOutputObjects, nftOutputObjects); + + const { mutateAsync: signAndExecuteTransaction, isPending: isSendingTransaction } = + useSignAndExecuteTransaction(); + + async function handleMigrate(): Promise<void> { + if (!migrateData) return; + signAndExecuteTransaction( + { + transaction: migrateData.transaction, + }, + { + onSuccess: (tx) => { + onSuccess(tx.digest); + setTxDigest(tx.digest); + setView(MigrationDialogView.TransactionDetails); + }, + }, + ) + .then(() => { + toast.success('Migration transaction has been sent'); + }) + .catch(() => { + toast.error('Migration transaction was not sent'); + }); + } + + return ( + <Dialog open={open} onOpenChange={setOpen}> + {view === MigrationDialogView.Confirmation && ( + <ConfirmMigrationView + basicOutputObjects={basicOutputObjects} + nftOutputObjects={nftOutputObjects} + onSuccess={handleMigrate} + setOpen={setOpen} + isTimelocked={isTimelocked} + migrateData={migrateData} + isMigrationPending={isMigrationPending} + isMigrationError={isMigrationError} + isSendingTransaction={isSendingTransaction} + /> + )} + {view === MigrationDialogView.TransactionDetails && ( + <TransactionDialogView txDigest={txDigest} onClose={handleClose} /> + )} + </Dialog> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/migration/enums/index.ts b/apps/wallet-dashboard/components/Dialogs/migration/enums/index.ts new file mode 100644 index 00000000000..6f408e39b8c --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/migration/enums/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './view.enums'; diff --git a/apps/wallet-dashboard/components/Dialogs/migration/enums/view.enums.ts b/apps/wallet-dashboard/components/Dialogs/migration/enums/view.enums.ts new file mode 100644 index 00000000000..5b16d31b836 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/migration/enums/view.enums.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum MigrationDialogView { + Confirmation = 'Confirmation', + TransactionDetails = 'TransactionDetails', +} diff --git a/apps/wallet-dashboard/components/Dialogs/migration/index.ts b/apps/wallet-dashboard/components/Dialogs/migration/index.ts new file mode 100644 index 00000000000..41dd3ff2b30 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/migration/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './MigrationDialog'; + +export * from './views'; diff --git a/apps/wallet-dashboard/components/Dialogs/migration/views/ConfirmMigrationView.tsx b/apps/wallet-dashboard/components/Dialogs/migration/views/ConfirmMigrationView.tsx new file mode 100644 index 00000000000..7066a922187 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/migration/views/ConfirmMigrationView.tsx @@ -0,0 +1,169 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { MigrationObjectLoading, VirtualList } from '@/components'; +import { useCurrentAccount } from '@iota/dapp-kit'; +import { IotaObjectData } from '@iota/iota-sdk/client'; +import { + Button, + Header, + InfoBox, + InfoBoxStyle, + InfoBoxType, + KeyValueInfo, + Panel, + Skeleton, + Title, + TitleSize, +} from '@iota/apps-ui-kit'; +import { useGroupedMigrationObjectsByExpirationDate } from '@/hooks'; +import { Loader, Warning } from '@iota/ui-icons'; +import { Collapsible, useFormatCoin } from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { summarizeMigratableObjectValues } from '@/lib/utils'; +import { MigrationObjectDetailsCard } from '@/components/migration/migration-object-details-card'; +import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout'; +import { Transaction } from '@iota/iota-sdk/transactions'; + +interface ConfirmMigrationViewProps { + basicOutputObjects: IotaObjectData[] | undefined; + nftOutputObjects: IotaObjectData[] | undefined; + onSuccess: () => void; + setOpen: (bool: boolean) => void; + isTimelocked: boolean; + migrateData: + | { + transaction: Transaction; + gasBudget: string | number | null; + } + | undefined; + isMigrationPending: boolean; + isMigrationError: boolean; + isSendingTransaction: boolean; +} + +export function ConfirmMigrationView({ + basicOutputObjects = [], + nftOutputObjects = [], + onSuccess, + setOpen, + isTimelocked, + migrateData, + isMigrationPending, + isMigrationError, + isSendingTransaction, +}: ConfirmMigrationViewProps): JSX.Element { + const account = useCurrentAccount(); + + const { + data: resolvedObjects = [], + isLoading, + error: isGroupedMigrationError, + } = useGroupedMigrationObjectsByExpirationDate( + [...basicOutputObjects, ...nftOutputObjects], + isTimelocked, + ); + + const { totalNotOwnedStorageDepositReturnAmount } = summarizeMigratableObjectValues({ + basicOutputs: basicOutputObjects, + nftOutputs: nftOutputObjects, + address: account?.address || '', + }); + + const [gasFee, gasFeeSymbol] = useFormatCoin(migrateData?.gasBudget, IOTA_TYPE_ARG); + const [totalStorageDepositReturnAmountFormatted, totalStorageDepositReturnAmountSymbol] = + useFormatCoin(totalNotOwnedStorageDepositReturnAmount.toString(), IOTA_TYPE_ARG); + + return ( + <DialogLayout> + <Header title="Confirmation" onClose={() => setOpen(false)} titleCentered /> + <DialogLayoutBody> + <div className="flex h-full flex-col gap-y-md overflow-y-auto"> + {isGroupedMigrationError && !isLoading && ( + <InfoBox + title="Error" + supportingText="Failed to load migration objects" + style={InfoBoxStyle.Elevated} + type={InfoBoxType.Error} + icon={<Warning />} + /> + )} + {isLoading ? ( + <> + <Panel hasBorder> + <div className="flex flex-col gap-y-sm p-md"> + <Skeleton widthClass="w-40" heightClass="h-3.5" /> + <MigrationObjectLoading /> + </div> + </Panel> + <Panel hasBorder> + <div className="flex flex-col gap-y-md p-md"> + <Skeleton widthClass="w-full" heightClass="h-3.5" /> + <Skeleton widthClass="w-full" heightClass="h-3.5" /> + </div> + </Panel> + </> + ) : ( + <> + <Collapsible + defaultOpen + render={() => ( + <Title size={TitleSize.Small} title="Assets to Migrate" /> + )} + > + <div className="h-[500px] pb-md--rs xl:h-[600px]"> + <VirtualList + heightClassName="h-full" + overflowClassName="overflow-y-auto" + items={resolvedObjects} + estimateSize={() => 58} + render={(migrationObject) => ( + <MigrationObjectDetailsCard + migrationObject={migrationObject} + isTimelocked={isTimelocked} + /> + )} + /> + </div> + </Collapsible> + <Panel hasBorder> + <div className="flex flex-col gap-y-sm p-md"> + <KeyValueInfo + keyText="Legacy storage deposit" + value={totalStorageDepositReturnAmountFormatted || '-'} + supportingLabel={totalStorageDepositReturnAmountSymbol} + fullwidth + /> + <KeyValueInfo + keyText="Gas Fees" + value={gasFee || '-'} + supportingLabel={gasFeeSymbol} + fullwidth + /> + </div> + </Panel> + </> + )} + </div> + </DialogLayoutBody> + <DialogLayoutFooter> + <Button + text="Migrate" + disabled={isMigrationPending || isMigrationError || isSendingTransaction} + onClick={onSuccess} + icon={ + isMigrationPending || isSendingTransaction ? ( + <Loader + className="h-4 w-4 animate-spin" + data-testid="loading-indicator" + /> + ) : null + } + iconAfterText + fullWidth + /> + </DialogLayoutFooter> + </DialogLayout> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/migration/views/index.ts b/apps/wallet-dashboard/components/Dialogs/migration/views/index.ts new file mode 100644 index 00000000000..b5a03528f1e --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/migration/views/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './ConfirmMigrationView'; diff --git a/apps/wallet-dashboard/components/Dialogs/settings/SettingsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/settings/SettingsDialog.tsx new file mode 100644 index 00000000000..447c3e3a4e9 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/SettingsDialog.tsx @@ -0,0 +1,36 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { Dialog } from '@iota/apps-ui-kit'; +import { SettingsDialogView } from './enums'; +import { SettingsListView, NetworkSelectorView } from './views'; + +interface SettingsDialogProps { + isOpen: boolean; + handleClose: () => void; + view: SettingsDialogView | undefined; + setView: (view: SettingsDialogView) => void; +} + +export function SettingsDialog({ + isOpen, + handleClose, + view, + setView, +}: SettingsDialogProps): JSX.Element { + function onBack(): void { + setView(SettingsDialogView.SelectSetting); + } + + return ( + <Dialog open={isOpen} onOpenChange={() => handleClose()}> + {view === SettingsDialogView.SelectSetting && ( + <SettingsListView handleClose={handleClose} setView={setView} /> + )} + {view === SettingsDialogView.NetworkSettings && ( + <NetworkSelectorView handleClose={handleClose} onBack={onBack} /> + )} + </Dialog> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/settings/enums/index.ts b/apps/wallet-dashboard/components/Dialogs/settings/enums/index.ts new file mode 100644 index 00000000000..6f408e39b8c --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/enums/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './view.enums'; diff --git a/apps/wallet-dashboard/components/Dialogs/settings/enums/view.enums.ts b/apps/wallet-dashboard/components/Dialogs/settings/enums/view.enums.ts new file mode 100644 index 00000000000..b3f17bb2130 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/enums/view.enums.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum SettingsDialogView { + SelectSetting = 'SelectSetting', + NetworkSettings = 'NetworkSettings', +} diff --git a/apps/wallet-dashboard/components/Dialogs/settings/hooks/index.ts b/apps/wallet-dashboard/components/Dialogs/settings/hooks/index.ts new file mode 100644 index 00000000000..dfc58ee2f8b --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/hooks/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './useSettingsDialog'; diff --git a/apps/wallet-dashboard/components/Dialogs/settings/hooks/useSettingsDialog.ts b/apps/wallet-dashboard/components/Dialogs/settings/hooks/useSettingsDialog.ts new file mode 100644 index 00000000000..6691f971e6c --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/hooks/useSettingsDialog.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useState } from 'react'; +import { SettingsDialogView } from '../enums'; + +export function useSettingsDialog() { + const [settingsDialogView, setSettingsDialogView] = useState<SettingsDialogView | undefined>(); + + const isSettingsDialogOpen = settingsDialogView !== undefined; + + function onCloseSettingsDialogClick() { + setSettingsDialogView(undefined); + } + + function onOpenSettingsDialogClick() { + setSettingsDialogView(SettingsDialogView.SelectSetting); + } + + return { + isSettingsDialogOpen, + settingsDialogView, + setSettingsDialogView, + onCloseSettingsDialogClick, + onOpenSettingsDialogClick, + }; +} diff --git a/apps/wallet-dashboard/components/Dialogs/settings/index.ts b/apps/wallet-dashboard/components/Dialogs/settings/index.ts new file mode 100644 index 00000000000..00a59c7b679 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './enums'; +export * from './SettingsDialog'; +export * from './hooks'; +export * from './views'; diff --git a/apps/wallet-dashboard/components/Dialogs/settings/views/NetworkSelectorView.tsx b/apps/wallet-dashboard/components/Dialogs/settings/views/NetworkSelectorView.tsx new file mode 100644 index 00000000000..9eb2d3a80d4 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/views/NetworkSelectorView.tsx @@ -0,0 +1,51 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { Header, RadioButton } from '@iota/apps-ui-kit'; +import { DialogLayout, DialogLayoutBody } from '../../layout'; +import { NetworkConfiguration } from '@iota/iota-sdk/client'; +import { useIotaClientContext } from '@iota/dapp-kit'; +import toast from 'react-hot-toast'; + +interface NetworkSelectorViewProps { + handleClose: () => void; + onBack: () => void; +} + +export function NetworkSelectorView({ + handleClose, + onBack, +}: NetworkSelectorViewProps): JSX.Element { + const clientContext = useIotaClientContext(); + const activeNetwork = clientContext.network; + // Dashboard doesn't support RPCs yet + const networks = clientContext.networks as Record<string, NetworkConfiguration>; + + async function handleNetworkChange(network: NetworkConfiguration) { + if (activeNetwork === network.id) { + return; + } + clientContext.selectNetwork(network.id); + toast.success(`Switched to ${network.name}`); + } + + return ( + <DialogLayout> + <Header title="Network" onClose={handleClose} onBack={onBack} titleCentered /> + <DialogLayoutBody> + <div className="flex w-full flex-col gap-md"> + {Object.values(networks).map((network) => ( + <div className="px-md" key={network.id}> + <RadioButton + label={network.name} + isChecked={activeNetwork === network.id} + onChange={() => handleNetworkChange(network)} + /> + </div> + ))} + </div> + </DialogLayoutBody> + </DialogLayout> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/settings/views/SettingsListView.tsx b/apps/wallet-dashboard/components/Dialogs/settings/views/SettingsListView.tsx new file mode 100644 index 00000000000..7e9d484de73 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/views/SettingsListView.tsx @@ -0,0 +1,62 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { + Card, + CardAction, + CardActionType, + CardBody, + CardImage, + CardType, + Header, + ImageType, +} from '@iota/apps-ui-kit'; +import { DialogLayout, DialogLayoutBody } from '../../layout'; +import { SettingsDialogView } from '../enums'; +import { getNetwork } from '@iota/iota-sdk/client'; +import { useIotaClientContext } from '@iota/dapp-kit'; +import { Globe } from '@iota/ui-icons'; + +interface SettingsListViewProps { + handleClose: () => void; + setView: (view: SettingsDialogView) => void; +} + +export function SettingsListView({ handleClose, setView }: SettingsListViewProps): JSX.Element { + const { network } = useIotaClientContext(); + const { name: networkName } = getNetwork(network); + + const MENU_ITEMS = [ + { + title: 'Network', + subtitle: networkName, + icon: <Globe />, + onClick: () => setView(SettingsDialogView.NetworkSettings), + }, + ]; + + return ( + <DialogLayout> + <Header title="Settings" onClose={handleClose} onBack={handleClose} titleCentered /> + <DialogLayoutBody> + <div className="flex h-full flex-col content-stretch"> + <div className="flex h-full w-full flex-col gap-md"> + {MENU_ITEMS.map((item, index) => ( + <Card key={index} type={CardType.Default} onClick={item.onClick}> + <CardImage type={ImageType.BgSolid}> + <div className="flex h-10 w-10 items-center justify-center rounded-full text-neutral-10 dark:text-neutral-92 [&_svg]:h-5 [&_svg]:w-5"> + <span className="text-2xl">{item.icon}</span> + </div> + </CardImage> + <CardBody title={item.title} subtitle={item.subtitle} /> + <CardAction type={CardActionType.Link} /> + </Card> + ))} + </div> + <p className="text-center">{process.env.NEXT_PUBLIC_DASHBOARD_REV}</p> + </div> + </DialogLayoutBody> + </DialogLayout> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/settings/views/index.ts b/apps/wallet-dashboard/components/Dialogs/settings/views/index.ts new file mode 100644 index 00000000000..738311af3ac --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/settings/views/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './SettingsListView'; +export * from './NetworkSelectorView'; diff --git a/apps/wallet-dashboard/components/Dialogs/transaction/TransactionDetailsLayout.tsx b/apps/wallet-dashboard/components/Dialogs/transaction/TransactionDetailsLayout.tsx new file mode 100644 index 00000000000..44bc9474a6a --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/transaction/TransactionDetailsLayout.tsx @@ -0,0 +1,58 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; +import { ExplorerLink } from '@/components'; +import { ExtendedTransaction } from '@/lib/interfaces'; +import { Header, LoadingIndicator } from '@iota/apps-ui-kit'; +import { + useTransactionSummary, + ViewTxnOnExplorerButton, + ExplorerLinkType, + TransactionReceipt, + useRecognizedPackages, +} from '@iota/core'; +import { useCurrentAccount, useIotaClientContext } from '@iota/dapp-kit'; +import { DialogLayoutBody, DialogLayoutFooter } from '../layout'; +import { Validator } from '../Staking/views/Validator'; +import { Network } from '@iota/iota-sdk/client'; + +interface TransactionDialogDetailsProps { + transaction: ExtendedTransaction; + onClose: () => void; +} +export function TransactionDetailsLayout({ transaction, onClose }: TransactionDialogDetailsProps) { + const address = useCurrentAccount()?.address ?? ''; + + const { network } = useIotaClientContext(); + const recognizedPackagesList = useRecognizedPackages(network as Network); + const summary = useTransactionSummary({ + transaction: transaction.raw, + currentAddress: address, + recognizedPackagesList, + }); + + if (!summary) return <LoadingIndicator />; + + return ( + <> + <Header title="Transaction" onClose={onClose} /> + <DialogLayoutBody> + <TransactionReceipt + txn={transaction.raw} + activeAddress={address} + summary={summary} + renderExplorerLink={ExplorerLink} + renderValidatorLogo={Validator} + /> + </DialogLayoutBody> + <DialogLayoutFooter> + <ExplorerLink + type={ExplorerLinkType.Transaction} + transactionID={transaction.raw.digest} + > + <ViewTxnOnExplorerButton digest={transaction.raw.digest} /> + </ExplorerLink> + </DialogLayoutFooter> + </> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/transaction/index.ts b/apps/wallet-dashboard/components/Dialogs/transaction/index.ts new file mode 100644 index 00000000000..55a1e09a752 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/transaction/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './TransactionDetailsLayout'; diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/UnstakeDialog.tsx b/apps/wallet-dashboard/components/Dialogs/unstake/UnstakeDialog.tsx new file mode 100644 index 00000000000..16ef52b41bb --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/UnstakeDialog.tsx @@ -0,0 +1,56 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Dialog } from '@iota/apps-ui-kit'; +import { UnstakeTimelockedObjectsView, UnstakeView } from './views'; +import { ExtendedDelegatedStake } from '@iota/core'; +import { TimelockedStakedObjectsGrouped } from '@/lib/utils'; +import { UnstakeDialogView } from './enums'; +import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard'; +import { TransactionDialogView } from '../TransactionDialog'; + +interface UnstakeDialogProps { + view: UnstakeDialogView; + handleClose: () => void; + onSuccess: (tx: IotaSignAndExecuteTransactionOutput) => void; + onBack?: (view: UnstakeDialogView) => (() => void) | undefined; + groupedTimelockedObjects?: TimelockedStakedObjectsGrouped; + extendedStake?: ExtendedDelegatedStake; + txDigest?: string; +} + +export function UnstakeDialog({ + view, + handleClose, + onSuccess, + extendedStake, + groupedTimelockedObjects, + onBack, + txDigest, +}: UnstakeDialogProps): React.JSX.Element { + return ( + <Dialog open onOpenChange={handleClose}> + {view === UnstakeDialogView.Unstake && extendedStake && ( + <UnstakeView + extendedStake={extendedStake} + handleClose={handleClose} + onBack={onBack?.(UnstakeDialogView.Unstake)} + showActiveStatus + onSuccess={onSuccess} + /> + )} + + {view === UnstakeDialogView.TimelockedUnstake && groupedTimelockedObjects && ( + <UnstakeTimelockedObjectsView + onClose={handleClose} + groupedTimelockedObjects={groupedTimelockedObjects} + onBack={onBack?.(UnstakeDialogView.TimelockedUnstake)} + onSuccess={onSuccess} + /> + )} + {view === UnstakeDialogView.TransactionDetails && ( + <TransactionDialogView txDigest={txDigest} onClose={handleClose} /> + )} + </Dialog> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/enums/index.ts b/apps/wallet-dashboard/components/Dialogs/unstake/enums/index.ts new file mode 100644 index 00000000000..75ff18fcbaf --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/enums/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './views.enums'; diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/enums/views.enums.ts b/apps/wallet-dashboard/components/Dialogs/unstake/enums/views.enums.ts new file mode 100644 index 00000000000..1b0b313dcce --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/enums/views.enums.ts @@ -0,0 +1,8 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum UnstakeDialogView { + Unstake = 'unstake', + TimelockedUnstake = 'timelockedUnstake', + TransactionDetails = 'transactionDetails', +} diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/hooks/index.ts b/apps/wallet-dashboard/components/Dialogs/unstake/hooks/index.ts new file mode 100644 index 00000000000..8834415eafd --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/hooks/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './useUnstakeDialog'; diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/hooks/useUnstakeDialog.ts b/apps/wallet-dashboard/components/Dialogs/unstake/hooks/useUnstakeDialog.ts new file mode 100644 index 00000000000..88de6c40f25 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/hooks/useUnstakeDialog.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { ComponentProps, useState } from 'react'; +import { UnstakeDialogView } from '../enums'; +import type { UnstakeDialog } from '../UnstakeDialog'; + +export function useUnstakeDialog() { + const [isOpen, setIsOpen] = useState(false); + const [view, setView] = useState<UnstakeDialogView>(UnstakeDialogView.Unstake); + const [txDigest, setTxDigest] = useState<string | undefined>(); + + function openUnstakeDialog(view?: UnstakeDialogView) { + setIsOpen(true); + if (view) { + setView(view); + } + } + + function handleClose() { + setIsOpen(false); + } + + const defaultDialogProps: Omit<ComponentProps<typeof UnstakeDialog>, 'onSuccess'> = { + view, + handleClose: () => setIsOpen(false), + txDigest, + }; + + return { + isOpen, + setIsOpen, + view, + setView, + openUnstakeDialog, + txDigest, + setTxDigest, + defaultDialogProps, + handleClose, + }; +} diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/index.ts b/apps/wallet-dashboard/components/Dialogs/unstake/index.ts new file mode 100644 index 00000000000..05b9b934af5 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/index.ts @@ -0,0 +1,8 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './views'; +export * from './enums'; +export * from './hooks'; + +export * from './UnstakeDialog'; diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/views/UnstakeTimelockedObjectsView.tsx b/apps/wallet-dashboard/components/Dialogs/unstake/views/UnstakeTimelockedObjectsView.tsx new file mode 100644 index 00000000000..45e34ee5cf0 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/views/UnstakeTimelockedObjectsView.tsx @@ -0,0 +1,213 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { StakeRewardsPanel, ValidatorStakingData } from '@/components'; +import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../../layout'; +import { Validator } from '../../Staking/views/Validator'; +import { useNewUnstakeTimelockedTransaction, useNotifications } from '@/hooks'; +import { + Collapsible, + TimeUnit, + useFormatCoin, + useGetActiveValidatorsInfo, + useTimeAgo, +} from '@iota/core'; +import { ExtendedDelegatedTimelockedStake, TimelockedStakedObjectsGrouped } from '@/lib/utils'; +import { formatAddress, IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { + Panel, + LoadingIndicator, + KeyValueInfo, + Header, + ButtonType, + Button, +} from '@iota/apps-ui-kit'; +import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard'; +import { NotificationType } from '@/stores/notificationStore'; + +interface UnstakeTimelockedObjectsViewProps { + onClose: () => void; + groupedTimelockedObjects: TimelockedStakedObjectsGrouped; + onSuccess: (tx: IotaSignAndExecuteTransactionOutput) => void; + onBack?: () => void; +} + +export function UnstakeTimelockedObjectsView({ + groupedTimelockedObjects, + onClose, + onBack, + onSuccess, +}: UnstakeTimelockedObjectsViewProps) { + const { addNotification } = useNotifications(); + const activeAddress = useCurrentAccount()?.address ?? ''; + const { data: activeValidators } = useGetActiveValidatorsInfo(); + + const stakes = groupedTimelockedObjects.stakes; + const timelockedStakedIotaIds = stakes.map((stake) => stake.timelockedStakedIotaId); + + const { data: unstakeData, isPending: isUnstakeTxPending } = useNewUnstakeTimelockedTransaction( + activeAddress, + timelockedStakedIotaIds, + ); + const { mutateAsync: signAndExecuteTransaction, isPending: isTransactionPending } = + useSignAndExecuteTransaction(); + + const validatorInfo = activeValidators?.find( + ({ iotaAddress: validatorAddress }) => + validatorAddress === groupedTimelockedObjects.validatorAddress, + ); + + const stakeId = stakes[0].timelockedStakedIotaId; + const totalStakedAmount = stakes.reduce((acc, stake) => acc + parseInt(stake.principal), 0); + const totalRewards = stakes.reduce( + (acc, stake) => acc + (stake.status === 'Active' ? parseInt(stake.estimatedReward) : 0), + 0, + ); + + const [rewardsPoolFormatted, rewardsToken] = useFormatCoin( + validatorInfo?.rewardsPool, + IOTA_TYPE_ARG, + ); + + function handleCopySuccess() { + addNotification('Copied to clipboard'); + } + + async function handleUnstake(): Promise<void> { + if (!unstakeData) return; + + await signAndExecuteTransaction( + { + transaction: unstakeData.transaction, + }, + { + onSuccess: (tx) => { + addNotification('Unstake transaction has been sent'); + onSuccess(tx); + }, + }, + ).catch(() => { + addNotification('Unstake transaction was not sent', NotificationType.Error); + }); + } + + return ( + <DialogLayout> + <Header title="Unstake" onClose={onClose} onBack={onBack} /> + <DialogLayoutBody> + <div className="flex flex-col gap-md"> + <Validator + address={groupedTimelockedObjects.validatorAddress} + isSelected + showActiveStatus + /> + + {stakeId && ( + <ValidatorStakingData + key={stakeId} + validatorAddress={groupedTimelockedObjects.validatorAddress} + stakeId={stakeId} + isUnstake + /> + )} + + <StakeRewardsPanel + stakingRewards={totalRewards} + totalStaked={totalStakedAmount} + isTimelocked + /> + + <Panel hasBorder> + <div className="flex flex-col gap-y-sm p-md"> + <KeyValueInfo + keyText="Stake Request Epoch" + value={groupedTimelockedObjects.stakeRequestEpoch} + fullwidth + /> + {rewardsPoolFormatted && ( + <KeyValueInfo + keyText="Rewards Pool" + value={rewardsPoolFormatted} + supportingLabel={rewardsToken} + fullwidth + /> + )} + <KeyValueInfo keyText="Total Stakes" value={stakes.length} fullwidth /> + </div> + </Panel> + + {stakes.map((stake, index) => ( + <TimelockedStakeCollapsible + title={`Stake Nº${index + 1}`} + key={stake.timelockedStakedIotaId} + stake={stake} + handleCopySuccess={handleCopySuccess} + /> + ))} + </div> + </DialogLayoutBody> + <DialogLayoutFooter> + <Button + onClick={handleUnstake} + text="Unstake" + icon={!unstakeData || isUnstakeTxPending ? <LoadingIndicator /> : undefined} + disabled={!unstakeData || isTransactionPending || isUnstakeTxPending} + type={ButtonType.Secondary} + fullWidth + /> + </DialogLayoutFooter> + </DialogLayout> + ); +} + +interface TimelockedStakeCollapsibleProps { + stake: ExtendedDelegatedTimelockedStake; + title: string; + handleCopySuccess: () => void; +} +function TimelockedStakeCollapsible({ + stake, + title, + handleCopySuccess, +}: TimelockedStakeCollapsibleProps) { + const currentEpochEndTimeAgo = useTimeAgo({ + timeFrom: Number(stake.expirationTimestampMs), + endLabel: '--', + shortedTimeLabel: false, + shouldEnd: true, + maxTimeUnit: TimeUnit.ONE_DAY, + }); + return ( + <Collapsible defaultOpen key={stake.timelockedStakedIotaId} title={title}> + <Panel> + <div className="flex flex-col gap-y-sm p-md--rs py-sm"> + <KeyValueInfo + keyText="Stake ID" + value={formatAddress(stake.timelockedStakedIotaId)} + valueHoverTitle={stake.timelockedStakedIotaId} + onCopySuccess={handleCopySuccess} + copyText={stake.timelockedStakedIotaId} + fullwidth + /> + <KeyValueInfo + keyText="Expiration time" + value={currentEpochEndTimeAgo} + fullwidth + /> + {stake.label && ( + <KeyValueInfo + keyText="Label" + value={formatAddress(stake.label)} + copyText={stake.label} + valueHoverTitle={stake.label} + onCopySuccess={handleCopySuccess} + fullwidth + /> + )} + <KeyValueInfo keyText="Status" value={stake.status} fullwidth /> + </div> + </Panel> + </Collapsible> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/views/UnstakeView.tsx b/apps/wallet-dashboard/components/Dialogs/unstake/views/UnstakeView.tsx new file mode 100644 index 00000000000..5d07eadcec3 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/views/UnstakeView.tsx @@ -0,0 +1,167 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { + Header, + Button, + KeyValueInfo, + ButtonType, + Panel, + LoadingIndicator, + InfoBoxType, + InfoBoxStyle, + InfoBox, +} from '@iota/apps-ui-kit'; +import { + ExtendedDelegatedStake, + GAS_SYMBOL, + useFormatCoin, + useGetStakingValidatorDetails, +} from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { Warning } from '@iota/ui-icons'; +import { StakeRewardsPanel, ValidatorStakingData } from '@/components'; +import { DialogLayout, DialogLayoutFooter, DialogLayoutBody } from '../../layout'; +import { Validator } from '../../Staking/views/Validator'; +import { useNewUnstakeTransaction, useNotifications } from '@/hooks'; +import { IotaSignAndExecuteTransactionOutput } from '@iota/wallet-standard'; +import { NotificationType } from '@/stores/notificationStore'; + +interface UnstakeDialogProps { + extendedStake: ExtendedDelegatedStake; + handleClose: () => void; + onSuccess: (tx: IotaSignAndExecuteTransactionOutput) => void; + showActiveStatus?: boolean; + onBack?: () => void; +} + +export function UnstakeView({ + extendedStake, + handleClose, + onBack, + onSuccess, + showActiveStatus, +}: UnstakeDialogProps): JSX.Element { + const activeAddress = useCurrentAccount()?.address ?? ''; + const { addNotification } = useNotifications(); + const { data: unstakeData, isPending: isUnstakeTxPending } = useNewUnstakeTransaction( + activeAddress, + extendedStake.stakedIotaId, + ); + const [gasFormatted] = useFormatCoin(unstakeData?.gasBudget, IOTA_TYPE_ARG); + + const { mutateAsync: signAndExecuteTransaction, isPending: isTransactionPending } = + useSignAndExecuteTransaction(); + + const { totalStakeOriginal, systemDataResult, delegatedStakeDataResult } = + useGetStakingValidatorDetails({ + accountAddress: activeAddress, + validatorAddress: extendedStake.validatorAddress, + stakeId: extendedStake.stakedIotaId, + unstake: true, + }); + + const { isLoading: loadingValidators, error: errorValidators } = systemDataResult; + const { + isLoading: isLoadingDelegatedStakeData, + isError, + error: delegatedStakeDataError, + } = delegatedStakeDataResult; + + const delegationId = extendedStake?.stakedIotaId; + const isPreparingUnstake = !unstakeData || isUnstakeTxPending; + + async function handleUnstake(): Promise<void> { + if (!unstakeData) return; + + await signAndExecuteTransaction( + { + transaction: unstakeData.transaction, + }, + { + onSuccess: (tx) => { + addNotification('Unstake transaction has been sent'); + onSuccess(tx); + }, + }, + ).catch(() => { + addNotification('Unstake transaction was not sent', NotificationType.Error); + }); + } + + if (isLoadingDelegatedStakeData || loadingValidators) { + return ( + <div className="flex h-full w-full items-center justify-center p-2"> + <LoadingIndicator /> + </div> + ); + } + + if (isError || errorValidators) { + return ( + <div className="mb-2 flex h-full w-full items-center justify-center p-2"> + <InfoBox + title="Something went wrong" + supportingText={delegatedStakeDataError?.message ?? 'An error occurred'} + style={InfoBoxStyle.Default} + type={InfoBoxType.Error} + icon={<Warning />} + /> + </div> + ); + } + + return ( + <DialogLayout> + <Header title="Unstake" onClose={handleClose} onBack={onBack} titleCentered /> + <DialogLayoutBody> + <div className="flex flex-col gap-y-md"> + <Validator + address={extendedStake.validatorAddress} + isSelected + showActiveStatus={showActiveStatus} + /> + + <ValidatorStakingData + validatorAddress={extendedStake.validatorAddress} + stakeId={extendedStake.stakedIotaId} + isUnstake + /> + + <StakeRewardsPanel + stakingRewards={extendedStake.estimatedReward} + totalStaked={totalStakeOriginal} + /> + + <Panel hasBorder> + <div className="flex flex-col gap-y-sm p-md"> + <KeyValueInfo + keyText="Gas Fees" + value={gasFormatted || '-'} + supportingLabel={GAS_SYMBOL} + fullwidth + /> + </div> + </Panel> + </div> + </DialogLayoutBody> + + <DialogLayoutFooter> + <Button + type={ButtonType.Secondary} + fullWidth + onClick={handleUnstake} + disabled={isPreparingUnstake || isTransactionPending || !delegationId} + text="Unstake" + icon={ + isPreparingUnstake ? ( + <LoadingIndicator data-testid="loading-indicator" /> + ) : null + } + iconAfterText + /> + </DialogLayoutFooter> + </DialogLayout> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/unstake/views/index.ts b/apps/wallet-dashboard/components/Dialogs/unstake/views/index.ts new file mode 100644 index 00000000000..88b177871cd --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/unstake/views/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './UnstakeView'; +export * from './UnstakeTimelockedObjectsView'; diff --git a/apps/wallet-dashboard/components/Dialogs/vesting/VestingScheduleBox.tsx b/apps/wallet-dashboard/components/Dialogs/vesting/VestingScheduleBox.tsx index 25f861590a0..d9a93fc8d7c 100644 --- a/apps/wallet-dashboard/components/Dialogs/vesting/VestingScheduleBox.tsx +++ b/apps/wallet-dashboard/components/Dialogs/vesting/VestingScheduleBox.tsx @@ -8,7 +8,7 @@ import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { LockLocked } from '@iota/ui-icons'; interface VestingScheduleBoxProps { - amount: number; + amount: bigint; expirationTimestampMs: number; } diff --git a/apps/wallet-dashboard/components/MigrationOverview.tsx b/apps/wallet-dashboard/components/MigrationOverview.tsx index 9b17852f3d5..09faf91737b 100644 --- a/apps/wallet-dashboard/components/MigrationOverview.tsx +++ b/apps/wallet-dashboard/components/MigrationOverview.tsx @@ -12,7 +12,7 @@ export function MigrationOverview() { const router = useRouter(); const account = useCurrentAccount(); const address = account?.address || ''; - const { migratableBasicOutputs, migratableNftOutputs } = + const { data: { migratableBasicOutputs = [], migratableNftOutputs = [] } = {} } = useGetStardustMigratableObjects(address); const needsMigration = migratableBasicOutputs.length > 0 || migratableNftOutputs.length > 0; diff --git a/apps/wallet-dashboard/components/Popup/Popup.tsx b/apps/wallet-dashboard/components/Popup/Popup.tsx deleted file mode 100644 index e16a915882c..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popup.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React from 'react'; -import { usePopups } from '@/hooks'; - -function Popup(): JSX.Element { - const { popups, closePopup } = usePopups(); - - return ( - <> - {popups.map((popup, index) => ( - <div - key={index} - className="fixed left-0 top-0 z-[1000] flex h-full w-full items-center justify-center bg-black/20" - > - <div className="relative"> - <div className="absolute left-2/4 top-2/4 flex max-h-[80vh] min-w-[200px] -translate-x-2/4 -translate-y-2/4 flex-col gap-3 overflow-y-auto rounded-lg bg-white p-5 text-black"> - <button className="cursor-pointer self-end" onClick={closePopup}> - X - </button> - {popup} - </div> - </div> - </div> - ))} - </> - ); -} - -export default Popup; diff --git a/apps/wallet-dashboard/components/Popup/PopupProvider.tsx b/apps/wallet-dashboard/components/Popup/PopupProvider.tsx deleted file mode 100644 index 8eb6fbfc3a3..00000000000 --- a/apps/wallet-dashboard/components/Popup/PopupProvider.tsx +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -'use client'; -import { PopupContext } from '@/contexts'; -import React, { useState, ReactNode } from 'react'; - -interface PopupProviderProps { - children: ReactNode; -} - -function PopupProvider({ children }: PopupProviderProps): JSX.Element { - const [popups, setPopups] = useState<ReactNode[]>([]); - - const openPopup = (content: ReactNode) => { - setPopups((prevPopups) => [...prevPopups, content]); - }; - - const closePopup = () => { - setPopups((prevPopups) => prevPopups.slice(0, prevPopups.length - 1)); - }; - - return ( - <PopupContext.Provider value={{ popups, openPopup, closePopup }}> - {children} - </PopupContext.Provider> - ); -} - -export default PopupProvider; diff --git a/apps/wallet-dashboard/components/Popup/Popups/MigratePopup.tsx b/apps/wallet-dashboard/components/Popup/Popups/MigratePopup.tsx deleted file mode 100644 index 627fb860f3f..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popups/MigratePopup.tsx +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React from 'react'; -import { VirtualList } from '@/components'; -import { - useCurrentAccount, - useIotaClientContext, - useSignAndExecuteTransaction, -} from '@iota/dapp-kit'; -import { getNetwork, IotaObjectData } from '@iota/iota-sdk/client'; -import { useMigrationTransaction } from '@/hooks/useMigrationTransaction'; -import { Button, InfoBox, InfoBoxStyle, InfoBoxType } from '@iota/apps-ui-kit'; -import { useNotifications } from '@/hooks'; -import { NotificationType } from '@/stores/notificationStore'; -import { Loader, Warning } from '@iota/ui-icons'; - -interface MigratePopupProps { - basicOutputObjects: IotaObjectData[]; - nftOutputObjects: IotaObjectData[]; - closePopup: () => void; - onSuccess?: (digest: string) => void; -} - -function MigratePopup({ - basicOutputObjects = [], - nftOutputObjects = [], - closePopup, - onSuccess, -}: MigratePopupProps): JSX.Element { - const account = useCurrentAccount(); - const { addNotification } = useNotifications(); - const { - data: migrateData, - isPending, - isError, - error, - } = useMigrationTransaction(account?.address || '', basicOutputObjects, nftOutputObjects); - - const { network } = useIotaClientContext(); - const { explorer } = getNetwork(network); - const { mutateAsync: signAndExecuteTransaction, isPending: isSendingTransaction } = - useSignAndExecuteTransaction(); - - async function handleMigrate(): Promise<void> { - if (!migrateData) return; - signAndExecuteTransaction( - { - transaction: migrateData.transaction, - }, - { - onSuccess: (tx) => { - if (onSuccess) { - onSuccess(tx.digest); - } - }, - }, - ) - .then(() => { - closePopup(); - addNotification('Migration transaction has been sent'); - }) - .catch(() => { - addNotification('Migration transaction was not sent', NotificationType.Error); - }); - } - - const virtualItem = (asset: IotaObjectData): JSX.Element => ( - <a href={`${explorer}/object/${asset.objectId}`} target="_blank" rel="noreferrer"> - {asset.objectId} - </a> - ); - return ( - <div className="flex min-w-[300px] flex-col gap-2"> - <div className="flex flex-col"> - <h1>Migratable Basic Outputs: {basicOutputObjects?.length}</h1> - <VirtualList - items={basicOutputObjects ?? []} - estimateSize={() => 30} - render={virtualItem} - /> - </div> - <div className="flex flex-col"> - <h1>Migratable Nft Outputs: {nftOutputObjects?.length}</h1> - <VirtualList - items={nftOutputObjects ?? []} - estimateSize={() => 30} - render={virtualItem} - /> - </div> - <p>Gas Fees: {migrateData?.gasBudget?.toString() || '--'}</p> - {isError ? ( - <InfoBox - type={InfoBoxType.Error} - title={error?.message || 'Error creating migration transcation'} - icon={<Warning />} - style={InfoBoxStyle.Elevated} - /> - ) : null} - <Button - text="Migrate" - disabled={isPending || isError || isSendingTransaction} - onClick={handleMigrate} - icon={ - isPending || isSendingTransaction ? ( - <Loader className="h-4 w-4 animate-spin" /> - ) : null - } - iconAfterText - /> - </div> - ); -} - -export default MigratePopup; diff --git a/apps/wallet-dashboard/components/Popup/Popups/SendAssetPopup.tsx b/apps/wallet-dashboard/components/Popup/Popups/SendAssetPopup.tsx deleted file mode 100644 index 679c4002d96..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popups/SendAssetPopup.tsx +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React, { useCallback, useState } from 'react'; -import { IotaObjectData } from '@iota/iota-sdk/client'; -import { VisualAssetTile, Input } from '@/components'; -import { Button } from '@/components/Buttons'; -import { FlexDirection } from '@/lib/ui/enums'; -import { useCurrentAccount } from '@iota/dapp-kit'; -import { createNftSendValidationSchema, ValidationError } from '@iota/core'; -import { useRouter } from 'next/navigation'; -import { useNotifications } from '@/hooks'; -import { NotificationType } from '@/stores/notificationStore'; -import { useCreateSendAssetTransaction } from '@/hooks'; -import { ASSETS_ROUTE } from '@/lib/constants/routes.constants'; - -interface SendAssetPopupProps { - asset: IotaObjectData; - onClose: () => void; -} - -export default function SendAssetPopup({ asset, onClose }: SendAssetPopupProps): JSX.Element { - const [recipientAddress, setRecipientAddress] = useState<string>(''); - const [errors, setErrors] = useState<string[]>([]); - const activeAddress = useCurrentAccount()?.address; - const router = useRouter(); - const { addNotification } = useNotifications(); - const { mutation: sendAsset } = useCreateSendAssetTransaction( - asset.objectId, - onSendAssetSuccess, - onSendAssetError, - ); - - const schema = createNftSendValidationSchema(activeAddress || '', asset.objectId); - - async function handleAddressChange(address: string): Promise<void> { - setRecipientAddress(address); - - try { - await schema.validate({ to: address }); - setErrors([]); - } catch (error) { - if (error instanceof ValidationError) { - setErrors(error.errors); - } - } - } - - function onSendAssetSuccess() { - addNotification('Transfer transaction successful', NotificationType.Success); - onClose?.(); - router.push(ASSETS_ROUTE.path + '/assets'); - } - - function onSendAssetError() { - addNotification('Transfer transaction failed', NotificationType.Error); - onClose?.(); - } - - const handleSendAsset = useCallback(async () => { - try { - await sendAsset.mutateAsync(recipientAddress); - } catch (error) { - addNotification('Transfer transaction failed', NotificationType.Error); - } - }, [recipientAddress, sendAsset, addNotification]); - - return ( - <div className="flex flex-col space-y-4"> - <VisualAssetTile asset={asset} flexDirection={FlexDirection.Column} /> - <div className="flex flex-col space-y-2"> - <Input - type="text" - value={recipientAddress} - placeholder="Enter Address" - onChange={(e) => handleAddressChange(e.target.value)} - label="Enter recipient address" - error={errors[0]} - /> - </div> - <Button onClick={handleSendAsset}>Send</Button> - <Button onClick={onClose}>Cancel</Button> - </div> - ); -} diff --git a/apps/wallet-dashboard/components/Popup/Popups/UnstakePopup.tsx b/apps/wallet-dashboard/components/Popup/Popups/UnstakePopup.tsx deleted file mode 100644 index 0518809fced..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popups/UnstakePopup.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React from 'react'; -import { Button } from '@/components'; -import { useUnstakeTransaction } from '@/hooks'; -import { useCurrentAccount, useSignAndExecuteTransaction } from '@iota/dapp-kit'; -import { ExtendedDelegatedStake } from '@iota/core'; - -interface UnstakePopupProps { - extendedStake: ExtendedDelegatedStake; - closePopup: () => void; -} - -function UnstakePopup({ extendedStake, closePopup }: UnstakePopupProps): JSX.Element { - const account = useCurrentAccount(); - const { data: unstakeData } = useUnstakeTransaction( - extendedStake.stakedIotaId, - account?.address || '', - ); - const { mutateAsync: signAndExecuteTransaction, isPending } = useSignAndExecuteTransaction(); - - async function handleUnstake(): Promise<void> { - if (!unstakeData) return; - await signAndExecuteTransaction({ - transaction: unstakeData.transaction, - }); - closePopup(); - } - - return ( - <div className="flex min-w-[300px] flex-col gap-2"> - <p>Stake ID: {extendedStake.stakedIotaId}</p> - <p>Validator: {extendedStake.validatorAddress}</p> - <p>Stake: {extendedStake.principal}</p> - {extendedStake.status === 'Active' && ( - <p>Estimated reward: {extendedStake.estimatedReward}</p> - )} - <p>Gas Fees: {unstakeData?.gasBudget?.toString() || '--'}</p> - {isPending ? ( - <Button disabled>Loading...</Button> - ) : ( - <Button onClick={handleUnstake}>Confirm Unstake</Button> - )} - </div> - ); -} - -export default UnstakePopup; diff --git a/apps/wallet-dashboard/components/Popup/Popups/VestingPopup/TimelockedUnstakePopup.tsx b/apps/wallet-dashboard/components/Popup/Popups/VestingPopup/TimelockedUnstakePopup.tsx deleted file mode 100644 index 9cfdc96f035..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popups/VestingPopup/TimelockedUnstakePopup.tsx +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React from 'react'; -import { Button } from '@/components'; -import { useNotifications, useTimelockedUnstakeTransaction } from '@/hooks'; -import { useSignAndExecuteTransaction } from '@iota/dapp-kit'; -import { IotaValidatorSummary } from '@iota/iota-sdk/client'; -import { NotificationType } from '@/stores/notificationStore'; -import { TimelockedStakedObjectsGrouped } from '@/lib/utils'; - -interface UnstakePopupProps { - accountAddress: string; - delegatedStake: TimelockedStakedObjectsGrouped; - validatorInfo: IotaValidatorSummary; - closePopup: () => void; - onSuccess?: (digest: string) => void; -} - -function TimelockedUnstakePopup({ - accountAddress, - delegatedStake, - validatorInfo, - closePopup, - onSuccess, -}: UnstakePopupProps): JSX.Element { - const objectIds = delegatedStake.stakes.map((stake) => stake.timelockedStakedIotaId); - const { data: timelockedUnstake } = useTimelockedUnstakeTransaction(objectIds, accountAddress); - const { mutateAsync: signAndExecuteTransaction, isPending } = useSignAndExecuteTransaction(); - const { addNotification } = useNotifications(); - - async function handleTimelockedUnstake(): Promise<void> { - if (!timelockedUnstake) return; - signAndExecuteTransaction( - { - transaction: timelockedUnstake.transaction, - }, - { - onSuccess: (tx) => { - if (onSuccess) { - onSuccess(tx.digest); - } - }, - }, - ) - .then(() => { - closePopup(); - addNotification('Unstake transaction has been sent'); - }) - .catch(() => { - addNotification('Unstake transaction was not sent', NotificationType.Error); - }); - } - - return ( - <div className="flex min-w-[300px] flex-col gap-2"> - <p>Validator Name: {validatorInfo.name}</p> - <p>Validator Address: {delegatedStake.validatorAddress}</p> - <p>Stake Request Epoch: {delegatedStake.stakeRequestEpoch}</p> - <p>Rewards: {validatorInfo.rewardsPool}</p> - <p>Total Stakes: {delegatedStake.stakes.length}</p> - {delegatedStake.stakes.map((stake, index) => { - return ( - <div key={stake.timelockedStakedIotaId} className="m-4 flex flex-col"> - <span> - Stake {index + 1}: {stake.timelockedStakedIotaId} - </span> - <span>Expiration time: {stake.expirationTimestampMs}</span> - <span>Label: {stake.label}</span> - <span>Status: {stake.status}</span> - </div> - ); - })} - <p>Gas Fees: {timelockedUnstake?.gasBudget?.toString() || '--'}</p> - {isPending ? ( - <Button disabled>Loading...</Button> - ) : ( - <Button onClick={handleTimelockedUnstake}>Confirm Unstake</Button> - )} - </div> - ); -} - -export default TimelockedUnstakePopup; diff --git a/apps/wallet-dashboard/components/Popup/Popups/VestingPopup/index.ts b/apps/wallet-dashboard/components/Popup/Popups/VestingPopup/index.ts deleted file mode 100644 index 2a8ca3b61df..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popups/VestingPopup/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export { default as TimelockedUnstakePopup } from './TimelockedUnstakePopup'; diff --git a/apps/wallet-dashboard/components/Popup/Popups/index.ts b/apps/wallet-dashboard/components/Popup/Popups/index.ts deleted file mode 100644 index ebdfc7f7391..00000000000 --- a/apps/wallet-dashboard/components/Popup/Popups/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export { default as UnstakePopup } from './UnstakePopup'; -export { default as SendAssetPopup } from './SendAssetPopup'; - -export * from './VestingPopup'; diff --git a/apps/wallet-dashboard/components/Popup/index.ts b/apps/wallet-dashboard/components/Popup/index.ts deleted file mode 100644 index baeafee23a7..00000000000 --- a/apps/wallet-dashboard/components/Popup/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export { default as Popup } from './Popup'; -export { default as PopupProvider } from './PopupProvider'; - -export * from './Popups'; diff --git a/apps/wallet-dashboard/components/StakeRewardsPanel.tsx b/apps/wallet-dashboard/components/StakeRewardsPanel.tsx new file mode 100644 index 00000000000..a7e5e84619c --- /dev/null +++ b/apps/wallet-dashboard/components/StakeRewardsPanel.tsx @@ -0,0 +1,70 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Divider, KeyValueInfo, Panel } from '@iota/apps-ui-kit'; +import { TimeUnit, useFormatCoin, useGetTimeBeforeEpochNumber, useTimeAgo } from '@iota/core'; +import { useIotaClientQuery } from '@iota/dapp-kit'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; + +interface StakeRewardsPanelProps { + stakingRewards: string | number | undefined; + totalStaked: number | bigint; + isTimelocked?: boolean; +} +export function StakeRewardsPanel({ + stakingRewards, + totalStaked, + isTimelocked, +}: StakeRewardsPanelProps) { + const { epoch = '0' } = useIotaClientQuery('getLatestIotaSystemState')?.data || {}; + const [rewards, symbol] = useFormatCoin(stakingRewards ?? 0, IOTA_TYPE_ARG); + const [stakedBalance] = useFormatCoin(totalStaked, IOTA_TYPE_ARG); + const [stakedAndRewards] = useFormatCoin( + BigInt(stakingRewards || 0) + BigInt(totalStaked), + IOTA_TYPE_ARG, + ); + + const { data: currentEpochEndTime } = useGetTimeBeforeEpochNumber(Number(epoch) + 1); + const currentEpochEndTimeAgo = useTimeAgo({ + timeFrom: currentEpochEndTime, + endLabel: '--', + shortedTimeLabel: false, + shouldEnd: true, + maxTimeUnit: TimeUnit.ONE_HOUR, + }); + + const currentEpochEndTimeFormatted = + currentEpochEndTime > 0 ? currentEpochEndTimeAgo : `Epoch #${epoch}`; + + return ( + <Panel hasBorder> + <div className="flex flex-col gap-y-sm p-md"> + <KeyValueInfo + keyText="Current Epoch Ends" + value={currentEpochEndTimeFormatted} + fullwidth + /> + <Divider /> + <KeyValueInfo + keyText="Your Stake" + value={stakedBalance} + supportingLabel={symbol} + fullwidth + /> + <KeyValueInfo + keyText="Rewards Earned" + value={rewards} + supportingLabel={symbol} + fullwidth + /> + <Divider /> + <KeyValueInfo + keyText={'Total unstaked ' + (isTimelocked ? 'Timelocked' : 'IOTA')} + value={stakedAndRewards} + supportingLabel={symbol} + fullwidth + /> + </div> + </Panel> + ); +} diff --git a/apps/wallet-dashboard/components/SupplyIncreaseVestingOverview.tsx b/apps/wallet-dashboard/components/SupplyIncreaseVestingOverview.tsx new file mode 100644 index 00000000000..7608bda1a3a --- /dev/null +++ b/apps/wallet-dashboard/components/SupplyIncreaseVestingOverview.tsx @@ -0,0 +1,151 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useCurrentAccount, useIotaClient } from '@iota/dapp-kit'; +import { useGetSupplyIncreaseVestingObjects } from '@/hooks'; +import { + ButtonType, + Card, + CardAction, + CardActionType, + CardBody, + CardType, + LabelText, + LabelTextSize, + Panel, + Title, +} from '@iota/apps-ui-kit'; +import { StakeDialog, useStakeDialog } from './Dialogs'; +import { TIMELOCK_IOTA_TYPE, useCountdownByTimestamp, useFormatCoin } from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import SvgClock from '@iota/ui-icons/src/Clock'; +import { useQueryClient } from '@tanstack/react-query'; + +export function SupplyIncreaseVestingOverview() { + const account = useCurrentAccount(); + const address = account?.address || ''; + const iotaClient = useIotaClient(); + const queryClient = useQueryClient(); + const { + nextPayout, + supplyIncreaseVestingSchedule, + supplyIncreaseVestingMapped, + supplyIncreaseVestingStakedMapped, + } = useGetSupplyIncreaseVestingObjects(address); + + const { + stakeDialogView, + setStakeDialogView, + selectedStake, + selectedValidator, + setSelectedValidator, + handleCloseStakeDialog, + handleNewStake, + } = useStakeDialog(); + + const formattedLastPayoutExpirationTime = useCountdownByTimestamp( + Number(nextPayout?.expirationTimestampMs), + { showSeconds: false, showMinutes: false }, + ); + const [formattedNextPayout, nextPayoutSymbol, nextPayoutResult] = useFormatCoin( + nextPayout?.amount, + IOTA_TYPE_ARG, + ); + + const [formattedAvailableStaking, availableStakingSymbol] = useFormatCoin( + supplyIncreaseVestingSchedule.availableStaking, + IOTA_TYPE_ARG, + ); + + const showSupplyIncreaseVestingOverview = + supplyIncreaseVestingMapped.length > 0 || supplyIncreaseVestingStakedMapped.length > 0; + + function handleOnSuccess(digest: string): void { + iotaClient + .waitForTransaction({ + digest, + }) + .then(() => { + queryClient.invalidateQueries({ + queryKey: ['get-timelocked-staked-objects', account?.address], + }); + queryClient.invalidateQueries({ + queryKey: [ + 'get-all-owned-objects', + account?.address, + { + StructType: TIMELOCK_IOTA_TYPE, + }, + ], + }); + }); + } + + return showSupplyIncreaseVestingOverview ? ( + <div style={{ gridArea: 'vesting' }} className="with-vesting flex grow overflow-hidden"> + <Panel> + <Title title="Vesting" /> + <div className="flex h-full w-full items-center gap-md p-md--rs"> + <div className="w-1/2"> + <Card type={CardType.Filled}> + <CardBody + title="" + subtitle={ + <LabelText + size={LabelTextSize.Large} + label="Next reward" + text={ + nextPayoutResult.isPending + ? '-' + : `${formattedNextPayout} ` + } + supportingLabel={nextPayoutSymbol} + /> + } + /> + <CardAction + type={CardActionType.Button} + buttonType={ButtonType.Ghost} + title={formattedLastPayoutExpirationTime} + icon={<SvgClock />} + /> + </Card> + </div> + <div className="w-1/2"> + <Card type={CardType.Filled}> + <CardBody + title="" + subtitle={ + <LabelText + size={LabelTextSize.Large} + label="Available for staking" + text={formattedAvailableStaking} + supportingLabel={availableStakingSymbol} + /> + } + /> + <CardAction + type={CardActionType.Button} + buttonType={ButtonType.Primary} + title={'Stake'} + onClick={() => handleNewStake()} + buttonDisabled={!supplyIncreaseVestingSchedule.availableStaking} + /> + </Card> + </div> + </div> + </Panel> + <StakeDialog + isTimelockedStaking={true} + stakedDetails={selectedStake} + onSuccess={handleOnSuccess} + handleClose={handleCloseStakeDialog} + view={stakeDialogView} + setView={setStakeDialogView} + selectedValidator={selectedValidator} + setSelectedValidator={setSelectedValidator} + maxStakableTimelockedAmount={BigInt(supplyIncreaseVestingSchedule.availableStaking)} + /> + </div> + ) : null; +} diff --git a/apps/wallet-dashboard/components/VirtualList.tsx b/apps/wallet-dashboard/components/VirtualList.tsx index 49178382bfc..58dc598a364 100644 --- a/apps/wallet-dashboard/components/VirtualList.tsx +++ b/apps/wallet-dashboard/components/VirtualList.tsx @@ -5,6 +5,7 @@ import React, { ReactNode, useEffect } from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; +import clsx from 'clsx'; interface VirtualListProps<T> { items: T[]; @@ -14,6 +15,8 @@ interface VirtualListProps<T> { estimateSize: (index: number) => number; render: (item: T, index: number) => ReactNode; onClick?: (item: T) => void; + heightClassName?: string; + overflowClassName?: string; } function VirtualList<T>({ @@ -24,6 +27,8 @@ function VirtualList<T>({ estimateSize, render, onClick, + heightClassName = 'h-fit', + overflowClassName, }: VirtualListProps<T>): JSX.Element { const containerRef = React.useRef<HTMLDivElement | null>(null); const virtualizer = useVirtualizer({ @@ -51,17 +56,13 @@ function VirtualList<T>({ if (lastItem.index >= items.length - 1 && hasNextPage && !isFetchingNextPage) { fetchNextPage(); } - }, [ - hasNextPage, - fetchNextPage, - items.length, - isFetchingNextPage, - virtualizer, - virtualizer.getVirtualItems(), - ]); + }, [hasNextPage, fetchNextPage, items.length, isFetchingNextPage, virtualizer, virtualItems]); return ( - <div className="relative h-full w-full" ref={containerRef}> + <div + className={clsx('relative w-full', heightClassName, overflowClassName)} + ref={containerRef} + > <div style={{ height: `${virtualizer.getTotalSize()}px`, @@ -69,27 +70,30 @@ function VirtualList<T>({ position: 'relative', }} > - {virtualItems.map((virtualItem) => ( - <div - key={virtualItem.key} - className={`absolute w-full ${onClick ? 'cursor-pointer' : ''}`} - style={{ - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: `${virtualItem.size}px`, - transform: `translateY(${virtualItem.start}px)`, - }} - onClick={() => onClick && onClick(items[virtualItem.index])} - > - {virtualItem.index > items.length - 1 - ? hasNextPage - ? 'Loading more...' - : 'Nothing more to load' - : render(items[virtualItem.index], virtualItem.index)} - </div> - ))} + {virtualItems.map((virtualItem) => { + const item = items[virtualItem.index]; + return ( + <div + key={virtualItem.key} + className={`absolute w-full ${onClick ? 'cursor-pointer' : ''}`} + style={{ + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: `${virtualItem.size}px`, + transform: `translateY(${virtualItem.start}px)`, + }} + onClick={() => onClick && onClick(item)} + > + {virtualItem.index > items.length - 1 + ? hasNextPage + ? 'Loading more...' + : 'Nothing more to load' + : render(item, virtualItem.index)} + </div> + ); + })} </div> </div> ); diff --git a/apps/wallet-dashboard/components/account-balance/AccountBalance.tsx b/apps/wallet-dashboard/components/account-balance/AccountBalance.tsx index 151a46d473f..9bb3f297b27 100644 --- a/apps/wallet-dashboard/components/account-balance/AccountBalance.tsx +++ b/apps/wallet-dashboard/components/account-balance/AccountBalance.tsx @@ -22,11 +22,11 @@ export function AccountBalance() { const [isSendTokenDialogOpen, setIsSendTokenDialogOpen] = useState(false); const explorerLink = `${explorer}/address/${address}`; - function openSendTokenPopup(): void { + function openSendTokenDialog(): void { setIsSendTokenDialogOpen(true); } - function openReceiveTokenPopup(): void { + function openReceiveTokenDialog(): void { setIsReceiveDialogOpen(true); } @@ -56,7 +56,7 @@ export function AccountBalance() { </span> <div className="flex w-full max-w-56 gap-xs"> <Button - onClick={openSendTokenPopup} + onClick={openSendTokenDialog} text="Send" size={ButtonSize.Small} disabled={!address} @@ -64,7 +64,7 @@ export function AccountBalance() { fullWidth /> <Button - onClick={openReceiveTokenPopup} + onClick={openReceiveTokenDialog} type={ButtonType.Secondary} text="Receive" size={ButtonSize.Small} diff --git a/apps/wallet-dashboard/components/coins/MyCoins.tsx b/apps/wallet-dashboard/components/coins/MyCoins.tsx index fc441d76148..411d7cdc702 100644 --- a/apps/wallet-dashboard/components/coins/MyCoins.tsx +++ b/apps/wallet-dashboard/components/coins/MyCoins.tsx @@ -71,9 +71,9 @@ function MyCoins(): React.JSX.Element { return ( <Panel> - <div className="flex w-full flex-col"> + <div className="flex h-full w-full flex-col"> <Title title="My Coins" /> - <div className="px-sm pb-md pt-sm"> + <div className="px-sm py-sm"> <div className="inline-flex"> <SegmentedButton type={SegmentedButtonType.Filled}> {TOKEN_CATEGORIES.map(({ label, value }) => { @@ -96,37 +96,35 @@ function MyCoins(): React.JSX.Element { })} </SegmentedButton> </div> - <div className="pb-md pt-sm"> - {[TokenCategory.All, TokenCategory.Recognized].includes( - selectedTokenCategory, - ) && - recognized?.map((coin, index) => { - return ( - <CoinItem - key={index} - coinType={coin.coinType} - balance={BigInt(coin.totalBalance)} - onClick={() => openSendTokenDialog(coin)} - icon={ - <RecognizedBadge className="h-4 w-4 text-primary-40" /> - } - /> - ); - })} - {[TokenCategory.All, TokenCategory.Unrecognized].includes( - selectedTokenCategory, - ) && - unrecognized?.map((coin, index) => { - return ( - <CoinItem - key={index} - coinType={coin.coinType} - balance={BigInt(coin.totalBalance)} - onClick={() => openSendTokenDialog(coin)} - /> - ); - })} - </div> + </div> + <div className="h-full overflow-y-auto px-sm pb-md pt-sm"> + {[TokenCategory.All, TokenCategory.Recognized].includes( + selectedTokenCategory, + ) && + recognized?.map((coin, index) => { + return ( + <CoinItem + key={index} + coinType={coin.coinType} + balance={BigInt(coin.totalBalance)} + onClick={() => openSendTokenDialog(coin)} + icon={<RecognizedBadge className="h-4 w-4 text-primary-40" />} + /> + ); + })} + {[TokenCategory.All, TokenCategory.Unrecognized].includes( + selectedTokenCategory, + ) && + unrecognized?.map((coin, index) => { + return ( + <CoinItem + key={index} + coinType={coin.coinType} + balance={BigInt(coin.totalBalance)} + onClick={() => openSendTokenDialog(coin)} + /> + ); + })} </div> </div> {selectedCoin && activeAccountAddress && ( diff --git a/apps/wallet-dashboard/components/index.ts b/apps/wallet-dashboard/components/index.ts index 37ff834be4f..fcccf7b3a95 100644 --- a/apps/wallet-dashboard/components/index.ts +++ b/apps/wallet-dashboard/components/index.ts @@ -13,7 +13,6 @@ export * from './PaginationOptions'; export * from './account-balance/AccountBalance'; export * from './coins'; -export * from './Popup'; export * from './AppList'; export * from './Cards'; export * from './Buttons'; @@ -23,7 +22,10 @@ export * from './ExplorerLink'; export * from './Dialogs'; export * from './ValidatorStakingData'; export * from './tiles'; +export * from './migration'; export * from './Toaster'; export * from './Banner'; +export * from './StakeRewardsPanel'; export * from './MigrationOverview'; +export * from './SupplyIncreaseVestingOverview'; export * from './staked-timelock-object'; diff --git a/apps/wallet-dashboard/components/migration/MigrationObjectLoading.tsx b/apps/wallet-dashboard/components/migration/MigrationObjectLoading.tsx new file mode 100644 index 00000000000..297d1a8fa81 --- /dev/null +++ b/apps/wallet-dashboard/components/migration/MigrationObjectLoading.tsx @@ -0,0 +1,27 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Card, CardImage, ImageShape, Skeleton } from '@iota/apps-ui-kit'; + +export function MigrationObjectLoading() { + return ( + <div className="flex h-full max-h-full w-full flex-col overflow-hidden"> + {new Array(10).fill(0).map((_, index) => ( + <Card key={index}> + <CardImage shape={ImageShape.SquareRounded}> + <div className="h-10 w-10 animate-pulse bg-neutral-90 dark:bg-neutral-12" /> + <Skeleton widthClass="w-10" heightClass="h-10" isRounded={false} /> + </CardImage> + <div className="flex flex-col gap-y-xs"> + <Skeleton widthClass="w-40" heightClass="h-3.5" /> + <Skeleton widthClass="w-32" heightClass="h-3" hasSecondaryColors /> + </div> + <div className="ml-auto flex flex-col gap-y-xs"> + <Skeleton widthClass="w-20" heightClass="h-3.5" /> + <Skeleton widthClass="w-16" heightClass="h-3" hasSecondaryColors /> + </div> + </Card> + ))} + </div> + ); +} diff --git a/apps/wallet-dashboard/components/migration/MigrationObjectsPanel.tsx b/apps/wallet-dashboard/components/migration/MigrationObjectsPanel.tsx new file mode 100644 index 00000000000..a3864b329cf --- /dev/null +++ b/apps/wallet-dashboard/components/migration/MigrationObjectsPanel.tsx @@ -0,0 +1,115 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +'use client'; + +import { useGroupedMigrationObjectsByExpirationDate } from '@/hooks'; +import { + STARDUST_MIGRATABLE_OBJECTS_FILTER_LIST, + STARDUST_TIMELOCKED_OBJECTS_FILTER_LIST, +} from '@/lib/constants'; +import { StardustOutputDetailsFilter } from '@/lib/enums'; +import { + Button, + ButtonType, + Chip, + InfoBox, + InfoBoxStyle, + InfoBoxType, + Panel, + Title, +} from '@iota/apps-ui-kit'; +import type { IotaObjectData } from '@iota/iota-sdk/client'; +import { Close, Warning } from '@iota/ui-icons'; +import clsx from 'clsx'; +import { useState } from 'react'; +import { MigrationObjectDetailsCard } from './migration-object-details-card'; +import VirtualList from '../VirtualList'; +import { filterMigrationObjects } from '@/lib/utils'; +import { MigrationObjectLoading } from './MigrationObjectLoading'; + +const FILTERS = { + migratable: STARDUST_MIGRATABLE_OBJECTS_FILTER_LIST, + timelocked: STARDUST_TIMELOCKED_OBJECTS_FILTER_LIST, +}; + +interface MigrationObjectsPanelProps { + selectedObjects: IotaObjectData[]; + onClose: () => void; + isTimelocked: boolean; +} + +export function MigrationObjectsPanel({ + selectedObjects, + onClose, + isTimelocked, +}: MigrationObjectsPanelProps): React.JSX.Element { + const [stardustOutputDetailsFilter, setStardustOutputDetailsFilter] = + useState<StardustOutputDetailsFilter>(StardustOutputDetailsFilter.All); + + const { + data: resolvedObjects = [], + isLoading, + error: isErrored, + } = useGroupedMigrationObjectsByExpirationDate(selectedObjects, isTimelocked); + + const filteredObjects = filterMigrationObjects(resolvedObjects, stardustOutputDetailsFilter); + + const filters = isTimelocked ? FILTERS.timelocked : FILTERS.migratable; + const isHidden = selectedObjects.length === 0; + + return ( + <div className={clsx('flex h-full min-h-0 w-2/3 flex-col', isHidden && 'hidden')}> + <Panel> + <Title + title="Details" + trailingElement={ + <Button icon={<Close />} type={ButtonType.Ghost} onClick={onClose} /> + } + /> + <div className="flex min-h-0 flex-1 flex-col px-md--rs"> + <div className="flex flex-row gap-xs py-xs"> + {filters.map((filter) => ( + <Chip + key={filter} + label={filter} + onClick={() => setStardustOutputDetailsFilter(filter)} + selected={stardustOutputDetailsFilter === filter} + /> + ))} + </div> + <div className="flex min-h-0 flex-col py-sm"> + <div className="h-full flex-1 overflow-auto"> + {isLoading && <MigrationObjectLoading />} + {isErrored && !isLoading && ( + <div className="flex h-full max-h-full w-full flex-col items-center"> + <InfoBox + title="Error" + supportingText="Failed to load stardust objects" + style={InfoBoxStyle.Elevated} + type={InfoBoxType.Error} + icon={<Warning />} + /> + </div> + )} + {!isLoading && !isErrored && ( + <VirtualList + heightClassName="h-[600px]" + overflowClassName="overflow-y-auto" + items={filteredObjects} + estimateSize={() => 58} + render={(migrationObject) => ( + <MigrationObjectDetailsCard + migrationObject={migrationObject} + isTimelocked={isTimelocked} + /> + )} + /> + )} + </div> + </div> + </div> + </Panel> + </div> + ); +} diff --git a/apps/wallet-dashboard/components/migration/index.ts b/apps/wallet-dashboard/components/migration/index.ts new file mode 100644 index 00000000000..2690e18d11b --- /dev/null +++ b/apps/wallet-dashboard/components/migration/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './MigrationObjectsPanel'; +export * from './MigrationObjectLoading'; diff --git a/apps/wallet-dashboard/components/migration/migration-object-details-card/MigrationObjectDetailsCard.tsx b/apps/wallet-dashboard/components/migration/migration-object-details-card/MigrationObjectDetailsCard.tsx new file mode 100644 index 00000000000..599e58dbd21 --- /dev/null +++ b/apps/wallet-dashboard/components/migration/migration-object-details-card/MigrationObjectDetailsCard.tsx @@ -0,0 +1,155 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { ExternalImage } from '@/components'; +import { useGetCurrentEpochStartTimestamp } from '@/hooks'; +import { useGetCurrentEpochEndTimestamp } from '@/hooks/useGetCurrentEpochEndTimestamp'; +import { MIGRATION_OBJECT_WITHOUT_UC_KEY } from '@/lib/constants'; +import { CommonMigrationObjectType } from '@/lib/enums'; +import { ResolvedObjectTypes } from '@/lib/types'; +import { Card, CardBody, CardImage, ImageShape, LabelText, LabelTextSize } from '@iota/apps-ui-kit'; +import { MILLISECONDS_PER_SECOND, TimeUnit, useFormatCoin, useTimeAgo } from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { Assets, DataStack, IotaLogoMark } from '@iota/ui-icons'; +import { useState } from 'react'; + +interface MigrationObjectDetailsCardProps { + migrationObject: ResolvedObjectTypes; + isTimelocked: boolean; +} +export function MigrationObjectDetailsCard({ + migrationObject: { unlockConditionTimestamp, ...migrationObject }, + isTimelocked: isTimelocked, +}: MigrationObjectDetailsCardProps) { + const coinType = 'coinType' in migrationObject ? migrationObject.coinType : IOTA_TYPE_ARG; + const [balance, token] = useFormatCoin(migrationObject.balance, coinType); + + switch (migrationObject.commonObjectType) { + case CommonMigrationObjectType.Basic: + return ( + <MigrationObjectCard + title={`${balance} ${token}`} + subtitle="IOTA Tokens" + unlockConditionTimestamp={unlockConditionTimestamp} + image={<IotaLogoMark />} + isTimelocked={isTimelocked} + /> + ); + case CommonMigrationObjectType.Nft: + return ( + <MigrationObjectCard + title={migrationObject.name} + subtitle="Visual Assets" + unlockConditionTimestamp={unlockConditionTimestamp} + image={ + <ExternalImageWithFallback + src={migrationObject.image_url} + alt={migrationObject.name} + fallback={<Assets />} + /> + } + isTimelocked={isTimelocked} + /> + ); + case CommonMigrationObjectType.NativeToken: + return ( + <MigrationObjectCard + isTimelocked={isTimelocked} + title={`${balance} ${token}`} + subtitle="Native Tokens" + unlockConditionTimestamp={unlockConditionTimestamp} + image={<DataStack />} + /> + ); + default: + return null; + } +} + +interface ExternalImageWithFallbackProps { + src: string; + alt: string; + fallback: React.ReactNode; +} +function ExternalImageWithFallback({ src, alt, fallback }: ExternalImageWithFallbackProps) { + const [errored, setErrored] = useState(false); + function handleError() { + setErrored(true); + } + return !errored ? <ExternalImage src={src} alt={alt} onError={handleError} /> : fallback; +} + +interface MigrationObjectCardProps { + title: string; + subtitle: string; + unlockConditionTimestamp: string; + isTimelocked: boolean; + image?: React.ReactNode; +} + +function MigrationObjectCard({ + title, + subtitle, + unlockConditionTimestamp, + isTimelocked, + image, +}: MigrationObjectCardProps) { + const hasUnlockConditionTimestamp = + unlockConditionTimestamp !== MIGRATION_OBJECT_WITHOUT_UC_KEY; + return ( + <Card> + <CardImage shape={ImageShape.SquareRounded}>{image}</CardImage> + <CardBody title={title} subtitle={subtitle} /> + {hasUnlockConditionTimestamp && ( + <UnlockConditionLabel + groupKey={unlockConditionTimestamp} + isTimelocked={isTimelocked} + /> + )} + </Card> + ); +} + +interface UnlockConditionLabelProps { + groupKey: string; + isTimelocked: boolean; +} +function UnlockConditionLabel({ groupKey, isTimelocked: isTimelocked }: UnlockConditionLabelProps) { + const { data: currentEpochStartTimestampMs, isLoading: isLoadingEpochStart } = + useGetCurrentEpochStartTimestamp(); + const { data: currentEpochEndTimestampMs, isLoading: isLoadingEpochEnd } = + useGetCurrentEpochEndTimestamp(); + + const epochEndMs = currentEpochEndTimestampMs ?? 0; + const epochStartMs = currentEpochStartTimestampMs ?? '0'; + const currentDateMs = Date.now(); + + const unlockConditionTimestampMs = parseInt(groupKey) * MILLISECONDS_PER_SECOND; + const isFromPreviousEpoch = + !isLoadingEpochStart && unlockConditionTimestampMs < parseInt(epochStartMs); + // TODO: https://github.com/iotaledger/iota/issues/4369 + const isInAFutureEpoch = !isLoadingEpochEnd && unlockConditionTimestampMs > epochEndMs; + + const outputTimestampMs = isInAFutureEpoch ? unlockConditionTimestampMs : epochEndMs; + + const timeLabel = useTimeAgo({ + timeFrom: outputTimestampMs, + shortedTimeLabel: true, + shouldEnd: true, + maxTimeUnit: TimeUnit.ONE_DAY, + }); + + const showLabel = !isFromPreviousEpoch && outputTimestampMs > currentDateMs; + + return ( + <div className="ml-auto h-full whitespace-nowrap"> + {showLabel && ( + <LabelText + size={LabelTextSize.Small} + text={timeLabel} + label={isTimelocked ? 'Unlocks in' : 'Expires in'} + /> + )} + </div> + ); +} diff --git a/apps/wallet-dashboard/components/migration/migration-object-details-card/index.ts b/apps/wallet-dashboard/components/migration/migration-object-details-card/index.ts new file mode 100644 index 00000000000..d7097a997c2 --- /dev/null +++ b/apps/wallet-dashboard/components/migration/migration-object-details-card/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './MigrationObjectDetailsCard'; diff --git a/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx b/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx index 8696705bc12..f7e21915a3a 100644 --- a/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx +++ b/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx @@ -27,11 +27,11 @@ export function StakedTimelockObject({ // TODO probably we could calculate estimated reward on grouping stage. const summary = timelockedStakedObject.stakes.reduce( (acc, stake) => { - const estimatedReward = stake.status === 'Active' ? stake.estimatedReward : 0; + const estimatedReward = stake.status === 'Active' ? BigInt(stake.estimatedReward) : 0n; return { principal: BigInt(stake.principal) + acc.principal, - estimatedReward: BigInt(estimatedReward) + acc.estimatedReward, + estimatedReward: estimatedReward + acc.estimatedReward, stakeRequestEpoch: stake.stakeRequestEpoch, }; }, diff --git a/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx b/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx index 828abc974ab..573a254a3d2 100644 --- a/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx +++ b/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx @@ -35,7 +35,6 @@ export function StartStaking() { {isDialogStakeOpen && stakeDialogView && ( <StakeDialog stakedDetails={selectedStake} - isOpen={isDialogStakeOpen} handleClose={handleCloseStakeDialog} view={stakeDialogView} setView={setStakeDialogView} diff --git a/apps/wallet-dashboard/components/staking-overview/index.ts b/apps/wallet-dashboard/components/staking-overview/index.ts index 88d8d1794ef..a2fdb941d58 100644 --- a/apps/wallet-dashboard/components/staking-overview/index.ts +++ b/apps/wallet-dashboard/components/staking-overview/index.ts @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 export * from './StakingOverview'; +export * from './StartStaking'; diff --git a/apps/wallet-dashboard/components/transactions/TransactionAmount.tsx b/apps/wallet-dashboard/components/transactions/TransactionAmount.tsx index ae7654b0273..498ebc19522 100644 --- a/apps/wallet-dashboard/components/transactions/TransactionAmount.tsx +++ b/apps/wallet-dashboard/components/transactions/TransactionAmount.tsx @@ -5,7 +5,7 @@ import { useFormatCoin } from '@iota/core'; interface TransactionAmountProps { - amount: string | number; + amount: string | number | bigint; coinType: string; label: string; approximation?: boolean; diff --git a/apps/wallet-dashboard/components/transactions/TransactionTile.tsx b/apps/wallet-dashboard/components/transactions/TransactionTile.tsx index 405a02e76c6..0c76c27ceb1 100644 --- a/apps/wallet-dashboard/components/transactions/TransactionTile.tsx +++ b/apps/wallet-dashboard/components/transactions/TransactionTile.tsx @@ -6,7 +6,6 @@ import React, { useState } from 'react'; import TransactionIcon from './TransactionIcon'; import formatTimestamp from '@/lib/utils/time'; -import { ExplorerLink } from '@/components'; import { ExtendedTransaction, TransactionState } from '@/lib/interfaces'; import { Card, @@ -18,21 +17,12 @@ import { CardAction, CardActionType, Dialog, - Header, - LoadingIndicator, } from '@iota/apps-ui-kit'; -import { - useFormatCoin, - getLabel, - useTransactionSummary, - ViewTxnOnExplorerButton, - ExplorerLinkType, - TransactionReceipt, -} from '@iota/core'; +import { useFormatCoin, getLabel, useTransactionSummary } from '@iota/core'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { useCurrentAccount } from '@iota/dapp-kit'; -import { DialogLayout, DialogLayoutBody, DialogLayoutFooter } from '../Dialogs/layout'; -import { Validator } from '../Dialogs/Staking/views/Validator'; +import { TransactionDetailsLayout } from '../Dialogs/transaction/TransactionDetailsLayout'; +import { DialogLayout } from '../Dialogs/layout'; interface TransactionTileProps { transaction: ExtendedTransaction; @@ -85,53 +75,14 @@ export function TransactionTile({ transaction }: TransactionTileProps): JSX.Elem } /> </Card> - <ActivityDetailsDialog transaction={transaction} open={open} setOpen={setOpen} /> - </> - ); -} - -interface ActivityDetailsDialogProps { - transaction: ExtendedTransaction; - open: boolean; - setOpen: (open: boolean) => void; -} -function ActivityDetailsDialog({ - transaction, - open, - setOpen, -}: ActivityDetailsDialogProps): React.JSX.Element { - const address = useCurrentAccount()?.address ?? ''; - - const summary = useTransactionSummary({ - transaction: transaction.raw, - currentAddress: address, - recognizedPackagesList: [], - }); - - if (!summary) return <LoadingIndicator />; - - return ( - <Dialog open={open} onOpenChange={setOpen}> - <DialogLayout> - <Header title="Transaction" onClose={() => setOpen(false)} /> - <DialogLayoutBody> - <TransactionReceipt - txn={transaction.raw} - activeAddress={address} - summary={summary} - renderExplorerLink={ExplorerLink} - renderValidatorLogo={Validator} + <Dialog open={open} onOpenChange={setOpen}> + <DialogLayout> + <TransactionDetailsLayout + transaction={transaction} + onClose={() => setOpen(false)} /> - </DialogLayoutBody> - <DialogLayoutFooter> - <ExplorerLink - type={ExplorerLinkType.Transaction} - transactionID={transaction.raw.digest} - > - <ViewTxnOnExplorerButton digest={transaction.raw.digest} /> - </ExplorerLink> - </DialogLayoutFooter> - </DialogLayout> - </Dialog> + </DialogLayout> + </Dialog> + </> ); } diff --git a/apps/wallet-dashboard/components/transactions/TransactionsOverview.tsx b/apps/wallet-dashboard/components/transactions/TransactionsOverview.tsx index a590e3abcdb..fcaf74eddaa 100644 --- a/apps/wallet-dashboard/components/transactions/TransactionsOverview.tsx +++ b/apps/wallet-dashboard/components/transactions/TransactionsOverview.tsx @@ -9,7 +9,7 @@ function TransactionsOverview() { return ( <Panel> <Title title="Activity" /> - <div className="px-sm pb-md pt-sm"> + <div className="h-full overflow-y-auto px-sm pb-md pt-sm"> <TransactionsList /> </div> </Panel> diff --git a/apps/wallet-dashboard/contexts/PopupContext.ts b/apps/wallet-dashboard/contexts/PopupContext.ts deleted file mode 100644 index ced7b13cc33..00000000000 --- a/apps/wallet-dashboard/contexts/PopupContext.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { ReactNode, createContext } from 'react'; - -export interface PopupManager { - popups: ReactNode[]; - openPopup: (content: ReactNode) => void; - closePopup: () => void; -} - -export const PopupContext = createContext<PopupManager>({} as PopupManager); diff --git a/apps/wallet-dashboard/hooks/index.ts b/apps/wallet-dashboard/hooks/index.ts index 77ea2304aa4..5ad872afeed 100644 --- a/apps/wallet-dashboard/hooks/index.ts +++ b/apps/wallet-dashboard/hooks/index.ts @@ -1,8 +1,7 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './useUnstakeTransaction'; -export * from './usePopups'; +export * from './useNewUnstakeTransaction'; export * from './useNewStakeTransaction'; export * from './useNotifications'; export * from './useSendCoinTransaction'; @@ -11,3 +10,6 @@ export * from './useGetCurrentEpochStartTimestamp'; export * from './useTimelockedUnstakeTransaction'; export * from './useExplorerLinkGetter'; export * from './useGetStardustMigratableObjects'; +export * from './useGetSupplyIncreaseVestingObjects'; +export * from './useGroupedMigrationObjectsByExpirationDate'; +export * from './useTransferTransaction'; diff --git a/apps/wallet-dashboard/hooks/useGetCurrentEpochEndTimestamp.ts b/apps/wallet-dashboard/hooks/useGetCurrentEpochEndTimestamp.ts new file mode 100644 index 00000000000..69217c4becf --- /dev/null +++ b/apps/wallet-dashboard/hooks/useGetCurrentEpochEndTimestamp.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useIotaClient } from '@iota/dapp-kit'; +import { useQuery } from '@tanstack/react-query'; + +export function useGetCurrentEpochEndTimestamp() { + const client = useIotaClient(); + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: ['current-epoch-end-timestamp'], + queryFn: async () => { + const iotaSystemState = await client.getLatestIotaSystemState(); + const epochStart = parseInt(iotaSystemState.epochStartTimestampMs); + const epochDuration = parseInt(iotaSystemState.epochDurationMs); + const epochEnd = epochStart + epochDuration; + return epochEnd; + }, + staleTime: 10 * 60 * 1000, // 10 minutes + }); +} diff --git a/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts b/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts index 7a5b40ce140..e17bb3d6b3b 100644 --- a/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts +++ b/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts @@ -1,21 +1,17 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { useQuery } from '@tanstack/react-query'; import { useGetCurrentEpochStartTimestamp } from '@/hooks'; import { groupStardustObjectsByMigrationStatus } from '@/lib/utils'; import { STARDUST_BASIC_OUTPUT_TYPE, STARDUST_NFT_OUTPUT_TYPE, + TimeUnit, useGetAllOwnedObjects, } from '@iota/core'; -import { IotaObjectData } from '@iota/iota-sdk/client'; -export function useGetStardustMigratableObjects(address: string): { - migratableBasicOutputs: IotaObjectData[]; - unmigratableBasicOutputs: IotaObjectData[]; - migratableNftOutputs: IotaObjectData[]; - unmigratableNftOutputs: IotaObjectData[]; -} { +export function useGetStardustMigratableObjects(address: string) { const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); const { data: basicOutputObjects } = useGetAllOwnedObjects(address, { StructType: STARDUST_BASIC_OUTPUT_TYPE, @@ -24,24 +20,41 @@ export function useGetStardustMigratableObjects(address: string): { StructType: STARDUST_NFT_OUTPUT_TYPE, }); - const { migratable: migratableBasicOutputs, unmigratable: unmigratableBasicOutputs } = - groupStardustObjectsByMigrationStatus( - basicOutputObjects ?? [], - Number(currentEpochMs), + return useQuery({ + queryKey: [ + 'stardust-migratable-objects', address, - ); + currentEpochMs, + basicOutputObjects, + nftOutputObjects, + ], + queryFn: () => { + const epochMs = Number(currentEpochMs) || 0; - const { migratable: migratableNftOutputs, unmigratable: unmigratableNftOutputs } = - groupStardustObjectsByMigrationStatus( - nftOutputObjects ?? [], - Number(currentEpochMs), - address, - ); + const { migratable: migratableBasicOutputs, timelocked: timelockedBasicOutputs } = + groupStardustObjectsByMigrationStatus(basicOutputObjects ?? [], epochMs, address); + + const { migratable: migratableNftOutputs, timelocked: timelockedNftOutputs } = + groupStardustObjectsByMigrationStatus(nftOutputObjects ?? [], epochMs, address); - return { - migratableBasicOutputs, - unmigratableBasicOutputs, - migratableNftOutputs, - unmigratableNftOutputs, - }; + return { + migratableBasicOutputs, + timelockedBasicOutputs, + migratableNftOutputs, + timelockedNftOutputs, + }; + }, + enabled: + !!address && + currentEpochMs !== undefined && + basicOutputObjects !== undefined && + nftOutputObjects !== undefined, + staleTime: TimeUnit.ONE_SECOND * TimeUnit.ONE_MINUTE * 5, + placeholderData: { + migratableBasicOutputs: [], + timelockedBasicOutputs: [], + migratableNftOutputs: [], + timelockedNftOutputs: [], + }, + }); } diff --git a/apps/wallet-dashboard/hooks/useGetSupplyIncreaseVestingObjects.ts b/apps/wallet-dashboard/hooks/useGetSupplyIncreaseVestingObjects.ts new file mode 100644 index 00000000000..58d19d63641 --- /dev/null +++ b/apps/wallet-dashboard/hooks/useGetSupplyIncreaseVestingObjects.ts @@ -0,0 +1,112 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useGetCurrentEpochStartTimestamp } from '@/hooks'; +import { + SupplyIncreaseVestingPayout, + SupplyIncreaseVestingPortfolio, + TimelockedObject, + VestingOverview, +} from '@/lib/interfaces'; +import { + buildSupplyIncreaseVestingSchedule, + ExtendedDelegatedTimelockedStake, + formatDelegatedTimelockedStake, + getLatestOrEarliestSupplyIncreaseVestingPayout, + getVestingOverview, + isSupplyIncreaseVestingObject, + isTimelockedUnlockable, + mapTimelockObjects, +} from '@/lib/utils'; +import { + TIMELOCK_IOTA_TYPE, + useGetAllOwnedObjects, + useGetTimelockedStakedObjects, + useUnlockTimelockedObjectsTransaction, +} from '@iota/core'; +import { Transaction } from '@iota/iota-sdk/transactions'; + +export function useGetSupplyIncreaseVestingObjects(address: string): { + nextPayout: SupplyIncreaseVestingPayout | undefined; + lastPayout: SupplyIncreaseVestingPayout | undefined; + supplyIncreaseVestingSchedule: VestingOverview; + supplyIncreaseVestingPortfolio: SupplyIncreaseVestingPortfolio | undefined; + supplyIncreaseVestingMapped: TimelockedObject[]; + supplyIncreaseVestingStakedMapped: ExtendedDelegatedTimelockedStake[]; + isTimelockedStakedObjectsLoading: boolean; + unlockAllSupplyIncreaseVesting: + | { + transactionBlock: Transaction; + } + | undefined; + refreshStakeList: () => void; +} { + const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); + + const { data: timelockedObjects, refetch: refetchGetAllOwnedObjects } = useGetAllOwnedObjects( + address || '', + { + StructType: TIMELOCK_IOTA_TYPE, + }, + ); + const { + data: timelockedStakedObjects, + isLoading: isTimelockedStakedObjectsLoading, + refetch: refetchTimelockedStakedObjects, + } = useGetTimelockedStakedObjects(address || ''); + + const supplyIncreaseVestingMapped = mapTimelockObjects(timelockedObjects || []).filter( + isSupplyIncreaseVestingObject, + ); + const supplyIncreaseVestingStakedMapped = formatDelegatedTimelockedStake( + timelockedStakedObjects || [], + ).filter(isSupplyIncreaseVestingObject); + + const supplyIncreaseVestingSchedule = getVestingOverview( + [...supplyIncreaseVestingMapped, ...supplyIncreaseVestingStakedMapped], + Number(currentEpochMs), + ); + + const nextPayout = getLatestOrEarliestSupplyIncreaseVestingPayout( + [...supplyIncreaseVestingMapped, ...supplyIncreaseVestingStakedMapped], + Number(currentEpochMs), + false, + ); + + const lastPayout = getLatestOrEarliestSupplyIncreaseVestingPayout( + [...supplyIncreaseVestingMapped, ...supplyIncreaseVestingStakedMapped], + Number(currentEpochMs), + true, + ); + + const supplyIncreaseVestingPortfolio = + lastPayout && buildSupplyIncreaseVestingSchedule(lastPayout, Number(currentEpochMs)); + + const supplyIncreaseVestingUnlocked = supplyIncreaseVestingMapped?.filter( + (supplyIncreaseVestingObject) => + isTimelockedUnlockable(supplyIncreaseVestingObject, Number(currentEpochMs)), + ); + const supplyIncreaseVestingUnlockedObjectIds: string[] = + supplyIncreaseVestingUnlocked.map((unlockedObject) => unlockedObject.id.id) || []; + const { data: unlockAllSupplyIncreaseVesting } = useUnlockTimelockedObjectsTransaction( + address || '', + supplyIncreaseVestingUnlockedObjectIds, + ); + + function refreshStakeList() { + refetchTimelockedStakedObjects(); + refetchGetAllOwnedObjects(); + } + + return { + nextPayout, + lastPayout, + supplyIncreaseVestingSchedule, + supplyIncreaseVestingPortfolio, + supplyIncreaseVestingMapped, + supplyIncreaseVestingStakedMapped, + isTimelockedStakedObjectsLoading, + unlockAllSupplyIncreaseVesting, + refreshStakeList, + }; +} diff --git a/apps/wallet-dashboard/hooks/useGroupedMigrationObjectsByExpirationDate.ts b/apps/wallet-dashboard/hooks/useGroupedMigrationObjectsByExpirationDate.ts new file mode 100644 index 00000000000..d9d96816fd2 --- /dev/null +++ b/apps/wallet-dashboard/hooks/useGroupedMigrationObjectsByExpirationDate.ts @@ -0,0 +1,59 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { ResolvedObjectTypes } from '@/lib/types'; +import { + groupMigrationObjectsByUnlockCondition, + sortStardustResolvedObjectsByExpiration, +} from '@/lib/utils'; +import { TimeUnit } from '@iota/core'; +import { useCurrentAccount, useIotaClient } from '@iota/dapp-kit'; +import { IotaObjectData } from '@iota/iota-sdk/client'; +import { useQuery } from '@tanstack/react-query'; +import { useGetCurrentEpochStartTimestamp } from './useGetCurrentEpochStartTimestamp'; +import { useGetCurrentEpochEndTimestamp } from './useGetCurrentEpochEndTimestamp'; + +export function useGroupedMigrationObjectsByExpirationDate( + objects: IotaObjectData[], + isTimelockUnlockCondition: boolean = false, +) { + const client = useIotaClient(); + const address = useCurrentAccount()?.address; + + const { data: currentEpochStartTimestampMs } = useGetCurrentEpochStartTimestamp(); + const { data: currentEpochEndTimestampMs } = useGetCurrentEpochEndTimestamp(); + + const epochStartMs = currentEpochStartTimestampMs ? parseInt(currentEpochStartTimestampMs) : 0; + const epochEndMs = currentEpochEndTimestampMs ? currentEpochEndTimestampMs : 0; + + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: [ + 'grouped-migration-objects', + objects, + address, + isTimelockUnlockCondition, + epochStartMs, + epochEndMs, + ], + queryFn: async (): Promise<ResolvedObjectTypes[]> => { + if (!client || objects.length === 0) { + return []; + } + const resolvedObjects = await groupMigrationObjectsByUnlockCondition( + objects, + client, + address, + isTimelockUnlockCondition, + ); + + return sortStardustResolvedObjectsByExpiration( + resolvedObjects, + epochStartMs, + epochEndMs, + ); + }, + enabled: !!client && objects.length > 0, + staleTime: TimeUnit.ONE_SECOND * TimeUnit.ONE_MINUTE * 5, + }); +} diff --git a/apps/wallet-dashboard/hooks/useNewStakeTransaction.ts b/apps/wallet-dashboard/hooks/useNewStakeTransaction.ts index 7bd856cf77d..3e45ccd6144 100644 --- a/apps/wallet-dashboard/hooks/useNewStakeTransaction.ts +++ b/apps/wallet-dashboard/hooks/useNewStakeTransaction.ts @@ -9,26 +9,54 @@ import { import { useIotaClient } from '@iota/dapp-kit'; import { useQuery } from '@tanstack/react-query'; -export function useNewStakeTransaction( +export function useNewStakeTransaction(validator: string, amount: bigint, senderAddress: string) { + const client = useIotaClient(); + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: ['stake-transaction', validator, amount.toString(), senderAddress], + queryFn: async () => { + const transaction = createStakeTransaction(amount, validator); + transaction.setSender(senderAddress); + await transaction.build({ client }); + return transaction; + }, + enabled: !!amount && !!validator && !!senderAddress, + gcTime: 0, + select: (transaction) => { + return { + transaction, + gasBudget: transaction.getData().gasData.budget, + }; + }, + }); +} + +export function useNewStakeTimelockedTransaction( validator: string, - amount: bigint, senderAddress: string, - isTimelockedStaking: boolean = false, - groupedTimelockObjects?: GroupedTimelockObject[], + groupedTimelockObjects: GroupedTimelockObject[], ) { + const amount = groupedTimelockObjects.reduce( + (acc, obj) => acc + (obj.totalLockedAmount - (obj.splitAmount ?? 0n)), + 0n, + ); const client = useIotaClient(); return useQuery({ // eslint-disable-next-line @tanstack/query/exhaustive-deps - queryKey: ['stake-transaction', validator, amount.toString(), senderAddress], + queryKey: [ + 'stake-timelocked-transaction', + validator, + senderAddress, + amount.toString(), + groupedTimelockObjects.length, + ], queryFn: async () => { - const transaction = isTimelockedStaking - ? createTimelockedStakeTransaction(groupedTimelockObjects || [], validator) - : createStakeTransaction(amount, validator); + const transaction = createTimelockedStakeTransaction(groupedTimelockObjects, validator); transaction.setSender(senderAddress); await transaction.build({ client }); return transaction; }, - enabled: !!amount && !!validator && !!senderAddress, + enabled: !!(validator && senderAddress && groupedTimelockObjects?.length), gcTime: 0, select: (transaction) => { return { diff --git a/apps/wallet-dashboard/hooks/useNewUnstakeTransaction.ts b/apps/wallet-dashboard/hooks/useNewUnstakeTransaction.ts new file mode 100644 index 00000000000..5704507f448 --- /dev/null +++ b/apps/wallet-dashboard/hooks/useNewUnstakeTransaction.ts @@ -0,0 +1,55 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { createTimelockedUnstakeTransaction, createUnstakeTransaction } from '@iota/core'; +import { useIotaClient } from '@iota/dapp-kit'; +import { useQuery } from '@tanstack/react-query'; + +export function useNewUnstakeTransaction(senderAddress: string, unstakeIotaId: string) { + const client = useIotaClient(); + + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: ['unstake-transaction', unstakeIotaId, senderAddress], + queryFn: async () => { + const transaction = createUnstakeTransaction(unstakeIotaId); + transaction.setSender(senderAddress); + await transaction.build({ client }); + return transaction; + }, + enabled: !!(senderAddress && unstakeIotaId), + gcTime: 0, + select: (transaction) => { + return { + transaction, + gasBudget: transaction.getData().gasData.budget, + }; + }, + }); +} + +export function useNewUnstakeTimelockedTransaction( + senderAddress: string, + timelockedUnstakeIotaIds: string[], +) { + const client = useIotaClient(); + + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: ['timelocked-unstake-transaction', timelockedUnstakeIotaIds, senderAddress], + queryFn: async () => { + const transaction = createTimelockedUnstakeTransaction(timelockedUnstakeIotaIds); + transaction.setSender(senderAddress); + await transaction.build({ client }); + return transaction; + }, + enabled: !!(senderAddress && timelockedUnstakeIotaIds?.length), + gcTime: 0, + select: (transaction) => { + return { + transaction, + gasBudget: transaction.getData().gasData.budget, + }; + }, + }); +} diff --git a/apps/wallet-dashboard/hooks/usePopups.ts b/apps/wallet-dashboard/hooks/usePopups.ts deleted file mode 100644 index b4867bbac73..00000000000 --- a/apps/wallet-dashboard/hooks/usePopups.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { PopupContext, PopupManager } from '@/contexts'; -import { useContext } from 'react'; - -export const usePopups = (): PopupManager => { - return useContext(PopupContext); -}; diff --git a/apps/wallet-dashboard/hooks/useTransferTransaction.ts b/apps/wallet-dashboard/hooks/useTransferTransaction.ts new file mode 100644 index 00000000000..ee673368ac0 --- /dev/null +++ b/apps/wallet-dashboard/hooks/useTransferTransaction.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +import { useMutation } from '@tanstack/react-query'; +import { useIotaClient, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { Transaction } from '@iota/iota-sdk/transactions'; + +export function useTransferTransactionMutation() { + const iotaClient = useIotaClient(); + const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); + + return useMutation({ + mutationFn: async (transaction: Transaction) => { + const executed = await signAndExecuteTransaction({ + transaction, + }); + + const tx = await iotaClient.waitForTransaction({ + digest: executed.digest, + }); + + return tx; + }, + }); +} diff --git a/apps/wallet-dashboard/hooks/useUnstakeTransaction.ts b/apps/wallet-dashboard/hooks/useUnstakeTransaction.ts deleted file mode 100644 index 9f8644eda4c..00000000000 --- a/apps/wallet-dashboard/hooks/useUnstakeTransaction.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { createUnstakeTransaction } from '@iota/core'; -import { useIotaClient } from '@iota/dapp-kit'; -import { useQuery } from '@tanstack/react-query'; - -export function useUnstakeTransaction(stakedIotaId: string, senderAddress: string) { - const client = useIotaClient(); - return useQuery({ - // eslint-disable-next-line @tanstack/query/exhaustive-deps - queryKey: ['unstake-transaction', stakedIotaId, senderAddress], - queryFn: async () => { - const transaction = createUnstakeTransaction(stakedIotaId); - transaction.setSender(senderAddress); - await transaction.build({ client }); - return transaction; - }, - enabled: !!stakedIotaId && !!senderAddress, - gcTime: 0, - select: (transaction) => { - return { - transaction, - gasBudget: transaction.getData().gasData.budget, - }; - }, - }); -} diff --git a/apps/wallet-dashboard/lib/constants/index.ts b/apps/wallet-dashboard/lib/constants/index.ts index b2c6ab5f6cd..30680459cef 100644 --- a/apps/wallet-dashboard/lib/constants/index.ts +++ b/apps/wallet-dashboard/lib/constants/index.ts @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 export * from './vesting.constants'; +export * from './migration.constants'; diff --git a/apps/wallet-dashboard/lib/constants/migration.constants.ts b/apps/wallet-dashboard/lib/constants/migration.constants.ts new file mode 100644 index 00000000000..1a203fc50e9 --- /dev/null +++ b/apps/wallet-dashboard/lib/constants/migration.constants.ts @@ -0,0 +1,14 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { StardustOutputDetailsFilter } from '../enums'; + +export const STARDUST_MIGRATABLE_OBJECTS_FILTER_LIST: StardustOutputDetailsFilter[] = Object.values( + StardustOutputDetailsFilter, +); + +export const STARDUST_TIMELOCKED_OBJECTS_FILTER_LIST: StardustOutputDetailsFilter[] = Object.values( + StardustOutputDetailsFilter, +).filter((element) => element !== StardustOutputDetailsFilter.WithExpiration); + +export const MIGRATION_OBJECT_WITHOUT_UC_KEY = 'no-unlock-condition-timestamp'; diff --git a/apps/wallet-dashboard/lib/constants/vesting.constants.ts b/apps/wallet-dashboard/lib/constants/vesting.constants.ts index 5e290e70e06..51ace2168a7 100644 --- a/apps/wallet-dashboard/lib/constants/vesting.constants.ts +++ b/apps/wallet-dashboard/lib/constants/vesting.constants.ts @@ -32,7 +32,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xfe755ca67e3a0714f97ec3c49cfc6f3ecdab2673d96b5840294d3a5db376c99', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1697320800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -42,7 +42,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x682d14613231dd1dde39397977cdfafb6b6263b5683b6782348c597c104b834', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1698530400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -52,7 +52,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x93f2bf2d044e45e1a85c010c22357892d1625436b8c95b26dcdb6f309319064', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1699740000000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -62,7 +62,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x44fa510ba216cd555ecd6b99d1ebd612f82e2bf421091c973bca49b064dc72b', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1700949600000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -72,7 +72,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xacd861b6dc5d108af03655a2175545ac6d432c526bcbe294b90e722fa36b459', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1702159200000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -82,7 +82,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x8f9eeb5953c77d53dcff3057619af7a29be1d9ce67bf66c86ad5309379d17e5', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1703368800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -92,7 +92,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x70b1063c1104760afc06df5217bebdf02f937e1aff51211fc0472e677ba8c74', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1704578400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -102,7 +102,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xb0aa6f655d08f630c15a2cfb4e3e13e307ce9d96c52c1e91c65a71a204819bd', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1705788000000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -112,7 +112,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x65224b9a3b9eadc55be4cb6efa363f283b924607496d60c02deef2aa6bf9e22', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1706997600000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -122,7 +122,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x68f9a2af0ebd0bcd9e3cc836ac7103670a9602e8dca8fd28e7b2b5a693898f2', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1708207200000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -132,7 +132,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x637e6b758efdb8d49ee96397ca909d579bb77b79f8b64e7e7f1af13ad4f7ce4', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1709416800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -142,7 +142,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xbd0f349c21b67faec992b6c9a1b9b6343b4ff1f2ad5f33b0b4cd0fc31be2b31', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1710626400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -152,7 +152,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xfb8c3539b22e4086bd03417027e70515e6fb6d18f366876ad5ad0d8da3bde0f', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1711836000000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -162,7 +162,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xbfb7c1a941885cc55a191e579c7c6d5dc345d6b5b9cfa439f724a343d354032', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1713045600000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -172,7 +172,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x8935a904f90e23f6f453cb0c85a03859e07f1c9e5a5d1644b2fbe7005d8e158', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1714255200000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -182,7 +182,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x73be6f8df4b73b83f8ccf909d61aabb56c56c56aa597d2806eccf3ab4fac66b', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1715464800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -192,7 +192,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x20075cc2ebd5fa6e069829e58e55e6e010ad115e8cbc48d7a3d98d079ce649a', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1716674400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -202,7 +202,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xce03433d496cb231ead90a661fe08b924eb9b0cfb43dd560ea02a8060f6afd0', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1717884000000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -212,7 +212,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xf111b8705ba276f8c6b76bdf72a4a46889cb8207cc5a80d3df0f40d9576116a', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1719093600000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -222,7 +222,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xbc27940fb9c6f96ae9e2c11ad151446e30de5281172e48aac7f600d1da92c10', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1720303200000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -232,7 +232,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x016fae8797d3d12a26e215ec1815ee8adce70bb93149b4d55eb06a81c476ff9', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1721512800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -242,7 +242,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x06f1e354ff551d76da8dc890eab728a65319defb3608991b4c70a1a2b30e8f1', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1722722400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -252,7 +252,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xc4cf3ea32480aab7d78784c6f00b9210ce0ffaabbcbb8cddd846073e7455386', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1723932000000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -262,7 +262,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x6dc10a8008855549b8d92e7704c799253a953d9835af001970426414fdd3ba7', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1725141600000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -272,7 +272,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xa5f7a66c575db3f74c5fe7043c28f7231a2127aec4dc2de88f5b9d3cf020511', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1726351200000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -282,7 +282,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xde0a4c2e0f16541983302c596339815ffa4d4743509e8115bc06fcf7f71ea8f', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1727560800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -292,7 +292,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0xccc5d23ab69789b934b9bf7f5006e43eef45c2d7a251e3eec8b7dd24bc20a07', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1728770400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -302,7 +302,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x503dc8844b0cd6e74e735433751328e8283569e81b4602aaa6941ce3fe826bb', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1729980000000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -312,7 +312,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x0fac98b5ac955644dffa0700933aababe438fae6fc58b8a4bd1f740c8aba941', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1731189600000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -322,7 +322,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x756483e3c7dd3491ea405f682df6c5dc1e4a59d8b5c9725b0d194815a25ea95', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1732399200000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -332,7 +332,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x72c4318876f51bed94c2228b395d18f5dce5f243039c7e3d8fad690dfe918fc', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1733608800000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -342,7 +342,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x37f68fd72af05b4c923268b64a0baa7511f27bc4cbd90641e444e7116f02604', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1734818400000, label: SUPPLY_INCREASE_VESTING_LABEL, @@ -352,7 +352,7 @@ export const MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS: TimelockedObject id: '0x97bedf66e48392a0b9baf8a8280e72fcce9b32ff980832edfe1a90a14ce9047', }, locked: { - value: 2_000_000_000, + value: 2_000_000_000n, }, expirationTimestampMs: 1736028000000, label: SUPPLY_INCREASE_VESTING_LABEL, diff --git a/apps/wallet-dashboard/lib/enums/commonMigrationObjectType.enums.ts b/apps/wallet-dashboard/lib/enums/commonMigrationObjectType.enums.ts new file mode 100644 index 00000000000..9395fca36b8 --- /dev/null +++ b/apps/wallet-dashboard/lib/enums/commonMigrationObjectType.enums.ts @@ -0,0 +1,8 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum CommonMigrationObjectType { + NativeToken = 'nativeToken', + Nft = 'nft', + Basic = 'basic', +} diff --git a/apps/wallet-dashboard/lib/enums/index.ts b/apps/wallet-dashboard/lib/enums/index.ts index 87f3b0e2f22..6d936d1ec45 100644 --- a/apps/wallet-dashboard/lib/enums/index.ts +++ b/apps/wallet-dashboard/lib/enums/index.ts @@ -3,3 +3,6 @@ export * from './protectedRouteTitle.enum'; export * from './assetCategory.enum'; +export * from './commonMigrationObjectType.enums'; +export * from './stardustOutputDetailsFilter.enum'; +export * from './stardustOutputMigrationStatus.enum'; diff --git a/apps/wallet-dashboard/lib/enums/stardustOutputDetailsFilter.enum.ts b/apps/wallet-dashboard/lib/enums/stardustOutputDetailsFilter.enum.ts new file mode 100644 index 00000000000..f1ff813072c --- /dev/null +++ b/apps/wallet-dashboard/lib/enums/stardustOutputDetailsFilter.enum.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum StardustOutputDetailsFilter { + All = 'All', + IOTA = 'IOTA', + NativeTokens = 'Native Tokens', + VisualAssets = 'Visual Assets', + WithExpiration = 'With Expiration', +} diff --git a/apps/wallet-dashboard/lib/enums/stardustOutputMigrationStatus.enum.ts b/apps/wallet-dashboard/lib/enums/stardustOutputMigrationStatus.enum.ts new file mode 100644 index 00000000000..29df88def12 --- /dev/null +++ b/apps/wallet-dashboard/lib/enums/stardustOutputMigrationStatus.enum.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum StardustOutputMigrationStatus { + Migratable = 'Migratable', + TimeLocked = 'TimeLocked', +} diff --git a/apps/wallet-dashboard/lib/interfaces/timelock.interface.ts b/apps/wallet-dashboard/lib/interfaces/timelock.interface.ts index 12b26daeecb..2cb58e1a988 100644 --- a/apps/wallet-dashboard/lib/interfaces/timelock.interface.ts +++ b/apps/wallet-dashboard/lib/interfaces/timelock.interface.ts @@ -6,7 +6,7 @@ export interface UID { } export interface Balance { - value: number; + value: bigint; } export interface TimelockedObject { diff --git a/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts b/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts index ba846d71e6c..4eac7bcc774 100644 --- a/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts +++ b/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts @@ -7,7 +7,7 @@ export enum SupplyIncreaseUserType { } export interface SupplyIncreaseVestingPayout { - amount: number; + amount: bigint; expirationTimestampMs: number; } diff --git a/apps/wallet-dashboard/lib/types/index.ts b/apps/wallet-dashboard/lib/types/index.ts new file mode 100644 index 00000000000..cdec0f4219f --- /dev/null +++ b/apps/wallet-dashboard/lib/types/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './stardustMigrationObjects'; diff --git a/apps/wallet-dashboard/lib/types/stardustMigrationObjects.ts b/apps/wallet-dashboard/lib/types/stardustMigrationObjects.ts new file mode 100644 index 00000000000..516cc7e2908 --- /dev/null +++ b/apps/wallet-dashboard/lib/types/stardustMigrationObjects.ts @@ -0,0 +1,33 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IotaObjectData } from '@iota/iota-sdk/client'; +import { CommonMigrationObjectType } from '../enums'; + +export type UnlockConditionTimestamp = string; + +interface CommonExpirationTypeObject { + unlockConditionTimestamp: UnlockConditionTimestamp; + output: IotaObjectData; + uniqueId: string; + balance: bigint; +} + +export interface ResolvedNativeToken extends CommonExpirationTypeObject { + name: string; + commonObjectType: CommonMigrationObjectType.NativeToken; + coinType: string; +} + +export interface ResolvedBasicObject extends CommonExpirationTypeObject { + type: string; + commonObjectType: CommonMigrationObjectType.Basic; +} + +export interface ResolvedNftObject extends CommonExpirationTypeObject { + name: string; + image_url: string; + commonObjectType: CommonMigrationObjectType.Nft; +} + +export type ResolvedObjectTypes = ResolvedBasicObject | ResolvedNftObject | ResolvedNativeToken; diff --git a/apps/wallet-dashboard/lib/utils/migration.ts b/apps/wallet-dashboard/lib/utils/migration.ts deleted file mode 100644 index ee129d4d211..00000000000 --- a/apps/wallet-dashboard/lib/utils/migration.ts +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { CommonOutputObjectWithUc } from '@iota/core'; -import { IotaObjectData } from '@iota/iota-sdk/client'; - -export type StardustMigrationGroupedObjects = { - migratable: IotaObjectData[]; - unmigratable: IotaObjectData[]; -}; - -export function groupStardustObjectsByMigrationStatus( - stardustOutputObjects: IotaObjectData[], - epochTimestamp: number, - address: string, -): StardustMigrationGroupedObjects { - const migratable: IotaObjectData[] = []; - const unmigratable: IotaObjectData[] = []; - - const epochUnix = epochTimestamp / 1000; - - for (const outputObject of stardustOutputObjects) { - const outputObjectFields = extractOutputFields(outputObject); - - if (outputObjectFields.expiration_uc) { - const unlockableAddress = - outputObjectFields.expiration_uc.fields.unix_time <= epochUnix - ? outputObjectFields.expiration_uc.fields.return_address - : outputObjectFields.expiration_uc.fields.owner; - - if (unlockableAddress !== address) { - unmigratable.push(outputObject); - continue; - } - } - if ( - outputObjectFields.timelock_uc && - outputObjectFields.timelock_uc.fields.unix_time > epochUnix - ) { - unmigratable.push(outputObject); - continue; - } - - migratable.push(outputObject); - } - - return { migratable, unmigratable }; -} - -interface MigratableObjectsData { - totalNativeTokens: number; - totalVisualAssets: number; - accumulatedIotaAmount: number; -} - -export function summarizeMigratableObjectValues({ - migratableBasicOutputs, - migratableNftOutputs, - address, -}: { - migratableBasicOutputs: IotaObjectData[]; - migratableNftOutputs: IotaObjectData[]; - address: string; -}): MigratableObjectsData { - let totalNativeTokens = 0; - let totalIotaAmount = 0; - - const totalVisualAssets = migratableNftOutputs.length; - const outputObjects = [...migratableBasicOutputs, ...migratableNftOutputs]; - - for (const output of outputObjects) { - const outputObjectFields = extractOutputFields(output); - - totalIotaAmount += parseInt(outputObjectFields.balance); - totalNativeTokens += parseInt(outputObjectFields.native_tokens.fields.size); - totalIotaAmount += extractStorageDepositReturnAmount(outputObjectFields, address) || 0; - } - - return { totalNativeTokens, totalVisualAssets, accumulatedIotaAmount: totalIotaAmount }; -} - -function extractStorageDepositReturnAmount( - { storage_deposit_return_uc }: CommonOutputObjectWithUc, - address: string, -): number | null { - if ( - storage_deposit_return_uc?.fields && - storage_deposit_return_uc?.fields.return_address === address - ) { - return parseInt(storage_deposit_return_uc?.fields.return_amount); - } - return null; -} - -function extractOutputFields(outputObject: IotaObjectData): CommonOutputObjectWithUc { - return ( - outputObject.content as unknown as { - fields: CommonOutputObjectWithUc; - } - ).fields; -} diff --git a/apps/wallet-dashboard/lib/utils/migration/filterMigrationObjectDetails.ts b/apps/wallet-dashboard/lib/utils/migration/filterMigrationObjectDetails.ts new file mode 100644 index 00000000000..3f9d54de6c8 --- /dev/null +++ b/apps/wallet-dashboard/lib/utils/migration/filterMigrationObjectDetails.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { MIGRATION_OBJECT_WITHOUT_UC_KEY } from '@/lib/constants'; +import { CommonMigrationObjectType, StardustOutputDetailsFilter } from '@/lib/enums'; +import { ResolvedObjectTypes } from '@/lib/types'; + +export function filterMigrationObjects( + objects: ResolvedObjectTypes[], + filter: StardustOutputDetailsFilter, +) { + switch (filter) { + case StardustOutputDetailsFilter.All: + return objects; + case StardustOutputDetailsFilter.IOTA: + return filterObjectByCommonOutputType(objects, CommonMigrationObjectType.Basic); + case StardustOutputDetailsFilter.VisualAssets: + return filterObjectByCommonOutputType(objects, CommonMigrationObjectType.Nft); + case StardustOutputDetailsFilter.NativeTokens: + return filterObjectByCommonOutputType(objects, CommonMigrationObjectType.NativeToken); + case StardustOutputDetailsFilter.WithExpiration: + return filterObjectsByExpiration(objects); + } +} + +function filterObjectByCommonOutputType( + objects: ResolvedObjectTypes[], + type: CommonMigrationObjectType, +) { + return objects.filter((object) => object.commonObjectType === type); +} + +function filterObjectsByExpiration(objects: ResolvedObjectTypes[]): ResolvedObjectTypes[] { + return objects.filter( + (object) => object.unlockConditionTimestamp !== MIGRATION_OBJECT_WITHOUT_UC_KEY, + ); +} diff --git a/apps/wallet-dashboard/lib/utils/migration/groupMigrationObjects.ts b/apps/wallet-dashboard/lib/utils/migration/groupMigrationObjects.ts new file mode 100644 index 00000000000..73991f4ac09 --- /dev/null +++ b/apps/wallet-dashboard/lib/utils/migration/groupMigrationObjects.ts @@ -0,0 +1,235 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { CommonMigrationObjectType } from '@/lib/enums'; +import { + ResolvedBasicObject, + ResolvedNativeToken, + ResolvedNftObject, + ResolvedObjectTypes, + UnlockConditionTimestamp, +} from '@/lib/types'; +import { + extractObjectTypeStruct, + getNativeTokensFromBag, + MILLISECONDS_PER_SECOND, + STARDUST_BASIC_OUTPUT_TYPE, + STARDUST_NFT_OUTPUT_TYPE, +} from '@iota/core'; +import { extractMigrationOutputFields, extractOwnedStorageDepositReturnAmount } from '.'; +import { IotaClient, IotaObjectData } from '@iota/iota-sdk/client'; +import { MIGRATION_OBJECT_WITHOUT_UC_KEY } from '@/lib/constants'; + +export async function groupMigrationObjectsByUnlockCondition( + objectsData: IotaObjectData[], + client: IotaClient, + currentAddress: string = '', + isTimelockUnlockCondition: boolean = false, +): Promise<ResolvedObjectTypes[]> { + const flatObjects: ResolvedObjectTypes[] = []; + const basicObjectMap: Map<string, ResolvedBasicObject> = new Map(); + const nativeTokenMap: Map<string, Map<string, ResolvedNativeToken>> = new Map(); + + const PROMISE_CHUNK_SIZE = 100; + + // Get output data in chunks of 100 + for (let i = 0; i < objectsData.length; i += PROMISE_CHUNK_SIZE) { + const chunk = objectsData.slice(i, i + PROMISE_CHUNK_SIZE); + + const promises = chunk.map(async (object) => { + const objectFields = extractMigrationOutputFields(object); + + let groupKey: string | undefined; + if (isTimelockUnlockCondition) { + const timestamp = objectFields.timelock_uc?.fields.unix_time.toString(); + groupKey = timestamp; + } else { + const timestamp = objectFields.expiration_uc?.fields.unix_time.toString(); + // Timestamp can be undefined if the object was timelocked and is now unlocked + // and it doesn't have an expiration unlock condition + groupKey = timestamp ?? MIGRATION_OBJECT_WITHOUT_UC_KEY; + } + + if (!groupKey) { + return; + } + + if (object.type === STARDUST_BASIC_OUTPUT_TYPE) { + const existing = basicObjectMap.get(groupKey); + const gasReturn = extractOwnedStorageDepositReturnAmount( + objectFields, + currentAddress, + ); + const newBalance = + (existing ? existing.balance : 0n) + + BigInt(objectFields.balance) + + (gasReturn ?? 0n); + + if (existing) { + existing.balance = newBalance; + } else { + const newBasicObject: ResolvedBasicObject = { + balance: newBalance, + unlockConditionTimestamp: groupKey, + type: object.type, + commonObjectType: CommonMigrationObjectType.Basic, + output: object, + uniqueId: objectFields.id.id, + }; + basicObjectMap.set(groupKey, newBasicObject); + flatObjects.push(newBasicObject); + } + } else if (object.type === STARDUST_NFT_OUTPUT_TYPE) { + const nftDetails = await getNftDetails(object, groupKey, client); + flatObjects.push(...nftDetails); + } + + if (!nativeTokenMap.has(groupKey)) { + nativeTokenMap.set(groupKey, new Map()); + } + + const tokenGroup = nativeTokenMap.get(groupKey)!; + const objectNativeTokens = await extractNativeTokensFromObject( + object, + client, + groupKey, + ); + + for (const token of objectNativeTokens) { + const existing = tokenGroup.get(token.name); + + if (existing) { + existing.balance += token.balance; + } else { + tokenGroup.set(token.name, token); + flatObjects.push(token); + } + } + }); + + // Wait for all promises in the chunk to resolve + await Promise.all(promises); + } + + return flatObjects; +} + +export function sortStardustResolvedObjectsByExpiration( + objects: ResolvedObjectTypes[], + currentEpochStartMs: number, + currentEpochEndMs: number, +): ResolvedObjectTypes[] { + const currentTimestampMs = Date.now(); + + return objects.sort((a, b) => { + const aIsNoExpiration = a.unlockConditionTimestamp === MIGRATION_OBJECT_WITHOUT_UC_KEY; + const bIsNoExpiration = b.unlockConditionTimestamp === MIGRATION_OBJECT_WITHOUT_UC_KEY; + + // No-expiration objects should be last + if (aIsNoExpiration && bIsNoExpiration) return 0; + if (aIsNoExpiration) return 1; + if (bIsNoExpiration) return -1; + + const aTimestampMs = parseInt(a.unlockConditionTimestamp) * MILLISECONDS_PER_SECOND; + const bTimestampMs = parseInt(b.unlockConditionTimestamp) * MILLISECONDS_PER_SECOND; + + const aIsFromPreviousEpoch = aTimestampMs < currentEpochStartMs; + const bIsFromPreviousEpoch = bTimestampMs < currentEpochStartMs; + + // Objects from a past epoch should be last (but before no-expiration objects) + if (aIsFromPreviousEpoch && bIsFromPreviousEpoch) return 0; + if (aIsFromPreviousEpoch) return 1; + if (bIsFromPreviousEpoch) return -1; + + const aIsInFutureEpoch = aTimestampMs > currentEpochEndMs; + const bIsInFutureEpoch = bTimestampMs > currentEpochEndMs; + + const aOutputTimestampMs = aIsInFutureEpoch ? aTimestampMs : currentEpochEndMs; + const bOutputTimestampMs = bIsInFutureEpoch ? bTimestampMs : currentEpochEndMs; + + // Objects closer to the calculated `outputTimestampMs` should be first + const aProximity = Math.abs(aOutputTimestampMs - currentTimestampMs); + const bProximity = Math.abs(bOutputTimestampMs - currentTimestampMs); + + return aProximity - bProximity; + }); +} + +async function getNftDetails( + object: IotaObjectData, + expirationKey: UnlockConditionTimestamp, + client: IotaClient, +): Promise<ResolvedNftObject[]> { + const objectFields = extractMigrationOutputFields(object); + const nftOutputDynamicFields = await client.getDynamicFields({ + parentId: objectFields.id.id, + }); + + const nftDetails: ResolvedNftObject[] = []; + for (const nft of nftOutputDynamicFields.data) { + const nftObject = await client.getObject({ + id: nft.objectId, + options: { showDisplay: true }, + }); + + if (!nftObject?.data?.display?.data) { + continue; + } + + nftDetails.push({ + balance: BigInt(objectFields.balance), + name: nftObject.data.display.data.name ?? '', + image_url: nftObject.data.display.data.image_url ?? '', + commonObjectType: CommonMigrationObjectType.Nft, + unlockConditionTimestamp: expirationKey, + output: object, + uniqueId: nftObject.data.objectId, + }); + } + + return nftDetails; +} + +async function extractNativeTokensFromObject( + object: IotaObjectData, + client: IotaClient, + expirationKey: UnlockConditionTimestamp, +): Promise<ResolvedNativeToken[]> { + const fields = extractMigrationOutputFields(object); + const bagId = fields.native_tokens.fields.id.id; + const bagSize = Number(fields.native_tokens.fields.size); + + const nativeTokens = bagSize > 0 ? await getNativeTokensFromBag(bagId, client) : []; + const result: ResolvedNativeToken[] = []; + + for (const nativeToken of nativeTokens) { + const nativeTokenParentId = fields.native_tokens.fields.id.id; + const objectDynamic = await client.getDynamicFieldObject({ + parentId: nativeTokenParentId, + name: nativeToken.name, + }); + + if (objectDynamic?.data?.content && 'fields' in objectDynamic.data.content) { + const nativeTokenFields = objectDynamic.data.content.fields as { + name: string; + value: string; + id: { id: string }; + }; + const tokenStruct = extractObjectTypeStruct(nativeTokenFields.name); + const tokenName = tokenStruct[2]; + const balance = BigInt(nativeTokenFields.value); + + result.push({ + name: tokenName, + balance, + coinType: nativeTokenFields.name, + unlockConditionTimestamp: expirationKey, + commonObjectType: CommonMigrationObjectType.NativeToken, + output: object, + uniqueId: nativeTokenFields.id.id, + }); + } + } + + return result; +} diff --git a/apps/wallet-dashboard/lib/utils/migration/groupStardustObjectsByMigrationStatus.ts b/apps/wallet-dashboard/lib/utils/migration/groupStardustObjectsByMigrationStatus.ts new file mode 100644 index 00000000000..b10f1cf4218 --- /dev/null +++ b/apps/wallet-dashboard/lib/utils/migration/groupStardustObjectsByMigrationStatus.ts @@ -0,0 +1,151 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { CommonOutputObjectWithUc, MILLISECONDS_PER_SECOND } from '@iota/core'; +import { IotaObjectData } from '@iota/iota-sdk/client'; + +export type StardustMigrationGroupedObjects = { + migratable: IotaObjectData[]; + timelocked: IotaObjectData[]; +}; + +export function groupStardustObjectsByMigrationStatus( + stardustOutputObjects: IotaObjectData[], + epochTimestampMs: number, + address: string, +): StardustMigrationGroupedObjects { + const migratable: IotaObjectData[] = []; + const timelocked: IotaObjectData[] = []; + + const epochUnix = epochTimestampMs / MILLISECONDS_PER_SECOND; + + for (const outputObject of stardustOutputObjects) { + const outputObjectFields = extractMigrationOutputFields(outputObject); + + if ( + outputObjectFields.timelock_uc && + outputObjectFields.timelock_uc.fields.unix_time > epochUnix + ) { + timelocked.push(outputObject); + continue; + } + + if (outputObjectFields.expiration_uc) { + const unlockableAddress = + outputObjectFields.expiration_uc.fields.unix_time <= epochUnix + ? outputObjectFields.expiration_uc.fields.return_address + : outputObjectFields.expiration_uc.fields.owner; + + if (unlockableAddress !== address) { + continue; + } + } + + migratable.push(outputObject); + } + + return { migratable, timelocked }; +} + +interface MigratableObjectsData { + totalNativeTokens: number; + totalVisualAssets: number; + totalIotaAmount: bigint; + totalNotOwnedStorageDepositReturnAmount: bigint; +} + +interface SummarizeMigrationObjectParams { + basicOutputs: IotaObjectData[] | undefined; + nftOutputs: IotaObjectData[] | undefined; + address: string; +} + +export function summarizeMigratableObjectValues({ + basicOutputs = [], + nftOutputs = [], + address, +}: SummarizeMigrationObjectParams): MigratableObjectsData { + let totalNativeTokens = 0; + let totalIotaAmount: bigint = 0n; + let totalNotOwnedStorageDepositReturnAmount: bigint = 0n; + + const totalVisualAssets = nftOutputs.length; + const outputObjects = [...basicOutputs, ...nftOutputs]; + + for (const output of outputObjects) { + const outputObjectFields = extractMigrationOutputFields(output); + + totalIotaAmount += BigInt(outputObjectFields.balance); + totalNativeTokens += parseInt(outputObjectFields.native_tokens.fields.size); + totalIotaAmount += + extractOwnedStorageDepositReturnAmount(outputObjectFields, address) || 0n; + totalNotOwnedStorageDepositReturnAmount += + extractNotOwnedStorageDepositReturnAmount(outputObjectFields, address) || 0n; + } + + return { + totalNativeTokens, + totalVisualAssets, + totalIotaAmount, + totalNotOwnedStorageDepositReturnAmount, + }; +} + +interface UnmmigratableObjectsData { + totalTimelockedObjects: number; +} + +export function summarizeTimelockedObjectValues({ + basicOutputs = [], + nftOutputs = [], +}: Omit<SummarizeMigrationObjectParams, 'address'>): UnmmigratableObjectsData { + const basicObjects = basicOutputs.length; + const nftObjects = nftOutputs.length; + let nativeTokens = 0; + + for (const output of [...basicOutputs, ...nftOutputs]) { + const outputObjectFields = extractMigrationOutputFields(output); + + nativeTokens += parseInt(outputObjectFields.native_tokens.fields.size); + } + + const totalTimelockedObjects = basicObjects + nativeTokens + nftObjects; + + return { totalTimelockedObjects }; +} + +export function extractOwnedStorageDepositReturnAmount( + { storage_deposit_return_uc }: CommonOutputObjectWithUc, + address: string, +): bigint | null { + if ( + storage_deposit_return_uc?.fields && + storage_deposit_return_uc?.fields.return_address === address + ) { + return BigInt(storage_deposit_return_uc?.fields.return_amount); + } + return null; +} + +export function extractMigrationOutputFields( + outputObject: IotaObjectData, +): CommonOutputObjectWithUc { + return ( + outputObject.content as unknown as { + fields: CommonOutputObjectWithUc; + } + ).fields; +} + +export function extractNotOwnedStorageDepositReturnAmount( + { storage_deposit_return_uc }: CommonOutputObjectWithUc, + address: string, +): bigint | null { + if ( + storage_deposit_return_uc?.fields && + storage_deposit_return_uc?.fields.return_address !== address + ) { + return BigInt(storage_deposit_return_uc?.fields.return_amount); + } + return null; +} diff --git a/apps/wallet-dashboard/lib/utils/migration/index.ts b/apps/wallet-dashboard/lib/utils/migration/index.ts new file mode 100644 index 00000000000..8dbde02f32c --- /dev/null +++ b/apps/wallet-dashboard/lib/utils/migration/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './filterMigrationObjectDetails'; +export * from './groupMigrationObjects'; +export * from './groupStardustObjectsByMigrationStatus'; diff --git a/apps/wallet-dashboard/lib/utils/timelock.ts b/apps/wallet-dashboard/lib/utils/timelock.ts index 90c77c0ba9f..f8e06340922 100644 --- a/apps/wallet-dashboard/lib/utils/timelock.ts +++ b/apps/wallet-dashboard/lib/utils/timelock.ts @@ -42,14 +42,14 @@ export function mapTimelockObjects(iotaObjects: IotaObjectData[]): TimelockedObj if (!iotaObject?.content?.dataType || iotaObject.content.dataType !== 'moveObject') { return { id: { id: '' }, - locked: { value: 0 }, + locked: { value: 0n }, expirationTimestampMs: 0, }; } const fields = iotaObject.content.fields as unknown as TimelockedIotaResponse; return { id: fields.id, - locked: { value: Number(fields.locked) }, + locked: { value: BigInt(fields.locked) }, expirationTimestampMs: Number(fields.expiration_timestamp_ms), label: fields.label, }; diff --git a/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts b/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts index e0bb1f37bd5..3612418d48b 100644 --- a/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts +++ b/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts @@ -21,10 +21,6 @@ import { const MOCKED_CURRENT_EPOCH_TIMESTAMP = Date.now() + MILLISECONDS_PER_HOUR * 6; // 6 hours later -function bigIntRound(n: number) { - return BigInt(Math.floor(n)); -} - describe('get last supply increase vesting payout', () => { it('should get the object with highest expirationTimestampMs', () => { const timelockedObjects = MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS; @@ -48,7 +44,7 @@ describe('get last supply increase vesting payout', () => { describe('get supply increase user type', () => { it('should return staker, if last payout is two years away from vesting starting year (2023)', () => { const vestingPayout: SupplyIncreaseVestingPayout = { - amount: 1000, + amount: 1000n, expirationTimestampMs: 1735689661000, // Wednesday, 1 January 2025 00:01:01 }; const userType = getSupplyIncreaseVestingUserType([vestingPayout]); @@ -57,7 +53,7 @@ describe('get supply increase user type', () => { it('should return entity, if last payout is more than two years away from vesting starting year (2023)', () => { const vestingPayout: SupplyIncreaseVestingPayout = { - amount: 1000, + amount: 1000n, expirationTimestampMs: 1798761661000, // Friday, 1 January 2027 00:01:01 }; const userType = getSupplyIncreaseVestingUserType([vestingPayout]); @@ -125,12 +121,12 @@ describe('vesting overview', () => { it('should get correct vesting overview data with timelocked objects', () => { const timelockedObjects = MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS; const lastPayout = timelockedObjects[timelockedObjects.length - 1]; - const totalAmount = bigIntRound( - (SUPPLY_INCREASE_STAKER_VESTING_DURATION * - SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR * + const totalAmount = + (BigInt(SUPPLY_INCREASE_STAKER_VESTING_DURATION) * + BigInt(SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR) * + 10n * lastPayout.locked.value) / - 0.9, - ); + 9n; const vestingOverview = getVestingOverview(timelockedObjects, Date.now()); expect(vestingOverview.totalVested).toEqual(totalAmount); @@ -145,9 +141,7 @@ describe('vesting overview', () => { const lockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() - ? acc + bigIntRound(current.amount) - : acc, + current.expirationTimestampMs > Date.now() ? acc + current.amount : acc, 0n, ); @@ -159,16 +153,12 @@ describe('vesting overview', () => { const lockedObjectsAmount = timelockedObjects.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() - ? acc + bigIntRound(current.locked.value) - : acc, + current.expirationTimestampMs > Date.now() ? acc + current.locked.value : acc, 0n, ); const unlockedObjectsAmount = timelockedObjects.reduce( (acc, current) => - current.expirationTimestampMs <= Date.now() - ? acc + bigIntRound(current.locked.value) - : acc, + current.expirationTimestampMs <= Date.now() ? acc + current.locked.value : acc, 0n, ); @@ -182,20 +172,20 @@ describe('vesting overview', () => { formatDelegatedTimelockedStake(timelockedStakedObjects); const lastPayout = extendedTimelockedStakedObjects[extendedTimelockedStakedObjects.length - 1]; - const lastPayoutValue = Number(lastPayout.principal); - const totalAmount = bigIntRound( - (SUPPLY_INCREASE_STAKER_VESTING_DURATION * - SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR * - lastPayoutValue) / - 0.9, - ); + const lastPayoutValue = BigInt(lastPayout.principal); + const totalAmount = + (BigInt(SUPPLY_INCREASE_STAKER_VESTING_DURATION) * + BigInt(SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR) * + lastPayoutValue * + 10n) / + 9n; const vestingOverview = getVestingOverview(extendedTimelockedStakedObjects, Date.now()); expect(vestingOverview.totalVested).toEqual(totalAmount); const vestingPortfolio = buildVestingPortfolio( { - amount: lastPayoutValue, + amount: BigInt(lastPayoutValue), expirationTimestampMs: Number(lastPayout.expirationTimestampMs), }, Date.now(), @@ -203,9 +193,7 @@ describe('vesting overview', () => { const lockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() - ? acc + bigIntRound(current.amount) - : acc, + current.expirationTimestampMs > Date.now() ? acc + current.amount : acc, 0n, ); @@ -215,7 +203,7 @@ describe('vesting overview', () => { let totalStaked = 0n; for (const timelockedStakedObject of timelockedStakedObjects) { const stakesAmount = timelockedStakedObject.stakes.reduce( - (acc, current) => acc + bigIntRound(Number(current.principal)), + (acc, current) => acc + BigInt(current.principal), 0n, ); totalStaked += stakesAmount; @@ -239,12 +227,12 @@ describe('vesting overview', () => { mixedObjects, MOCKED_CURRENT_EPOCH_TIMESTAMP, )!; - const totalAmount = bigIntRound( - (SUPPLY_INCREASE_STAKER_VESTING_DURATION * - SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR * + const totalAmount = + (BigInt(SUPPLY_INCREASE_STAKER_VESTING_DURATION) * + BigInt(SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR) * + 10n * lastPayout.amount) / - 0.9, - ); + 9n; const vestingOverview = getVestingOverview(mixedObjects, Date.now()); expect(vestingOverview.totalVested).toEqual(totalAmount); @@ -259,9 +247,7 @@ describe('vesting overview', () => { const lockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() - ? acc + bigIntRound(current.amount) - : acc, + current.expirationTimestampMs > Date.now() ? acc + current.amount : acc, 0n, ); @@ -269,7 +255,7 @@ describe('vesting overview', () => { expect(vestingOverview.totalUnlocked).toEqual(totalAmount - lockedAmount); const totalStaked = extendedTimelockedStakedObjects.reduce( - (acc, current) => acc + bigIntRound(Number(current.principal)), + (acc, current) => acc + BigInt(current.principal), 0n, ); @@ -278,16 +264,12 @@ describe('vesting overview', () => { const timelockObjects = mixedObjects.filter(isTimelockedObject); const availableClaiming = timelockObjects.reduce( (acc, current) => - current.expirationTimestampMs <= Date.now() - ? acc + bigIntRound(current.locked.value) - : acc, + current.expirationTimestampMs <= Date.now() ? acc + current.locked.value : acc, 0n, ); const availableStaking = timelockObjects.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() - ? acc + bigIntRound(current.locked.value) - : acc, + current.expirationTimestampMs > Date.now() ? acc + current.locked.value : acc, 0n, ); expect(vestingOverview.availableClaiming).toEqual(availableClaiming); diff --git a/apps/wallet-dashboard/lib/utils/vesting/vesting.ts b/apps/wallet-dashboard/lib/utils/vesting/vesting.ts index e0bb8a29158..374d9a19204 100644 --- a/apps/wallet-dashboard/lib/utils/vesting/vesting.ts +++ b/apps/wallet-dashboard/lib/utils/vesting/vesting.ts @@ -53,7 +53,7 @@ export function getLatestOrEarliestSupplyIncreaseVestingPayout( } function addVestingPayoutToSupplyIncreaseMap( - value: number, + value: bigint, expirationTimestampMs: number, supplyIncreaseMap: Map<number, SupplyIncreaseVestingPayout>, ) { @@ -85,7 +85,7 @@ function supplyIncreaseVestingObjectsToPayoutMap( expirationToVestingPayout, ); } else if (isTimelockedStakedIota(vestingObject)) { - const objectValue = Number(vestingObject.principal); + const objectValue = BigInt(vestingObject.principal); const expirationTimestampMs = Number(vestingObject.expirationTimestampMs); addVestingPayoutToSupplyIncreaseMap( objectValue, @@ -162,7 +162,7 @@ export function getVestingOverview( const userType = getSupplyIncreaseVestingUserType([latestPayout]); const vestingPayoutsCount = getSupplyIncreaseVestingPayoutsCount(userType!); // Note: we add the initial payout to the total rewards, 10% of the total rewards are paid out immediately - const totalVestedAmount = BigInt(Math.floor((vestingPayoutsCount * latestPayout.amount) / 0.9)); + const totalVestedAmount = (BigInt(vestingPayoutsCount) * latestPayout.amount * 10n) / 9n; const vestingPortfolio = buildSupplyIncreaseVestingSchedule( latestPayout, currentEpochTimestamp, @@ -326,7 +326,7 @@ export function prepareObjectsForTimelockedStakingTransaction( targetAmount: bigint, currentEpochMs: string, ): GroupedTimelockObject[] { - if (Number(targetAmount) === 0) { + if (targetAmount === 0n) { return []; } const timelockedMapped = mapTimelockObjects(timelockedObjects); @@ -368,7 +368,7 @@ export function prepareObjectsForTimelockedStakingTransaction( const remainingAmount = totalLocked - targetAmount; // Add splitAmount property to the vesting objects that need to be split - if (remainingAmount > 0) { + if (remainingAmount > 0n) { selectedGroupedTimelockObjects = adjustSplitAmountsInGroupedTimelockObjects( selectedGroupedTimelockObjects, remainingAmount, diff --git a/apps/wallet-dashboard/next.config.mjs b/apps/wallet-dashboard/next.config.mjs index a7417e613fd..533cbcccda9 100644 --- a/apps/wallet-dashboard/next.config.mjs +++ b/apps/wallet-dashboard/next.config.mjs @@ -1,6 +1,9 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import { execSync } from 'child_process'; +const NEXT_PUBLIC_DASHBOARD_REV = execSync('git rev-parse HEAD').toString().trim().toString(); + /** @type {import('next').NextConfig} */ const nextConfig = { async redirects() { @@ -16,6 +19,9 @@ const nextConfig = { // Remove this domain when fetching data domains: ['d315pvdvxi2gex.cloudfront.net'], }, + env: { + NEXT_PUBLIC_DASHBOARD_REV, + }, }; export default nextConfig; diff --git a/apps/wallet-dashboard/package.json b/apps/wallet-dashboard/package.json index 3cf781bca2f..9cff8865c4d 100644 --- a/apps/wallet-dashboard/package.json +++ b/apps/wallet-dashboard/package.json @@ -22,11 +22,12 @@ "@iota/dapp-kit": "workspace:*", "@iota/iota-sdk": "workspace:*", "@iota/ui-icons": "workspace:*", + "@iota/wallet-standard": "workspace:*", "@tanstack/react-query": "^5.50.1", "@tanstack/react-virtual": "^3.5.0", "clsx": "^2.1.1", "formik": "^2.4.2", - "next": "14.2.10", + "next": "14.2.15", "react": "^18.3.1", "react-hot-toast": "^2.4.1", "zustand": "^4.4.1" diff --git a/apps/wallet-dashboard/providers/AppProviders.tsx b/apps/wallet-dashboard/providers/AppProviders.tsx index 27744398f86..26be321a43b 100644 --- a/apps/wallet-dashboard/providers/AppProviders.tsx +++ b/apps/wallet-dashboard/providers/AppProviders.tsx @@ -3,7 +3,7 @@ 'use client'; -import { PopupProvider, Toaster } from '@/components'; +import { Toaster } from '@/components'; import { GrowthBookProvider } from '@growthbook/growthbook-react'; import { IotaClientProvider, lightTheme, darkTheme, WalletProvider } from '@iota/dapp-kit'; import { getAllNetworks, getDefaultNetwork } from '@iota/iota-sdk/client'; @@ -11,7 +11,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useState } from 'react'; import { KioskClientProvider } from '@iota/core'; import { growthbook } from '@/lib/utils'; -import { Popup } from '@/components/Popup'; import { ThemeProvider } from '@iota/core'; growthbook.init(); @@ -20,11 +19,18 @@ export function AppProviders({ children }: React.PropsWithChildren) { const [queryClient] = useState(() => new QueryClient()); const allNetworks = getAllNetworks(); const defaultNetwork = getDefaultNetwork(); - + function handleNetworkChange() { + queryClient.resetQueries(); + queryClient.clear(); + } return ( <GrowthBookProvider growthbook={growthbook}> <QueryClientProvider client={queryClient}> - <IotaClientProvider networks={allNetworks} defaultNetwork={defaultNetwork}> + <IotaClientProvider + networks={allNetworks} + defaultNetwork={defaultNetwork} + onNetworkChange={handleNetworkChange} + > <KioskClientProvider> <WalletProvider autoConnect={true} @@ -39,11 +45,8 @@ export function AppProviders({ children }: React.PropsWithChildren) { ]} > <ThemeProvider appId="iota-dashboard"> - <PopupProvider> - {children} - <Toaster /> - <Popup /> - </PopupProvider> + {children} + <Toaster /> </ThemeProvider> </WalletProvider> </KioskClientProvider> diff --git a/apps/wallet/package.json b/apps/wallet/package.json index a240f6170bb..59ad3cd44ec 100644 --- a/apps/wallet/package.json +++ b/apps/wallet/package.json @@ -1,6 +1,6 @@ { "name": "iota-wallet", - "version": "0.3.1", + "version": "0.4.0", "private": true, "description": "A wallet for IOTA", "main": "./dist/ui.js", @@ -81,7 +81,7 @@ "vite": "^5.3.3", "vite-tsconfig-paths": "^4.2.0", "vitest": "^2.0.1", - "web-ext": "^7.6.2", + "web-ext": "^8.3.0", "webpack": "^5.79.0", "webpack-cli": "^5.0.1", "webpack-merge": "^5.8.0" @@ -117,7 +117,7 @@ "@reduxjs/toolkit": "^1.9.5", "@scure/bip32": "^1.4.0", "@scure/bip39": "^1.3.0", - "@sentry/browser": "^7.61.0", + "@sentry/browser": "^7.120.2", "@tanstack/react-query": "^5.50.1", "@tanstack/react-query-persist-client": "^5.40.1", "axios": "^1.7.4", @@ -132,7 +132,6 @@ "formik": "^2.4.2", "framer-motion": "^10.12.22", "idb-keyval": "^6.2.1", - "jose": "^5.2.3", "mitt": "^3.0.1", "poseidon-lite": "^0.2.0", "react": "^18.3.1", diff --git a/apps/wallet/src/ui/app/components/accounts/AccountIcon.tsx b/apps/wallet/src/ui/app/components/accounts/AccountIcon.tsx index ffcc77d28dc..5ae5de338f1 100644 --- a/apps/wallet/src/ui/app/components/accounts/AccountIcon.tsx +++ b/apps/wallet/src/ui/app/components/accounts/AccountIcon.tsx @@ -11,7 +11,7 @@ interface AccountIconProps { export function AccountIcon({ account }: AccountIconProps) { if (account.type === AccountType.LedgerDerived) { - return <Ledger className="h-5 w-5" />; + return <Ledger className="h-5 w-5 text-neutral-10 dark:text-neutral-92" />; } - return <IotaLogoMark className="h-5 w-5" />; + return <IotaLogoMark className="h-5 w-5 text-neutral-10 dark:text-neutral-92" />; } diff --git a/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx b/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx index 85d4ff481e0..cb17eab7d23 100644 --- a/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx +++ b/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx @@ -28,7 +28,7 @@ export function TxnAmount({ amount, coinType, subtitle, approximation }: TxnAmou return Number(amount) !== 0 ? ( <Card type={CardType.Filled}> <CardImage type={ImageType.BgSolid}> - <CoinIcon coinType={coinType} rounded size={ImageIconSize.Large} hasCoinWrapper /> + <CoinIcon coinType={coinType} rounded size={ImageIconSize.Small} /> </CardImage> <CardBody title={`${approximation ? '~' : ''}${formatAmount} ${symbol}`} diff --git a/apps/wallet/src/ui/app/pages/accounts/WelcomePage.tsx b/apps/wallet/src/ui/app/pages/accounts/WelcomePage.tsx index a9ac8919e66..c9f6d36ddd7 100644 --- a/apps/wallet/src/ui/app/pages/accounts/WelcomePage.tsx +++ b/apps/wallet/src/ui/app/pages/accounts/WelcomePage.tsx @@ -23,7 +23,11 @@ export function WelcomePage() { return ( <Loading loading={isInitializedLoading || isFullscreenGuardLoading}> <div className="flex h-full w-full flex-col items-center justify-between bg-neutral-100 px-md py-2xl shadow-wallet-content dark:bg-neutral-6"> - <IotaLogoWeb width={130} height={32} /> + <IotaLogoWeb + width={130} + height={32} + className="text-neutral-10 dark:text-neutral-92" + /> <div className="flex flex-col items-center gap-8 text-center"> <div className="flex flex-col items-center gap-4"> <span className="text-headline-sm text-neutral-40">Welcome to</span> diff --git a/consensus/core/src/authority_node.rs b/consensus/core/src/authority_node.rs index b07c487bfac..70f81c271fb 100644 --- a/consensus/core/src/authority_node.rs +++ b/consensus/core/src/authority_node.rs @@ -36,8 +36,8 @@ use crate::{ /// ConsensusAuthority is used by Iota to manage the lifetime of AuthorityNode. /// It hides the details of the implementation from the caller, /// MysticetiManager. -#[allow(private_interfaces)] pub enum ConsensusAuthority { + #[expect(private_interfaces)] WithTonic(AuthorityNode<TonicManager>), } @@ -100,7 +100,7 @@ impl ConsensusAuthority { } } - #[allow(unused)] + #[cfg(test)] fn sync_last_known_own_block_enabled(&self) -> bool { match self { Self::WithTonic(authority) => authority.sync_last_known_own_block, @@ -124,6 +124,7 @@ where broadcaster: Option<Broadcaster>, subscriber: Option<Subscriber<N::Client, AuthorityService<ChannelCoreThreadDispatcher>>>, network_manager: N, + #[cfg(test)] sync_last_known_own_block: bool, } @@ -306,6 +307,7 @@ where broadcaster, subscriber, network_manager, + #[cfg(test)] sync_last_known_own_block, } } diff --git a/consensus/core/src/base_committer.rs b/consensus/core/src/base_committer.rs index 2440f2c513a..bdc9188d1b1 100644 --- a/consensus/core/src/base_committer.rs +++ b/consensus/core/src/base_committer.rs @@ -427,19 +427,19 @@ mod base_committer_builder { } } - #[allow(unused)] + #[expect(unused)] pub(crate) fn with_wave_length(mut self, wave_length: u32) -> Self { self.wave_length = wave_length; self } - #[allow(unused)] + #[expect(unused)] pub(crate) fn with_leader_offset(mut self, leader_offset: u32) -> Self { self.leader_offset = leader_offset; self } - #[allow(unused)] + #[expect(unused)] pub(crate) fn with_round_offset(mut self, round_offset: u32) -> Self { self.round_offset = round_offset; self @@ -447,9 +447,9 @@ mod base_committer_builder { pub(crate) fn build(self) -> BaseCommitter { let options = BaseCommitterOptions { - wave_length: DEFAULT_WAVE_LENGTH, - leader_offset: 0, - round_offset: 0, + wave_length: self.wave_length, + leader_offset: self.leader_offset, + round_offset: self.round_offset, }; BaseCommitter::new( self.context.clone(), diff --git a/consensus/core/src/block_verifier.rs b/consensus/core/src/block_verifier.rs index 74b7a8e4583..843a495fe4d 100644 --- a/consensus/core/src/block_verifier.rs +++ b/consensus/core/src/block_verifier.rs @@ -206,9 +206,10 @@ impl BlockVerifier for SignedBlockVerifier { } } -#[allow(unused)] +#[cfg(test)] pub(crate) struct NoopBlockVerifier; +#[cfg(test)] impl BlockVerifier for NoopBlockVerifier { fn verify(&self, _block: &SignedBlock) -> ConsensusResult<()> { Ok(()) diff --git a/consensus/core/src/commit.rs b/consensus/core/src/commit.rs index 9135fd342e1..fc2221a84b0 100644 --- a/consensus/core/src/commit.rs +++ b/consensus/core/src/commit.rs @@ -91,7 +91,6 @@ impl Commit { pub(crate) trait CommitAPI { fn round(&self) -> Round; fn index(&self) -> CommitIndex; - #[allow(dead_code)] fn previous_digest(&self) -> CommitDigest; fn timestamp_ms(&self) -> BlockTimestampMs; fn leader(&self) -> BlockRef; diff --git a/consensus/core/src/core.rs b/consensus/core/src/core.rs index 8031ddd064a..1446fb656e4 100644 --- a/consensus/core/src/core.rs +++ b/consensus/core/src/core.rs @@ -849,7 +849,7 @@ pub(crate) struct CoreTextFixture { pub core: Core, pub signal_receivers: CoreSignalsReceivers, pub block_receiver: broadcast::Receiver<VerifiedBlock>, - #[allow(unused)] + #[expect(unused)] pub commit_receiver: UnboundedReceiver<CommittedSubDag>, pub store: Arc<MemStore>, } diff --git a/consensus/core/src/leader_schedule.rs b/consensus/core/src/leader_schedule.rs index 62b9e522b98..871f9f5c1ea 100644 --- a/consensus/core/src/leader_schedule.rs +++ b/consensus/core/src/leader_schedule.rs @@ -298,7 +298,7 @@ impl LeaderSwapTable { context: Arc<Context>, // Ignore linter warning in simtests. // TODO: maybe override protocol configs in tests for swap_stake_threshold, and call new(). - #[allow(unused_variables)] swap_stake_threshold: u64, + #[cfg_attr(msim, expect(unused_variables))] swap_stake_threshold: u64, commit_index: CommitIndex, reputation_scores: ReputationScores, ) -> Self { diff --git a/consensus/core/src/leader_scoring_strategy.rs b/consensus/core/src/leader_scoring_strategy.rs index 72b54eb4e9a..366ad857bdf 100644 --- a/consensus/core/src/leader_scoring_strategy.rs +++ b/consensus/core/src/leader_scoring_strategy.rs @@ -11,13 +11,13 @@ use crate::{ stake_aggregator::{QuorumThreshold, StakeAggregator}, }; -#[allow(unused)] pub(crate) trait ScoringStrategy: Send + Sync { fn calculate_scores_for_leader(&self, subdag: &UnscoredSubdag, leader_slot: Slot) -> Vec<u64>; // Based on the scoring strategy there is a minimum number of rounds required // for the scores to be calculated. This method allows that to be set by the // scoring strategy. + #[expect(unused)] fn leader_scoring_round_range(&self, min_round: u32, max_round: u32) -> Range<u32>; } diff --git a/consensus/core/src/network/metrics_layer.rs b/consensus/core/src/network/metrics_layer.rs index c9949830dcf..01c35cc31c5 100644 --- a/consensus/core/src/network/metrics_layer.rs +++ b/consensus/core/src/network/metrics_layer.rs @@ -79,7 +79,7 @@ impl MetricsCallbackMaker { pub(crate) struct MetricsResponseCallback { metrics: Arc<NetworkRouteMetrics>, // The timer is held on to and "observed" once dropped - #[allow(unused)] + #[expect(unused)] timer: HistogramTimer, route: String, excessive_message_size: usize, diff --git a/consensus/core/src/stake_aggregator.rs b/consensus/core/src/stake_aggregator.rs index 4c2a1c8c06a..330939c2873 100644 --- a/consensus/core/src/stake_aggregator.rs +++ b/consensus/core/src/stake_aggregator.rs @@ -12,7 +12,7 @@ pub(crate) trait CommitteeThreshold { pub(crate) struct QuorumThreshold; -#[allow(unused)] +#[cfg(test)] pub(crate) struct ValidityThreshold; impl CommitteeThreshold for QuorumThreshold { @@ -21,6 +21,7 @@ impl CommitteeThreshold for QuorumThreshold { } } +#[cfg(test)] impl CommitteeThreshold for ValidityThreshold { fn is_threshold(committee: &Committee, amount: Stake) -> bool { committee.reached_validity(amount) diff --git a/consensus/core/src/storage/mem_store.rs b/consensus/core/src/storage/mem_store.rs index 656837433f7..602fd2ba4f9 100644 --- a/consensus/core/src/storage/mem_store.rs +++ b/consensus/core/src/storage/mem_store.rs @@ -21,12 +21,10 @@ use crate::{ }; /// In-memory storage for testing. -#[allow(unused)] pub(crate) struct MemStore { inner: RwLock<Inner>, } -#[allow(unused)] struct Inner { blocks: BTreeMap<(Round, AuthorityIndex, BlockDigest), VerifiedBlock>, digests_by_authorities: BTreeSet<(AuthorityIndex, Round, BlockDigest)>, @@ -36,7 +34,6 @@ struct Inner { } impl MemStore { - #[cfg(test)] pub(crate) fn new() -> Self { MemStore { inner: RwLock::new(Inner { diff --git a/consensus/core/src/storage/mod.rs b/consensus/core/src/storage/mod.rs index d83d49cb89e..925ed467fbb 100644 --- a/consensus/core/src/storage/mod.rs +++ b/consensus/core/src/storage/mod.rs @@ -2,6 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +#[cfg(test)] pub(crate) mod mem_store; pub(crate) mod rocksdb_store; @@ -12,13 +13,12 @@ use consensus_config::AuthorityIndex; use crate::{ CommitIndex, - block::{BlockRef, Round, Slot, VerifiedBlock}, + block::{BlockRef, Round, VerifiedBlock}, commit::{CommitInfo, CommitRange, CommitRef, TrustedCommit}, error::ConsensusResult, }; /// A common interface for consensus storage. -#[allow(unused)] pub(crate) trait Store: Send + Sync { /// Writes blocks, consensus commits and other data to store atomically. fn write(&self, write_batch: WriteBatch) -> ConsensusResult<()>; @@ -31,7 +31,7 @@ pub(crate) trait Store: Send + Sync { /// Checks whether there is any block at the given slot #[allow(dead_code)] - fn contains_block_at_slot(&self, slot: Slot) -> ConsensusResult<bool>; + fn contains_block_at_slot(&self, slot: crate::block::Slot) -> ConsensusResult<bool>; /// Reads blocks for an authority, from start_round. fn scan_blocks_by_author( diff --git a/consensus/core/src/storage/rocksdb_store.rs b/consensus/core/src/storage/rocksdb_store.rs index 39f18a906b7..aa06216318a 100644 --- a/consensus/core/src/storage/rocksdb_store.rs +++ b/consensus/core/src/storage/rocksdb_store.rs @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::{collections::VecDeque, ops::Bound::Included, time::Duration}; +use std::{ops::Bound::Included, time::Duration}; use bytes::Bytes; use consensus_config::AuthorityIndex; @@ -16,7 +16,7 @@ use typed_store::{ use super::{CommitInfo, Store, WriteBatch}; use crate::{ - block::{BlockAPI as _, BlockDigest, BlockRef, Round, SignedBlock, Slot, VerifiedBlock}, + block::{BlockAPI as _, BlockDigest, BlockRef, Round, SignedBlock, VerifiedBlock}, commit::{CommitAPI as _, CommitDigest, CommitIndex, CommitRange, CommitRef, TrustedCommit}, error::{ConsensusError, ConsensusResult}, }; @@ -176,7 +176,7 @@ impl Store for RocksDBStore { Ok(exist) } - fn contains_block_at_slot(&self, slot: Slot) -> ConsensusResult<bool> { + fn contains_block_at_slot(&self, slot: crate::block::Slot) -> ConsensusResult<bool> { let found = self .digests_by_authorities .safe_range_iter(( @@ -222,7 +222,7 @@ impl Store for RocksDBStore { before_round: Option<Round>, ) -> ConsensusResult<Vec<VerifiedBlock>> { let before_round = before_round.unwrap_or(Round::MAX); - let mut refs = VecDeque::new(); + let mut refs = std::collections::VecDeque::new(); for kv in self .digests_by_authorities .safe_range_iter(( diff --git a/consensus/core/src/synchronizer.rs b/consensus/core/src/synchronizer.rs index e928dd246c7..de1c0568960 100644 --- a/consensus/core/src/synchronizer.rs +++ b/consensus/core/src/synchronizer.rs @@ -975,21 +975,16 @@ impl<C: NetworkClient, V: BlockVerifier, D: CoreThreadDispatcher> Synchronizer<C .take(MAX_PEERS * context.parameters.max_blocks_per_fetch) .collect::<Vec<_>>(); - #[allow(unused_mut)] + #[cfg_attr(test, expect(unused_mut))] let mut peers = context .committee .authorities() .filter_map(|(peer_index, _)| (peer_index != context.own_index).then_some(peer_index)) .collect::<Vec<_>>(); - // TODO: probably inject the RNG to allow unit testing - this is a work around - // for now. - cfg_if::cfg_if! { - if #[cfg(not(test))] { - // Shuffle the peers - peers.shuffle(&mut ThreadRng::default()); - } - } + // In test, the order is not randomized + #[cfg(not(test))] + peers.shuffle(&mut ThreadRng::default()); let mut peers = peers.into_iter(); let mut request_futures = FuturesUnordered::new(); diff --git a/consensus/core/src/test_dag_builder.rs b/consensus/core/src/test_dag_builder.rs index 20e4ebe8d79..607c5d65dc8 100644 --- a/consensus/core/src/test_dag_builder.rs +++ b/consensus/core/src/test_dag_builder.rs @@ -76,7 +76,6 @@ use crate::{ /// dag_builder.layer(1).build(); /// dag_builder.print(); // pretty print the entire DAG /// ``` -#[allow(unused)] pub(crate) struct DagBuilder { pub(crate) context: Arc<Context>, pub(crate) leader_schedule: LeaderSchedule, @@ -93,7 +92,6 @@ pub(crate) struct DagBuilder { pipeline: bool, } -#[allow(unused)] impl DagBuilder { pub(crate) fn new(context: Arc<Context>) -> Self { let leader_schedule = LeaderSchedule::new(context.clone(), LeaderSwapTable::default()); @@ -206,25 +204,26 @@ impl DagBuilder { !self.blocks.is_empty(), "No blocks have been created, please make sure that you have called build method" ); - self.blocks - .iter() - .find(|(block_ref, block)| { - block_ref.round == round - && block_ref.author == self.leader_schedule.elect_leader(round, 0) - }) - .map(|(_block_ref, block)| block.clone()) + self.blocks.iter().find_map(|(block_ref, block)| { + (block_ref.round == round + && block_ref.author == self.leader_schedule.elect_leader(round, 0)) + .then_some(block.clone()) + }) } + #[expect(unused)] pub(crate) fn with_wave_length(mut self, wave_length: Round) -> Self { self.wave_length = wave_length; self } + #[expect(unused)] pub(crate) fn with_number_of_leaders(mut self, number_of_leaders: u32) -> Self { self.number_of_leaders = number_of_leaders; self } + #[expect(unused)] pub(crate) fn with_pipeline(mut self, pipeline: bool) -> Self { self.pipeline = pipeline; self @@ -290,7 +289,7 @@ impl DagBuilder { /// Gets all uncommitted blocks in a slot. pub(crate) fn get_uncommitted_blocks_at_slot(&self, slot: Slot) -> Vec<VerifiedBlock> { let mut blocks = vec![]; - for (block_ref, block) in self.blocks.range(( + for (_block_ref, block) in self.blocks.range(( Included(BlockRef::new(slot.round, slot.authority, BlockDigest::MIN)), Included(BlockRef::new(slot.round, slot.authority, BlockDigest::MAX)), )) { @@ -366,7 +365,7 @@ pub struct LayerBuilder<'a> { blocks: Vec<VerifiedBlock>, } -#[allow(unused)] +#[expect(unused)] impl<'a> LayerBuilder<'a> { fn new(dag_builder: &'a mut DagBuilder, start_round: Round) -> Self { assert!(start_round > 0, "genesis round is created by default"); diff --git a/consensus/core/src/transaction.rs b/consensus/core/src/transaction.rs index d2df8a87fe9..f22eeeba635 100644 --- a/consensus/core/src/transaction.rs +++ b/consensus/core/src/transaction.rs @@ -240,9 +240,10 @@ pub enum ValidationError { } /// `NoopTransactionVerifier` accepts all transactions. -#[allow(unused)] +#[cfg(test)] pub(crate) struct NoopTransactionVerifier; +#[cfg(test)] impl TransactionVerifier for NoopTransactionVerifier { fn verify_batch(&self, _batch: &[&[u8]]) -> Result<(), ValidationError> { Ok(()) diff --git a/consensus/core/src/universal_committer.rs b/consensus/core/src/universal_committer.rs index bee37eef92e..970638e868d 100644 --- a/consensus/core/src/universal_committer.rs +++ b/consensus/core/src/universal_committer.rs @@ -182,7 +182,7 @@ pub(crate) mod universal_committer_builder { } } - #[allow(unused)] + #[expect(unused)] pub(crate) fn with_wave_length(mut self, wave_length: Round) -> Self { self.wave_length = wave_length; self diff --git a/crates/bin-version/src/lib.rs b/crates/bin-version/src/lib.rs index 7af360af7fe..76f534af1e1 100644 --- a/crates/bin-version/src/lib.rs +++ b/crates/bin-version/src/lib.rs @@ -12,11 +12,11 @@ pub mod _hidden { /// /// Defines two global `const`s: /// `GIT_REVISION`: The git revision as specified by the `GIT_REVISION` env -/// variable provided at compile time, or the current git revision as -/// discovered by running `git describe`. +/// variable provided at compile time, or the current git revision as discovered +/// by running `git describe`. /// /// `VERSION`: The value of the `CARGO_PKG_VERSION` environment variable -/// concatenated with the value of `GIT_REVISION`. +/// concatenated with the value of `GIT_REVISION` if it is not empty. /// /// Note: This macro must only be used from a binary, if used inside a library /// this will fail to compile. @@ -25,16 +25,19 @@ macro_rules! bin_version { () => { $crate::git_revision!(); - const VERSION: &str = - $crate::_hidden::concat!(env!("CARGO_PKG_VERSION"), "-", GIT_REVISION); + const VERSION: &str = if GIT_REVISION.is_empty() { + env!("CARGO_PKG_VERSION") + } else { + $crate::_hidden::concat!(env!("CARGO_PKG_VERSION"), "-", GIT_REVISION) + }; }; } /// Defines constant that holds the git revision at build time. /// /// `GIT_REVISION`: The git revision as specified by the `GIT_REVISION` env -/// variable provided at compile time, or the current git revision as -/// discovered by running `git describe`. +/// variable provided at compile time, or the current git revision as discovered +/// by running `git describe`. /// /// Note: This macro must only be used from a binary, if used inside a library /// this will fail to compile. diff --git a/crates/iota-analytics-indexer/src/analytics_processor.rs b/crates/iota-analytics-indexer/src/analytics_processor.rs index 7203f5dfe5c..036b25ad188 100644 --- a/crates/iota-analytics-indexer/src/analytics_processor.rs +++ b/crates/iota-analytics-indexer/src/analytics_processor.rs @@ -41,9 +41,9 @@ pub struct AnalyticsProcessor<S: Serialize + ParquetSchema> { metrics: AnalyticsMetrics, config: AnalyticsIndexerConfig, sender: mpsc::Sender<FileMetadata>, - #[allow(dead_code)] + #[expect(dead_code)] kill_sender: oneshot::Sender<()>, - #[allow(dead_code)] + #[expect(dead_code)] max_checkpoint_sender: oneshot::Sender<()>, } diff --git a/crates/iota-archival/src/lib.rs b/crates/iota-archival/src/lib.rs index e4a783b5b25..21938964cc6 100644 --- a/crates/iota-archival/src/lib.rs +++ b/crates/iota-archival/src/lib.rs @@ -52,7 +52,7 @@ use tracing::{error, info}; use crate::reader::{ArchiveReader, ArchiveReaderMetrics}; -#[allow(rustdoc::invalid_html_tags)] +#[expect(rustdoc::invalid_html_tags)] /// Checkpoints and summaries are persisted as blob files. Files are committed /// to local store by duration or file size. Committed files are synced with the /// remote store continuously. Files are optionally compressed with the zstd diff --git a/crates/iota-aws-orchestrator/src/benchmark.rs b/crates/iota-aws-orchestrator/src/benchmark.rs index d53d42e225d..733537adfe7 100644 --- a/crates/iota-aws-orchestrator/src/benchmark.rs +++ b/crates/iota-aws-orchestrator/src/benchmark.rs @@ -103,7 +103,6 @@ pub enum LoadType { /// Search for the breaking point of the L-graph. // TODO: Doesn't work very well, use tps regression as additional signal. - #[allow(dead_code)] Search { /// The initial load to test (and use a baseline). starting_load: usize, diff --git a/crates/iota-aws-orchestrator/src/monitor.rs b/crates/iota-aws-orchestrator/src/monitor.rs index 3a3da4f7a43..335960f8b96 100644 --- a/crates/iota-aws-orchestrator/src/monitor.rs +++ b/crates/iota-aws-orchestrator/src/monitor.rs @@ -220,7 +220,6 @@ impl Grafana { } } -#[allow(dead_code)] /// Bootstrap the grafana with datasource to connect to the given instances. /// NOTE: Only for macOS. Grafana must be installed through homebrew (and not /// from source). Deeper grafana configuration can be done through the @@ -228,7 +227,7 @@ impl Grafana { /// (~/Library/LaunchAgents/homebrew.mxcl.grafana.plist). pub struct LocalGrafana; -#[allow(dead_code)] +#[expect(dead_code)] impl LocalGrafana { /// The default grafana home directory (macOS, homebrew install). const DEFAULT_GRAFANA_HOME: &'static str = "/opt/homebrew/opt/grafana/share/grafana/"; diff --git a/crates/iota-benchmark/src/drivers/mod.rs b/crates/iota-benchmark/src/drivers/mod.rs index d242d6723a3..f442d30a3b8 100644 --- a/crates/iota-benchmark/src/drivers/mod.rs +++ b/crates/iota-benchmark/src/drivers/mod.rs @@ -55,7 +55,6 @@ impl std::fmt::Display for Interval { } // wrapper which implements serde -#[allow(dead_code)] #[derive(Debug)] pub struct HistogramWrapper { histogram: Histogram<u64>, diff --git a/crates/iota-benchmark/src/lib.rs b/crates/iota-benchmark/src/lib.rs index 2b807952799..f715a56f18f 100644 --- a/crates/iota-benchmark/src/lib.rs +++ b/crates/iota-benchmark/src/lib.rs @@ -72,7 +72,7 @@ use tracing::{error, info}; #[derive(Debug)] /// A wrapper on execution results to accommodate different types of /// responses from LocalValidatorAggregatorProxy and FullNodeProxy -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] pub enum ExecutionEffects { CertifiedTransactionEffects(CertifiedTransactionEffects, TransactionEvents), IotaTransactionBlockEffects(IotaTransactionBlockEffects), diff --git a/crates/iota-bridge/src/action_executor.rs b/crates/iota-bridge/src/action_executor.rs index be81c44ed10..a1070ae3aab 100644 --- a/crates/iota-bridge/src/action_executor.rs +++ b/crates/iota-bridge/src/action_executor.rs @@ -695,9 +695,8 @@ mod tests { #[tokio::test] #[ignore = "https://github.com/iotaledger/iota/issues/3224"] async fn test_onchain_execution_loop() { - let ( + let SetupData { signing_tx, - _execution_tx, iota_client_mock, mut tx_subscription, store, @@ -707,12 +706,11 @@ mod tests { mock1, mock2, mock3, - _handles, gas_object_ref, iota_address, iota_token_type_tags, - _bridge_pause_tx, - ) = setup().await; + .. + } = setup().await; let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], @@ -903,9 +901,8 @@ mod tests { #[tokio::test] #[ignore = "https://github.com/iotaledger/iota/issues/3224"] async fn test_signature_aggregation_loop() { - let ( + let SetupData { signing_tx, - _execution_tx, iota_client_mock, mut tx_subscription, store, @@ -915,12 +912,11 @@ mod tests { mock1, mock2, mock3, - _handles, gas_object_ref, iota_address, iota_token_type_tags, - _bridge_pause_tx, - ) = setup().await; + .. + } = setup().await; let id_token_map = (*iota_token_type_tags.load().clone()).clone(); let (action_certificate, iota_tx_digest, iota_tx_event_index) = get_bridge_authority_approved_action( @@ -1030,24 +1026,17 @@ mod tests { #[tokio::test] #[ignore = "https://github.com/iotaledger/iota/issues/3224"] async fn test_skip_request_signature_if_already_processed_on_chain() { - let ( + let SetupData { signing_tx, - _execution_tx, iota_client_mock, mut tx_subscription, store, - _secrets, - _dummy_iota_key, mock0, mock1, mock2, mock3, - _handles, - _gas_object_ref, - _iota_address, - _iota_token_type_tags, - _bridge_pause_tx, - ) = setup().await; + .. + } = setup().await; let iota_tx_digest = TransactionDigest::random(); let iota_tx_event_index = 0; @@ -1101,8 +1090,7 @@ mod tests { #[tokio::test] #[ignore = "https://github.com/iotaledger/iota/issues/3224"] async fn test_skip_tx_submission_if_already_processed_on_chain() { - let ( - _signing_tx, + let SetupData { execution_tx, iota_client_mock, mut tx_subscription, @@ -1113,12 +1101,11 @@ mod tests { mock1, mock2, mock3, - _handles, gas_object_ref, iota_address, iota_token_type_tags, - _bridge_pause_tx, - ) = setup().await; + .. + } = setup().await; let id_token_map = (*iota_token_type_tags.load().clone()).clone(); let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], @@ -1188,8 +1175,7 @@ mod tests { #[tokio::test] #[ignore = "https://github.com/iotaledger/iota/issues/3224"] async fn test_skip_tx_submission_if_bridge_is_paused() { - let ( - _signing_tx, + let SetupData { execution_tx, iota_client_mock, mut tx_subscription, @@ -1200,12 +1186,12 @@ mod tests { mock1, mock2, mock3, - _handles, gas_object_ref, iota_address, iota_token_type_tags, bridge_pause_tx, - ) = setup().await; + .. + } = setup().await; let id_token_map: HashMap<u8, TypeTag> = (*iota_token_type_tags.load().clone()).clone(); let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], @@ -1290,24 +1276,21 @@ mod tests { async fn test_action_executor_handle_new_token() { let new_token_id = 255u8; // token id that does not exist let new_type_tag = TypeTag::from_str("0xbeef::beef::BEEF").unwrap(); - let ( - _signing_tx, + let SetupData { execution_tx, iota_client_mock, mut tx_subscription, - _store, secrets, dummy_iota_key, mock0, mock1, mock2, mock3, - _handles, gas_object_ref, iota_address, iota_token_type_tags, - _bridge_pause_tx, - ) = setup().await; + .. + } = setup().await; let mut id_token_map: HashMap<u8, TypeTag> = (*iota_token_type_tags.load().clone()).clone(); let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], @@ -1501,25 +1484,27 @@ mod tests { } } - #[allow(clippy::type_complexity)] - async fn setup() -> ( - iota_metrics::metered_channel::Sender<BridgeActionExecutionWrapper>, - iota_metrics::metered_channel::Sender<CertifiedBridgeActionExecutionWrapper>, - IotaMockClient, - tokio::sync::broadcast::Receiver<TransactionDigest>, - Arc<BridgeOrchestratorTables>, - Vec<BridgeAuthorityKeyPair>, - IotaKeyPair, - BridgeRequestMockHandler, - BridgeRequestMockHandler, - BridgeRequestMockHandler, - BridgeRequestMockHandler, - Vec<tokio::task::JoinHandle<()>>, - ObjectRef, - IotaAddress, - Arc<ArcSwap<HashMap<u8, TypeTag>>>, - tokio::sync::watch::Sender<IsBridgePaused>, - ) { + struct SetupData { + signing_tx: iota_metrics::metered_channel::Sender<BridgeActionExecutionWrapper>, + execution_tx: iota_metrics::metered_channel::Sender<CertifiedBridgeActionExecutionWrapper>, + iota_client_mock: IotaMockClient, + tx_subscription: tokio::sync::broadcast::Receiver<TransactionDigest>, + store: Arc<BridgeOrchestratorTables>, + secrets: Vec<BridgeAuthorityKeyPair>, + dummy_iota_key: IotaKeyPair, + mock0: BridgeRequestMockHandler, + mock1: BridgeRequestMockHandler, + mock2: BridgeRequestMockHandler, + mock3: BridgeRequestMockHandler, + #[expect(unused)] + handles: Vec<tokio::task::JoinHandle<()>>, + gas_object_ref: ObjectRef, + iota_address: IotaAddress, + iota_token_type_tags: Arc<ArcSwap<HashMap<u8, TypeTag>>>, + bridge_pause_tx: tokio::sync::watch::Sender<IsBridgePaused>, + } + + async fn setup() -> SetupData { telemetry_subscribers::init_for_testing(); let registry = Registry::new(); iota_metrics::init_metrics(®istry); @@ -1578,7 +1563,7 @@ mod tests { let (executor_handle, signing_tx, execution_tx) = executor.run_inner(); handles.extend(executor_handle); - ( + SetupData { signing_tx, execution_tx, iota_client_mock, @@ -1595,6 +1580,6 @@ mod tests { iota_address, iota_token_type_tags, bridge_pause_tx, - ) + } } } diff --git a/crates/iota-bridge/src/e2e_tests/test_utils.rs b/crates/iota-bridge/src/e2e_tests/test_utils.rs index 35195b89d22..a836ae9888f 100644 --- a/crates/iota-bridge/src/e2e_tests/test_utils.rs +++ b/crates/iota-bridge/src/e2e_tests/test_utils.rs @@ -434,7 +434,6 @@ pub async fn get_eth_signer_client_e2e_test_only( Ok((signer_0, private_key_0.to_string())) } -#[allow(dead_code)] #[derive(Debug, Clone)] pub struct DeployedSolContracts { pub iota_bridge: EthAddress, diff --git a/crates/iota-bridge/src/eth_syncer.rs b/crates/iota-bridge/src/eth_syncer.rs index cdbd7bf59a6..2fb109dd0da 100644 --- a/crates/iota-bridge/src/eth_syncer.rs +++ b/crates/iota-bridge/src/eth_syncer.rs @@ -36,7 +36,6 @@ pub struct EthSyncer<P> { /// Map from contract address to their start block. pub type EthTargetAddresses = HashMap<EthAddress, u64>; -#[allow(clippy::new_without_default)] impl<P> EthSyncer<P> where P: ethers::providers::JsonRpcClient + 'static, diff --git a/crates/iota-bridge/src/iota_mock_client.rs b/crates/iota-bridge/src/iota_mock_client.rs index 178e631de03..acf4fa4fd27 100644 --- a/crates/iota-bridge/src/iota_mock_client.rs +++ b/crates/iota-bridge/src/iota_mock_client.rs @@ -30,7 +30,7 @@ use crate::{ }; /// Mock client used in test environments. -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] #[derive(Clone, Debug)] pub struct IotaMockClient { // the top two fields do not change during tests so we don't need them to be Arc<Mutex>> diff --git a/crates/iota-bridge/src/monitor.rs b/crates/iota-bridge/src/monitor.rs index f2b0a551e51..3d483d216be 100644 --- a/crates/iota-bridge/src/monitor.rs +++ b/crates/iota-bridge/src/monitor.rs @@ -906,7 +906,7 @@ mod tests { .unwrap(); } - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] fn setup() -> ( iota_metrics::metered_channel::Sender<IotaBridgeEvent>, iota_metrics::metered_channel::Receiver<IotaBridgeEvent>, diff --git a/crates/iota-bridge/src/orchestrator.rs b/crates/iota-bridge/src/orchestrator.rs index e0193d15230..8604d0fe28d 100644 --- a/crates/iota-bridge/src/orchestrator.rs +++ b/crates/iota-bridge/src/orchestrator.rs @@ -485,7 +485,7 @@ mod tests { assert_eq!(digests.len(), 2); } - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] fn setup() -> ( iota_metrics::metered_channel::Sender<(Identifier, Vec<IotaEvent>)>, iota_metrics::metered_channel::Receiver<(Identifier, Vec<IotaEvent>)>, diff --git a/crates/iota-bridge/src/server/mock_handler.rs b/crates/iota-bridge/src/server/mock_handler.rs index c986cf4e143..39505eba88f 100644 --- a/crates/iota-bridge/src/server/mock_handler.rs +++ b/crates/iota-bridge/src/server/mock_handler.rs @@ -26,7 +26,7 @@ use crate::{ types::SignedBridgeAction, }; -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] #[derive(Clone)] pub struct BridgeRequestMockHandler { signer: Arc<ArcSwap<Option<BridgeAuthorityKeyPair>>>, diff --git a/crates/iota-cluster-test/src/lib.rs b/crates/iota-cluster-test/src/lib.rs index b107bc08f3f..855ab846157 100644 --- a/crates/iota-cluster-test/src/lib.rs +++ b/crates/iota-cluster-test/src/lib.rs @@ -50,7 +50,6 @@ pub mod wallet_client; pub use iota_genesis_builder::SnapshotUrl as MigrationSnapshotUrl; -#[allow(unused)] pub struct TestContext { /// Cluster handle that allows access to various components in a cluster cluster: Box<dyn Cluster + Sync + Send>, diff --git a/crates/iota-common/src/sync/async_once_cell.rs b/crates/iota-common/src/sync/async_once_cell.rs index b2759e4ba62..68cf5583271 100644 --- a/crates/iota-common/src/sync/async_once_cell.rs +++ b/crates/iota-common/src/sync/async_once_cell.rs @@ -42,7 +42,7 @@ impl<T: Send + Clone> AsyncOnceCell<T> { } /// Sets the value and notifies waiters. Return error if called twice - #[allow(clippy::result_unit_err)] + #[expect(clippy::result_unit_err)] pub fn set(&self, value: T) -> Result<(), ()> { let mut writer = self.writer.lock(); match writer.take() { diff --git a/crates/iota-common/src/sync/notify_once.rs b/crates/iota-common/src/sync/notify_once.rs index dfeb264ab4b..2c6b2b5eac8 100644 --- a/crates/iota-common/src/sync/notify_once.rs +++ b/crates/iota-common/src/sync/notify_once.rs @@ -35,7 +35,7 @@ impl NotifyOnce { /// After this method all pending and future calls to .wait() will return /// /// This method returns errors if called more then once - #[allow(clippy::result_unit_err)] + #[expect(clippy::result_unit_err)] pub fn notify(&self) -> Result<(), ()> { let Some(notify) = self.notify.lock().take() else { return Err(()); diff --git a/crates/iota-config/src/genesis.rs b/crates/iota-config/src/genesis.rs index 3cb724e34b6..d7c850ca148 100644 --- a/crates/iota-config/src/genesis.rs +++ b/crates/iota-config/src/genesis.rs @@ -586,7 +586,7 @@ pub struct TokenDistributionScheduleBuilder { } impl TokenDistributionScheduleBuilder { - #[allow(clippy::new_without_default)] + #[expect(clippy::new_without_default)] pub fn new() -> Self { Self { pre_minted_supply: 0, diff --git a/crates/iota-core/src/authority.rs b/crates/iota-core/src/authority.rs index bd968065e99..d85c217974f 100644 --- a/crates/iota-core/src/authority.rs +++ b/crates/iota-core/src/authority.rs @@ -784,7 +784,7 @@ pub struct AuthorityState { transaction_manager: Arc<TransactionManager>, /// Shuts down the execution task. Used only in testing. - #[allow(unused)] + #[cfg_attr(not(test), expect(unused))] tx_execution_shutdown: Mutex<Option<oneshot::Sender<()>>>, pub metrics: Arc<AuthorityMetrics>, @@ -1593,7 +1593,7 @@ impl AuthorityState { let transaction_data = &certificate.data().intent_message().value; let (kind, signer, gas) = transaction_data.execution_parts(); - #[allow(unused_mut)] + #[expect(unused_mut)] let (inner_temp_store, _, mut effects, execution_error_opt) = epoch_store.executor().execute_transaction_to_effects( self.get_backing_store().as_ref(), @@ -1860,7 +1860,6 @@ impl AuthorityState { } /// The object ID for gas can be any object ID, even for an uncreated object - #[allow(clippy::collapsible_else_if)] pub async fn dev_inspect_transaction_block( &self, sender: IotaAddress, @@ -2610,7 +2609,7 @@ impl AuthorityState { } } - #[allow(clippy::disallowed_methods)] // allow unbounded_channel() + #[expect(clippy::disallowed_methods)] // allow unbounded_channel() pub async fn new( name: AuthorityName, secret: StableSyncAuthoritySigner, diff --git a/crates/iota-core/src/authority/authority_per_epoch_store.rs b/crates/iota-core/src/authority/authority_per_epoch_store.rs index cc6340a840e..cf080a649d9 100644 --- a/crates/iota-core/src/authority/authority_per_epoch_store.rs +++ b/crates/iota-core/src/authority/authority_per_epoch_store.rs @@ -131,8 +131,8 @@ pub(crate) type EncG = bls12381::G2Element; // retain a distinction anyway. If we need to support distributed object // storage, having this distinction will be useful, as we will most likely have // to re-implement a retry / write-ahead-log at that point. -pub struct CertLockGuard(#[allow(unused)] MutexGuard); -pub struct CertTxGuard(#[allow(unused)] CertLockGuard); +pub struct CertLockGuard(#[expect(unused)] MutexGuard); +pub struct CertTxGuard(#[expect(unused)] CertLockGuard); impl CertTxGuard { pub fn release(self) {} @@ -2273,7 +2273,6 @@ impl AuthorityPerEpochStore { /// /// In addition to the early termination guarantee, this function also /// prevents epoch_terminated() if future is being executed. - #[allow(clippy::result_unit_err)] pub async fn within_alive_epoch<F: Future + Send>(&self, f: F) -> Result<F::Output, ()> { // This guard is kept in the future until it resolves, preventing // `epoch_terminated` to acquire a write lock @@ -2911,7 +2910,7 @@ impl AuthorityPerEpochStore { /// VerifiedCertificates for each executable certificate /// - Or update the state for checkpoint or epoch change protocol. #[instrument(level = "debug", skip_all)] - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] pub(crate) async fn process_consensus_transactions<C: CheckpointServiceNotify>( &self, output: &mut ConsensusCommitOutput, @@ -4103,8 +4102,8 @@ impl LockDetailsWrapper { match self { Self::V1(v1) => v1, - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] + // can remove #[expect] when there are multiple versions + #[expect(unreachable_patterns)] _ => panic!("lock details should have been migrated to latest version at read time"), } } @@ -4112,8 +4111,8 @@ impl LockDetailsWrapper { match self { Self::V1(v1) => v1, - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] + // can remove #[expect] when there are multiple versions + #[expect(unreachable_patterns)] _ => panic!("lock details should have been migrated to latest version at read time"), } } diff --git a/crates/iota-core/src/authority/authority_store.rs b/crates/iota-core/src/authority/authority_store.rs index 98d68d84ae1..2ae0de3fb71 100644 --- a/crates/iota-core/src/authority/authority_store.rs +++ b/crates/iota-core/src/authority/authority_store.rs @@ -163,7 +163,7 @@ impl AuthorityStore { let epoch_start_configuration = if perpetual_tables.database_is_empty()? { info!("Creating new epoch start config from genesis"); - #[allow(unused_mut)] + #[expect(unused_mut)] let mut initial_epoch_flags = EpochFlag::default_flags_for_new_epoch(config); fail_point_arg!("initial_epoch_flags", |flags: Vec<EpochFlag>| { info!("Setting initial epoch flags to {:?}", flags); diff --git a/crates/iota-core/src/authority/authority_store_types.rs b/crates/iota-core/src/authority/authority_store_types.rs index b50904ea5c6..d46bd084eca 100644 --- a/crates/iota-core/src/authority/authority_store_types.rs +++ b/crates/iota-core/src/authority/authority_store_types.rs @@ -65,8 +65,8 @@ impl StoreObjectWrapper { match self { Self::V1(v1) => v1, - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] + // can remove #[expect] when there are multiple versions + #[expect(unreachable_patterns)] _ => panic!("object should have been migrated to latest version at read time"), } } @@ -74,8 +74,8 @@ impl StoreObjectWrapper { match self { Self::V1(v1) => v1, - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] + // can remove #[expect] when there are multiple versions + #[expect(unreachable_patterns)] _ => panic!("object should have been migrated to latest version at read time"), } } @@ -145,8 +145,8 @@ impl StoreMoveObjectWrapper { match self { Self::V1(v1) => v1, - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] + // can remove #[expect] when there are multiple versions + #[expect(unreachable_patterns)] _ => panic!("object should have been migrated to latest version at read time"), } } @@ -154,8 +154,8 @@ impl StoreMoveObjectWrapper { match self { Self::V1(v1) => v1, - // can remove #[allow] when there are multiple versions - #[allow(unreachable_patterns)] + // can remove #[expect] when there are multiple versions + #[expect(unreachable_patterns)] _ => panic!("object should have been migrated to latest version at read time"), } } diff --git a/crates/iota-core/src/authority/epoch_start_configuration.rs b/crates/iota-core/src/authority/epoch_start_configuration.rs index 7ae6446577a..26df7d243ae 100644 --- a/crates/iota-core/src/authority/epoch_start_configuration.rs +++ b/crates/iota-core/src/authority/epoch_start_configuration.rs @@ -127,7 +127,7 @@ impl EpochStartConfiguration { })) } - #[allow(unreachable_patterns)] + #[expect(unreachable_patterns)] pub fn new_at_next_epoch_for_testing(&self) -> Self { // We only need to implement this function for the latest version. // When a new version is introduced, this function should be updated. diff --git a/crates/iota-core/src/authority_aggregator.rs b/crates/iota-core/src/authority_aggregator.rs index d7a3f8b6251..f42945e4990 100644 --- a/crates/iota-core/src/authority_aggregator.rs +++ b/crates/iota-core/src/authority_aggregator.rs @@ -287,7 +287,7 @@ pub enum AggregatorProcessCertificateError { /// Groups the errors by error type and stake. pub fn group_errors(errors: Vec<(IotaError, Vec<AuthorityName>, StakeUnit)>) -> GroupedErrors { - #[allow(clippy::mutable_key_type)] + #[expect(clippy::mutable_key_type)] let mut grouped_errors = HashMap::new(); for (error, names, stake) in errors { let entry = grouped_errors.entry(error).or_insert((0, vec![])); @@ -382,7 +382,7 @@ struct ProcessTransactionState { impl ProcessTransactionState { /// Returns the conflicting transaction digest and its validators with the /// most stake. - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] pub fn conflicting_tx_digest_with_most_stake( &self, ) -> Option<( diff --git a/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs b/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs index bac30e2396c..d1266c66400 100644 --- a/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs +++ b/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs @@ -1002,7 +1002,7 @@ fn extract_end_of_epoch_tx( // Given a checkpoint, filter out any already executed transactions, then return // the remaining execution digests, transaction digests, transactions to be // executed, and randomness rounds (if any) included in the checkpoint. -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] fn get_unexecuted_transactions( checkpoint: VerifiedCheckpoint, cache_reader: &dyn TransactionCacheRead, diff --git a/crates/iota-core/src/checkpoints/mod.rs b/crates/iota-core/src/checkpoints/mod.rs index 3c819e3c8c6..9ab63227779 100644 --- a/crates/iota-core/src/checkpoints/mod.rs +++ b/crates/iota-core/src/checkpoints/mod.rs @@ -1238,7 +1238,7 @@ impl CheckpointBuilder { Ok(()) } - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] fn split_checkpoint_chunks( &self, effects_and_transaction_sizes: Vec<(TransactionEffects, usize)>, @@ -1924,7 +1924,7 @@ impl CheckpointAggregator { } impl CheckpointSignatureAggregator { - #[allow(clippy::result_unit_err)] + #[expect(clippy::result_unit_err)] pub fn try_aggregate( &mut self, data: CheckpointSignatureMessage, diff --git a/crates/iota-core/src/connection_monitor.rs b/crates/iota-core/src/connection_monitor.rs index a15afd715c4..33224315332 100644 --- a/crates/iota-core/src/connection_monitor.rs +++ b/crates/iota-core/src/connection_monitor.rs @@ -109,9 +109,7 @@ impl ConnectionMonitor { rx.receiver.recv().await } else { // If no shutdown receiver is provided, wait forever. - let future = future::pending(); - #[allow(clippy::let_unit_value)] - let () = future.await; + future::pending::<()>().await; Ok(()) } } diff --git a/crates/iota-core/src/consensus_adapter.rs b/crates/iota-core/src/consensus_adapter.rs index 46e8e9b6875..eb25d7b5137 100644 --- a/crates/iota-core/src/consensus_adapter.rs +++ b/crates/iota-core/src/consensus_adapter.rs @@ -280,7 +280,7 @@ impl ConsensusAdapter { // be a big deal but can be optimized let mut recovered = epoch_store.get_all_pending_consensus_transactions(); - #[allow(clippy::collapsible_if)] // This if can be collapsed but it will be ugly + #[expect(clippy::collapsible_if)] // This if can be collapsed but it will be ugly if epoch_store .get_reconfig_state_read_lock_guard() .is_reject_user_certs() @@ -592,7 +592,6 @@ impl ConsensusAdapter { // care about it } - #[allow(clippy::option_map_unit_fn)] async fn submit_and_wait_inner( self: Arc<Self>, transactions: Vec<ConsensusTransaction>, diff --git a/crates/iota-core/src/epoch/randomness.rs b/crates/iota-core/src/epoch/randomness.rs index e63f4f2a4e8..be323fa4747 100644 --- a/crates/iota-core/src/epoch/randomness.rs +++ b/crates/iota-core/src/epoch/randomness.rs @@ -57,7 +57,6 @@ pub const SINGLETON_KEY: u64 = 0; // Wrappers for DKG messages (to simplify upgrades). #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[allow(clippy::large_enum_variant)] pub enum VersionedProcessedMessage { V1(dkg_v1::ProcessedMessage<PkG, EncG>), } @@ -427,7 +426,7 @@ impl RandomnessManager { info!("random beacon: created {msg:?} with dkg version {dkg_version}"); let transaction = ConsensusTransaction::new_randomness_dkg_message(epoch_store.name, &msg); - #[allow(unused_mut)] + #[expect(unused_mut)] let mut fail_point_skip_sending = false; fail_point_if!("rb-dkg", || { // maybe skip sending in simtests @@ -499,7 +498,7 @@ impl RandomnessManager { &conf, ); - #[allow(unused_mut)] + #[expect(unused_mut)] let mut fail_point_skip_sending = false; fail_point_if!("rb-dkg", || { // maybe skip sending in simtests diff --git a/crates/iota-core/src/overload_monitor.rs b/crates/iota-core/src/overload_monitor.rs index 3f155117282..c4e921d15cf 100644 --- a/crates/iota-core/src/overload_monitor.rs +++ b/crates/iota-core/src/overload_monitor.rs @@ -271,7 +271,7 @@ pub fn overload_monitor_accept_tx( } #[cfg(test)] -#[allow(clippy::disallowed_methods)] // allow unbounded_channel() since tests are simulating txn manager execution +#[expect(clippy::disallowed_methods)] // allow unbounded_channel() since tests are simulating txn manager execution // driver interaction. mod tests { use std::sync::Arc; diff --git a/crates/iota-core/src/stake_aggregator.rs b/crates/iota-core/src/stake_aggregator.rs index b8d8907fca0..3269ba384ac 100644 --- a/crates/iota-core/src/stake_aggregator.rs +++ b/crates/iota-core/src/stake_aggregator.rs @@ -301,7 +301,7 @@ impl<K, V, const STRENGTH: bool> MultiStakeAggregator<K, V, STRENGTH> where K: Hash + Eq, { - #[allow(dead_code)] + #[expect(dead_code)] pub fn authorities_for_key(&self, k: &K) -> Option<impl Iterator<Item = &AuthorityName>> { self.stake_maps.get(k).map(|(_, agg)| agg.keys()) } diff --git a/crates/iota-core/src/test_utils.rs b/crates/iota-core/src/test_utils.rs index 809ca04d8c5..4a215adf02a 100644 --- a/crates/iota-core/src/test_utils.rs +++ b/crates/iota-core/src/test_utils.rs @@ -11,7 +11,6 @@ use iota_framework::BuiltInFramework; use iota_genesis_builder::{ genesis_build_effects::GenesisBuildEffects, validator_info::ValidatorInfo, }; -use iota_macros::nondeterministic; use iota_move_build::{BuildConfig, CompiledPackage, IotaPackageHooks}; use iota_protocol_config::ProtocolConfig; use iota_types::{ @@ -94,14 +93,12 @@ pub async fn send_and_confirm_transaction( Ok((certificate.into_inner(), result.into_inner())) } -// note: clippy is confused about this being dead - it appears to only be used -// in cfg(test), but adding #[cfg(test)] causes other targets to fail -#[allow(dead_code)] +#[cfg(test)] pub(crate) fn init_state_parameters_from_rng<R>(rng: &mut R) -> (Genesis, AuthorityKeyPair) where R: rand::CryptoRng + rand::RngCore, { - let dir = nondeterministic!(tempfile::TempDir::new().unwrap()); + let dir = iota_macros::nondeterministic!(tempfile::TempDir::new().unwrap()); let network_config = iota_swarm_config::network_config_builder::ConfigBuilder::new(&dir) .rng(rng) .build(); diff --git a/crates/iota-core/src/transaction_manager.rs b/crates/iota-core/src/transaction_manager.rs index 527897c08e1..0192e28bc37 100644 --- a/crates/iota-core/src/transaction_manager.rs +++ b/crates/iota-core/src/transaction_manager.rs @@ -64,7 +64,7 @@ pub struct TransactionManager { #[derive(Clone, Debug)] pub struct PendingCertificateStats { // The time this certificate enters transaction manager. - #[allow(unused)] + #[cfg(test)] pub enqueue_time: Instant, // The time this certificate becomes ready for execution. pub ready_time: Option<Instant>, @@ -556,6 +556,7 @@ impl TransactionManager { expected_effects_digest, waiting_input_objects: input_object_keys, stats: PendingCertificateStats { + #[cfg(test)] enqueue_time: pending_cert_enqueue_time, ready_time: None, }, diff --git a/crates/iota-core/src/transaction_orchestrator.rs b/crates/iota-core/src/transaction_orchestrator.rs index 5dc3ae32188..9aee380abea 100644 --- a/crates/iota-core/src/transaction_orchestrator.rs +++ b/crates/iota-core/src/transaction_orchestrator.rs @@ -347,7 +347,6 @@ where let digests = [tx_digest]; let effects_await = cache_reader.notify_read_executed_effects(&digests); // let-and-return necessary to satisfy borrow checker. - #[allow(clippy::let_and_return)] let res = match select(ticket, effects_await.boxed()).await { Either::Left((quorum_driver_response, _)) => Ok(quorum_driver_response), Either::Right((_, unfinished_quorum_driver_task)) => { @@ -360,6 +359,7 @@ where Ok(unfinished_quorum_driver_task.await) } }; + #[expect(clippy::let_and_return)] res }) } diff --git a/crates/iota-core/src/unit_tests/authority_aggregator_tests.rs b/crates/iota-core/src/unit_tests/authority_aggregator_tests.rs index bfb2e944747..ac734a40e8f 100644 --- a/crates/iota-core/src/unit_tests/authority_aggregator_tests.rs +++ b/crates/iota-core/src/unit_tests/authority_aggregator_tests.rs @@ -345,254 +345,262 @@ fn effects_with_tx(digest: TransactionDigest) -> TransactionEffects { effects } -/// The intent of this is to test whether client side timeouts -/// have any impact on the server execution. Turns out because -/// we spawn a tokio task on the server, client timing out and -/// terminating the connection does not stop server from completing -/// execution on its side -#[sim_test(config = "constant_latency_ms(1)")] -async fn test_quorum_map_and_reduce_timeout() { - let build_config = BuildConfig::new_for_testing(); - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.extend(["src", "unit_tests", "data", "object_basics"]); - let client_ip = make_socket_addr(); - let modules: Vec<_> = build_config - .build(&path) - .unwrap() - .get_modules() - .cloned() - .collect(); - let pkg = Object::new_package_for_testing( - &modules, - TransactionDigest::genesis_marker(), - BuiltInFramework::genesis_move_packages(), - ) - .unwrap(); - let (addr1, key1): (_, AccountKeyPair) = get_key_pair(); - let gas_object1 = Object::with_owner_for_testing(addr1); - let genesis_objects = vec![pkg.clone(), gas_object1.clone()]; - let (mut authorities, _, genesis, _) = init_local_authorities(4, genesis_objects).await; - let rgp = reference_gas_price(&authorities); - let pkg = genesis.object(pkg.id()).unwrap(); - let gas_object1 = genesis.object(gas_object1.id()).unwrap(); - let gas_ref_1 = gas_object1.compute_object_reference(); - let tx = create_object_move_transaction(addr1, &key1, addr1, 100, pkg.id(), gas_ref_1, rgp); - let certified_tx = authorities - .process_transaction(tx.clone(), Some(client_ip)) - .await; - assert!(certified_tx.is_ok()); - let certificate = certified_tx.unwrap().into_cert_for_testing(); - // Send request with a very small timeout to trigger timeout error - authorities.timeouts.pre_quorum_timeout = Duration::from_nanos(0); - authorities.timeouts.post_quorum_timeout = Duration::from_nanos(0); - let request = HandleCertificateRequestV1 { - certificate: certificate.clone(), - include_events: true, - include_input_objects: false, - include_output_objects: false, - include_auxiliary_data: false, - }; - let certified_effects = authorities - .process_certificate(request, Some(client_ip)) - .await; - // Ensure it is an error - assert!(certified_effects.is_err()); - assert!(matches!( - certified_effects, - Err(AggregatorProcessCertificateError::RetryableExecuteCertificate { .. }) - )); - tokio::time::sleep(tokio::time::Duration::from_secs(10)).await; - let tx_info = TransactionInfoRequest { - transaction_digest: *tx.digest(), - }; - for (_, client) in authorities.authority_clients.iter() { - let resp = client - .handle_transaction_info_request(tx_info.clone()) +mod move_tests { + use super::*; + + /// The intent of this is to test whether client side timeouts + /// have any impact on the server execution. Turns out because + /// we spawn a tokio task on the server, client timing out and + /// terminating the connection does not stop server from completing + /// execution on its side + #[sim_test(config = "constant_latency_ms(1)")] + async fn test_quorum_map_and_reduce_timeout() { + let build_config = BuildConfig::new_for_testing(); + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.extend(["src", "unit_tests", "data", "object_basics"]); + let client_ip = make_socket_addr(); + let modules: Vec<_> = build_config + .build(&path) + .unwrap() + .get_modules() + .cloned() + .collect(); + let pkg = Object::new_package_for_testing( + &modules, + TransactionDigest::genesis_marker(), + BuiltInFramework::genesis_move_packages(), + ) + .unwrap(); + let (addr1, key1): (_, AccountKeyPair) = get_key_pair(); + let gas_object1 = Object::with_owner_for_testing(addr1); + let genesis_objects = vec![pkg.clone(), gas_object1.clone()]; + let (mut authorities, _, genesis, _) = init_local_authorities(4, genesis_objects).await; + let rgp = reference_gas_price(&authorities); + let pkg = genesis.object(pkg.id()).unwrap(); + let gas_object1 = genesis.object(gas_object1.id()).unwrap(); + let gas_ref_1 = gas_object1.compute_object_reference(); + let tx = create_object_move_transaction(addr1, &key1, addr1, 100, pkg.id(), gas_ref_1, rgp); + let certified_tx = authorities + .process_transaction(tx.clone(), Some(client_ip)) .await; - // Server should return a signed effect even though previous calls - // failed due to timeout - assert!(resp.is_ok()); - assert!(resp.unwrap().is_executed()); + assert!(certified_tx.is_ok()); + let certificate = certified_tx.unwrap().into_cert_for_testing(); + // Send request with a very small timeout to trigger timeout error + authorities.timeouts.pre_quorum_timeout = Duration::from_nanos(0); + authorities.timeouts.post_quorum_timeout = Duration::from_nanos(0); + let request = HandleCertificateRequestV1 { + certificate: certificate.clone(), + include_events: true, + include_input_objects: false, + include_output_objects: false, + include_auxiliary_data: false, + }; + let certified_effects = authorities + .process_certificate(request, Some(client_ip)) + .await; + // Ensure it is an error + assert!(certified_effects.is_err()); + assert!(matches!( + certified_effects, + Err(AggregatorProcessCertificateError::RetryableExecuteCertificate { .. }) + )); + tokio::time::sleep(tokio::time::Duration::from_secs(10)).await; + let tx_info = TransactionInfoRequest { + transaction_digest: *tx.digest(), + }; + for (_, client) in authorities.authority_clients.iter() { + let resp = client + .handle_transaction_info_request(tx_info.clone()) + .await; + // Server should return a signed effect even though previous calls + // failed due to timeout + assert!(resp.is_ok()); + assert!(resp.unwrap().is_executed()); + } } -} -#[sim_test] -async fn test_map_reducer() { - let (authorities, _, _, _) = init_local_authorities(4, vec![]).await; - - // Test: mapper errors do not get propagated up, reducer works - let res = quorum_map_then_reduce_with_timeout::<_, _, _, _, _, (), _, _, _>( - authorities.committee.clone(), - authorities.authority_clients.clone(), - 0usize, - |_name, _client| { - Box::pin(async move { - let res: Result<usize, IotaError> = Err(IotaError::TooManyIncorrectAuthorities { - errors: vec![], - action: "".to_string(), - }); - res - }) - }, - |mut accumulated_state, _authority_name, _authority_weight, result| { - Box::pin(async move { - assert!(matches!( - result, - Err(IotaError::TooManyIncorrectAuthorities { .. }) - )); - accumulated_state += 1; - ReduceOutput::Continue(accumulated_state) - }) - }, - Duration::from_millis(1000), - ) - .await - .unwrap_err(); - assert_eq!(4, res); - - // Test: early end - let res = quorum_map_then_reduce_with_timeout( - authorities.committee.clone(), - authorities.authority_clients.clone(), - 0usize, - |_name, _client| Box::pin(async move { Ok::<(), anyhow::Error>(()) }), - |mut accumulated_state, _authority_name, _authority_weight, _result| { - Box::pin(async move { - if accumulated_state > 2 { - ReduceOutput::Success(accumulated_state) - } else { + #[sim_test] + async fn test_map_reducer() { + let (authorities, _, _, _) = init_local_authorities(4, vec![]).await; + + // Test: mapper errors do not get propagated up, reducer works + let res = quorum_map_then_reduce_with_timeout::<_, _, _, _, _, (), _, _, _>( + authorities.committee.clone(), + authorities.authority_clients.clone(), + 0usize, + |_name, _client| { + Box::pin(async move { + let res: Result<usize, IotaError> = + Err(IotaError::TooManyIncorrectAuthorities { + errors: vec![], + action: "".to_string(), + }); + res + }) + }, + |mut accumulated_state, _authority_name, _authority_weight, result| { + Box::pin(async move { + assert!(matches!( + result, + Err(IotaError::TooManyIncorrectAuthorities { .. }) + )); accumulated_state += 1; ReduceOutput::Continue(accumulated_state) - } - }) - }, - Duration::from_millis(1000), - ) - .await - .unwrap(); - assert_eq!(3, res.0); - - // Test: Global timeout works - let res = quorum_map_then_reduce_with_timeout::<_, _, _, _, _, (), _, _, _>( - authorities.committee.clone(), - authorities.authority_clients.clone(), - 0usize, - |_name, _client| { - Box::pin(async move { - // 10 mins - tokio::time::sleep(Duration::from_secs(10 * 60)).await; - Ok::<(), anyhow::Error>(()) - }) - }, - |_accumulated_state, _authority_name, _authority_weight, _result| { - Box::pin(async move { ReduceOutput::Continue(0) }) - }, - Duration::from_millis(10), - ) - .await - .unwrap_err(); - assert_eq!(0, res); - - // Test: Local timeout works - let bad_auth = *authorities.committee.sample(); - let res = quorum_map_then_reduce_with_timeout::<_, _, _, _, _, (), _, _, _>( - authorities.committee.clone(), - authorities.authority_clients.clone(), - HashSet::new(), - |_name, _client| { - Box::pin(async move { - // 10 mins - if _name == bad_auth { + }) + }, + Duration::from_millis(1000), + ) + .await + .unwrap_err(); + assert_eq!(4, res); + + // Test: early end + let res = quorum_map_then_reduce_with_timeout( + authorities.committee.clone(), + authorities.authority_clients.clone(), + 0usize, + |_name, _client| Box::pin(async move { Ok::<(), anyhow::Error>(()) }), + |mut accumulated_state, _authority_name, _authority_weight, _result| { + Box::pin(async move { + if accumulated_state > 2 { + ReduceOutput::Success(accumulated_state) + } else { + accumulated_state += 1; + ReduceOutput::Continue(accumulated_state) + } + }) + }, + Duration::from_millis(1000), + ) + .await + .unwrap(); + assert_eq!(3, res.0); + + // Test: Global timeout works + let res = quorum_map_then_reduce_with_timeout::<_, _, _, _, _, (), _, _, _>( + authorities.committee.clone(), + authorities.authority_clients.clone(), + 0usize, + |_name, _client| { + Box::pin(async move { + // 10 mins tokio::time::sleep(Duration::from_secs(10 * 60)).await; - } - Ok::<(), anyhow::Error>(()) - }) - }, - |mut accumulated_state, authority_name, _authority_weight, _result| { - Box::pin(async move { - accumulated_state.insert(authority_name); - if accumulated_state.len() <= 3 { - ReduceOutput::Continue(accumulated_state) - } else { - ReduceOutput::ContinueWithTimeout(accumulated_state, Duration::from_millis(10)) - } - }) - }, - // large delay - Duration::from_millis(10 * 60), - ) - .await; - assert_eq!(res.as_ref().unwrap_err().len(), 3); - assert!(!res.as_ref().unwrap_err().contains(&bad_auth)); -} + Ok::<(), anyhow::Error>(()) + }) + }, + |_accumulated_state, _authority_name, _authority_weight, _result| { + Box::pin(async move { ReduceOutput::Continue(0) }) + }, + Duration::from_millis(10), + ) + .await + .unwrap_err(); + assert_eq!(0, res); + + // Test: Local timeout works + let bad_auth = *authorities.committee.sample(); + let res = quorum_map_then_reduce_with_timeout::<_, _, _, _, _, (), _, _, _>( + authorities.committee.clone(), + authorities.authority_clients.clone(), + HashSet::new(), + |_name, _client| { + Box::pin(async move { + // 10 mins + if _name == bad_auth { + tokio::time::sleep(Duration::from_secs(10 * 60)).await; + } + Ok::<(), anyhow::Error>(()) + }) + }, + |mut accumulated_state, authority_name, _authority_weight, _result| { + Box::pin(async move { + accumulated_state.insert(authority_name); + if accumulated_state.len() <= 3 { + ReduceOutput::Continue(accumulated_state) + } else { + ReduceOutput::ContinueWithTimeout( + accumulated_state, + Duration::from_millis(10), + ) + } + }) + }, + // large delay + Duration::from_millis(10 * 60), + ) + .await; + assert_eq!(res.as_ref().unwrap_err().len(), 3); + assert!(!res.as_ref().unwrap_err().contains(&bad_auth)); + } -#[sim_test] -async fn test_process_transaction_fault_success() { - // This test exercises the 4 different possible failing case when one authority - // is faulty. A transaction is sent to all authories, however one of them - // will error out either before or after processing the transaction. - // A cert should still be created, and sent out to all authorities again. This - // time a different authority errors out either before or after processing - // the cert. - for i in 0..4 { - let mut config_before_process_transaction = LocalAuthorityClientFaultConfig::default(); - if i % 2 == 0 { - config_before_process_transaction.fail_before_handle_transaction = true; - } else { - config_before_process_transaction.fail_after_handle_transaction = true; - } - let mut config_before_process_certificate = LocalAuthorityClientFaultConfig::default(); - if i < 2 { - config_before_process_certificate.fail_before_handle_confirmation = true; - } else { - config_before_process_certificate.fail_after_handle_confirmation = true; + #[sim_test] + async fn test_process_transaction_fault_success() { + // This test exercises the 4 different possible failing case when one authority + // is faulty. A transaction is sent to all authories, however one of them + // will error out either before or after processing the transaction. + // A cert should still be created, and sent out to all authorities again. This + // time a different authority errors out either before or after processing + // the cert. + for i in 0..4 { + let mut config_before_process_transaction = LocalAuthorityClientFaultConfig::default(); + if i % 2 == 0 { + config_before_process_transaction.fail_before_handle_transaction = true; + } else { + config_before_process_transaction.fail_after_handle_transaction = true; + } + let mut config_before_process_certificate = LocalAuthorityClientFaultConfig::default(); + if i < 2 { + config_before_process_certificate.fail_before_handle_confirmation = true; + } else { + config_before_process_certificate.fail_after_handle_confirmation = true; + } + assert!( + execute_transaction_with_fault_configs( + &[(0, config_before_process_transaction)], + &[(1, config_before_process_certificate)], + ) + .await + ); } + } + + #[sim_test] + async fn test_process_transaction_fault_fail() { + // This test exercises the cases when there are 2 authorities faulty, + // and hence no quorum could be formed. This is tested on the + // process_transaction phase. + let fail_before_process_transaction_config = LocalAuthorityClientFaultConfig { + fail_before_handle_transaction: true, + ..Default::default() + }; assert!( - execute_transaction_with_fault_configs(&[(0, config_before_process_transaction)], &[( - 1, - config_before_process_certificate - )],) + !execute_transaction_with_fault_configs( + &[ + (0, fail_before_process_transaction_config), + (1, fail_before_process_transaction_config), + ], + &[], + ) .await ); } -} -#[sim_test] -async fn test_process_transaction_fault_fail() { - // This test exercises the cases when there are 2 authorities faulty, - // and hence no quorum could be formed. This is tested on the - // process_transaction phase. - let fail_before_process_transaction_config = LocalAuthorityClientFaultConfig { - fail_before_handle_transaction: true, - ..Default::default() - }; - assert!( - !execute_transaction_with_fault_configs( - &[ - (0, fail_before_process_transaction_config), - (1, fail_before_process_transaction_config), - ], - &[], - ) - .await - ); -} - -#[sim_test] -async fn test_process_certificate_fault_fail() { - // Similar to test_process_transaction_fault_fail but tested on the - // process_certificate phase. - let fail_before_process_certificate_config = LocalAuthorityClientFaultConfig { - fail_before_handle_confirmation: true, - ..Default::default() - }; - assert!( - !execute_transaction_with_fault_configs(&[], &[ - (0, fail_before_process_certificate_config), - (1, fail_before_process_certificate_config), - ],) - .await - ); + #[sim_test] + async fn test_process_certificate_fault_fail() { + // Similar to test_process_transaction_fault_fail but tested on the + // process_certificate phase. + let fail_before_process_certificate_config = LocalAuthorityClientFaultConfig { + fail_before_handle_confirmation: true, + ..Default::default() + }; + assert!( + !execute_transaction_with_fault_configs(&[], &[ + (0, fail_before_process_certificate_config), + (1, fail_before_process_certificate_config), + ],) + .await + ); + } } #[tokio::test(start_paused = true)] @@ -661,7 +669,7 @@ async fn test_quorum_once_with_timeout() { ); } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] fn get_authorities( count: Arc<Mutex<u32>>, committee_size: u64, @@ -2243,7 +2251,7 @@ async fn test_process_transaction_again() { } } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] fn make_fake_authorities() -> ( BTreeMap<AuthorityName, StakeUnit>, BTreeMap<AuthorityName, HandleTransactionTestAuthorityClient>, diff --git a/crates/iota-core/src/unit_tests/execution_driver_tests.rs b/crates/iota-core/src/unit_tests/execution_driver_tests.rs index a2a51628601..e41d0583860 100644 --- a/crates/iota-core/src/unit_tests/execution_driver_tests.rs +++ b/crates/iota-core/src/unit_tests/execution_driver_tests.rs @@ -51,7 +51,7 @@ use crate::{ }, }; -#[allow(dead_code)] +#[expect(dead_code)] async fn wait_for_certs( stream: &mut UnboundedReceiver<VerifiedCertificate>, certs: &[VerifiedCertificate], @@ -298,268 +298,300 @@ async fn execute_shared_on_first_three_authorities( (cert, effects) } -#[tokio::test(flavor = "current_thread", start_paused = true)] -async fn test_execution_with_dependencies() { - telemetry_subscribers::init_for_testing(); - - // ---- Initialize a network with three accounts, each with 10 gas objects. - - const NUM_ACCOUNTS: usize = 3; - let accounts: Vec<(_, AccountKeyPair)> = - (0..NUM_ACCOUNTS).map(|_| get_key_pair()).collect_vec(); - - const NUM_GAS_OBJECTS_PER_ACCOUNT: usize = 10; - let gas_objects = (0..NUM_ACCOUNTS) - .map(|i| { - (0..NUM_GAS_OBJECTS_PER_ACCOUNT) - .map(|_| Object::with_owner_for_testing(accounts[i].0)) - .collect_vec() - }) - .collect_vec(); - let all_gas_objects = gas_objects.clone().into_iter().flatten().collect_vec(); - - let (aggregator, authorities, _genesis, package) = - init_local_authorities(4, all_gas_objects.clone()).await; - let authority_clients: Vec<_> = authorities - .iter() - .map(|a| aggregator.authority_clients[&a.name].clone()) - .collect(); - let rgp = authorities - .first() - .unwrap() - .reference_gas_price_for_testing() - .unwrap(); - - // ---- Create an owned object and a shared counter. - - let mut executed_owned_certs = Vec::new(); - let mut executed_shared_certs = Vec::new(); - - // Initialize an object owned by 1st account. - let (addr1, key1): &(_, AccountKeyPair) = &accounts[0]; - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0][0].id()).await; - let tx1 = create_object_move_transaction(*addr1, key1, *addr1, 100, package, gas_ref, rgp); - let (cert, effects1) = - execute_owned_on_first_three_authorities(&authority_clients, &aggregator.committee, &tx1) - .await; - executed_owned_certs.push(cert); - let mut owned_object_ref = effects1.created()[0].0; - - // Initialize a shared counter, re-using gas_ref_0 so it has to execute after - // tx1. - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0][0].id()).await; - let tx2 = TestTransactionBuilder::new(*addr1, gas_ref, rgp) - .call_counter_create(package) - .build_and_sign(key1); - let (cert, effects2) = - execute_owned_on_first_three_authorities(&authority_clients, &aggregator.committee, &tx2) - .await; - executed_owned_certs.push(cert); - let (mut shared_counter_ref, owner) = effects2.created()[0]; - let shared_counter_initial_version = if let Owner::Shared { - initial_shared_version, - } = owner - { - // Because the gas object used has version 2, the initial lamport timestamp of - // the shared counter is 3. - assert_eq!(initial_shared_version.value(), 3); - initial_shared_version - } else { - panic!("Not a shared object! {:?} {:?}", shared_counter_ref, owner); - }; +mod move_tests { + use super::*; + + #[tokio::test(flavor = "current_thread", start_paused = true)] + async fn test_execution_with_dependencies() { + telemetry_subscribers::init_for_testing(); + + // ---- Initialize a network with three accounts, each with 10 gas objects. + + const NUM_ACCOUNTS: usize = 3; + let accounts: Vec<(_, AccountKeyPair)> = + (0..NUM_ACCOUNTS).map(|_| get_key_pair()).collect_vec(); + + const NUM_GAS_OBJECTS_PER_ACCOUNT: usize = 10; + let gas_objects = (0..NUM_ACCOUNTS) + .map(|i| { + (0..NUM_GAS_OBJECTS_PER_ACCOUNT) + .map(|_| Object::with_owner_for_testing(accounts[i].0)) + .collect_vec() + }) + .collect_vec(); + let all_gas_objects = gas_objects.clone().into_iter().flatten().collect_vec(); + + let (aggregator, authorities, _genesis, package) = + init_local_authorities(4, all_gas_objects.clone()).await; + let authority_clients: Vec<_> = authorities + .iter() + .map(|a| aggregator.authority_clients[&a.name].clone()) + .collect(); + let rgp = authorities + .first() + .unwrap() + .reference_gas_price_for_testing() + .unwrap(); - // ---- Execute transactions with dependencies on first 3 nodes in the - // dependency order. + // ---- Create an owned object and a shared counter. - // In each iteration, creates an owned and a shared transaction that depends on - // previous input and gas objects. - for i in 0..100 { - let source_index = i % NUM_ACCOUNTS; - let (source_addr, source_key) = &accounts[source_index]; + let mut executed_owned_certs = Vec::new(); + let mut executed_shared_certs = Vec::new(); - let gas_ref = get_latest_ref( - authority_clients[source_index].clone(), - gas_objects[source_index][i * 3 % NUM_GAS_OBJECTS_PER_ACCOUNT].id(), + // Initialize an object owned by 1st account. + let (addr1, key1): &(_, AccountKeyPair) = &accounts[0]; + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0][0].id()).await; + let tx1 = create_object_move_transaction(*addr1, key1, *addr1, 100, package, gas_ref, rgp); + let (cert, effects1) = execute_owned_on_first_three_authorities( + &authority_clients, + &aggregator.committee, + &tx1, ) .await; - let (dest_addr, _) = &accounts[(i + 1) % NUM_ACCOUNTS]; - let owned_tx = make_transfer_object_move_transaction( - *source_addr, - source_key, - *dest_addr, - owned_object_ref, - package, - gas_ref, - TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE, - rgp, - ); - let (cert, effects) = execute_owned_on_first_three_authorities( + executed_owned_certs.push(cert); + let mut owned_object_ref = effects1.created()[0].0; + + // Initialize a shared counter, re-using gas_ref_0 so it has to execute after + // tx1. + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0][0].id()).await; + let tx2 = TestTransactionBuilder::new(*addr1, gas_ref, rgp) + .call_counter_create(package) + .build_and_sign(key1); + let (cert, effects2) = execute_owned_on_first_three_authorities( &authority_clients, &aggregator.committee, - &owned_tx, + &tx2, ) .await; executed_owned_certs.push(cert); - owned_object_ref = effects.mutated_excluding_gas().first().unwrap().0; + let (mut shared_counter_ref, owner) = effects2.created()[0]; + let shared_counter_initial_version = if let Owner::Shared { + initial_shared_version, + } = owner + { + // Because the gas object used has version 2, the initial lamport timestamp of + // the shared counter is 3. + assert_eq!(initial_shared_version.value(), 3); + initial_shared_version + } else { + panic!("Not a shared object! {:?} {:?}", shared_counter_ref, owner); + }; - let gas_ref = get_latest_ref( - authority_clients[source_index].clone(), - gas_objects[source_index][i * 7 % NUM_GAS_OBJECTS_PER_ACCOUNT].id(), - ) - .await; - let shared_tx = TestTransactionBuilder::new(*source_addr, gas_ref, rgp) - .call_counter_increment( + // ---- Execute transactions with dependencies on first 3 nodes in the + // dependency order. + + // In each iteration, creates an owned and a shared transaction that depends on + // previous input and gas objects. + for i in 0..100 { + let source_index = i % NUM_ACCOUNTS; + let (source_addr, source_key) = &accounts[source_index]; + + let gas_ref = get_latest_ref( + authority_clients[source_index].clone(), + gas_objects[source_index][i * 3 % NUM_GAS_OBJECTS_PER_ACCOUNT].id(), + ) + .await; + let (dest_addr, _) = &accounts[(i + 1) % NUM_ACCOUNTS]; + let owned_tx = make_transfer_object_move_transaction( + *source_addr, + source_key, + *dest_addr, + owned_object_ref, package, - shared_counter_ref.0, - shared_counter_initial_version, + gas_ref, + TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE, + rgp, + ); + let (cert, effects) = execute_owned_on_first_three_authorities( + &authority_clients, + &aggregator.committee, + &owned_tx, ) - .build_and_sign(source_key); - let (cert, effects) = execute_shared_on_first_three_authorities( - &authority_clients, - &aggregator.committee, - &shared_tx, - ) - .await; - executed_shared_certs.push(cert); - shared_counter_ref = effects.mutated_excluding_gas().first().unwrap().0; - } + .await; + executed_owned_certs.push(cert); + owned_object_ref = effects.mutated_excluding_gas().first().unwrap().0; - // ---- Execute transactions in reverse dependency order on the last authority. + let gas_ref = get_latest_ref( + authority_clients[source_index].clone(), + gas_objects[source_index][i * 7 % NUM_GAS_OBJECTS_PER_ACCOUNT].id(), + ) + .await; + let shared_tx = TestTransactionBuilder::new(*source_addr, gas_ref, rgp) + .call_counter_increment( + package, + shared_counter_ref.0, + shared_counter_initial_version, + ) + .build_and_sign(source_key); + let (cert, effects) = execute_shared_on_first_three_authorities( + &authority_clients, + &aggregator.committee, + &shared_tx, + ) + .await; + executed_shared_certs.push(cert); + shared_counter_ref = effects.mutated_excluding_gas().first().unwrap().0; + } - // Sets shared object locks in the executed order. - for cert in executed_shared_certs.iter() { - send_consensus_no_execution(&authorities[3], cert).await; - } + // ---- Execute transactions in reverse dependency order on the last authority. - // Enqueue certs out of dependency order for executions. - for cert in executed_shared_certs.iter().rev() { - authorities[3].enqueue_certificates_for_execution( - vec![cert.clone()], - &authorities[3].epoch_store_for_testing(), - ); - } - for cert in executed_owned_certs.iter().rev() { - authorities[3].enqueue_certificates_for_execution( - vec![cert.clone()], - &authorities[3].epoch_store_for_testing(), - ); - } + // Sets shared object locks in the executed order. + for cert in executed_shared_certs.iter() { + send_consensus_no_execution(&authorities[3], cert).await; + } - // All certs should get executed eventually. - let digests: Vec<_> = executed_shared_certs - .iter() - .chain(executed_owned_certs.iter()) - .map(|cert| *cert.digest()) - .collect(); - authorities[3] - .get_transaction_cache_reader() - .notify_read_executed_effects(&digests) - .await - .unwrap(); -} + // Enqueue certs out of dependency order for executions. + for cert in executed_shared_certs.iter().rev() { + authorities[3].enqueue_certificates_for_execution( + vec![cert.clone()], + &authorities[3].epoch_store_for_testing(), + ); + } + for cert in executed_owned_certs.iter().rev() { + authorities[3].enqueue_certificates_for_execution( + vec![cert.clone()], + &authorities[3].epoch_store_for_testing(), + ); + } -fn make_socket_addr() -> std::net::SocketAddr { - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0) -} + // All certs should get executed eventually. + let digests: Vec<_> = executed_shared_certs + .iter() + .chain(executed_owned_certs.iter()) + .map(|cert| *cert.digest()) + .collect(); + authorities[3] + .get_transaction_cache_reader() + .notify_read_executed_effects(&digests) + .await + .unwrap(); + } -async fn try_sign_on_first_three_authorities( - authority_clients: &[Arc<SafeClient<LocalAuthorityClient>>], - committee: &Committee, - txn: &Transaction, -) -> IotaResult<VerifiedCertificate> { - for client in authority_clients.iter().take(3) { - client - .handle_transaction(txn.clone(), Some(make_socket_addr())) - .await?; + fn make_socket_addr() -> std::net::SocketAddr { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0) } - extract_cert(authority_clients, committee, txn.digest()) - .await - .try_into_verified_for_testing(committee, &Default::default()) -} -#[tokio::test(flavor = "current_thread", start_paused = true)] -async fn test_per_object_overload() { - telemetry_subscribers::init_for_testing(); + async fn try_sign_on_first_three_authorities( + authority_clients: &[Arc<SafeClient<LocalAuthorityClient>>], + committee: &Committee, + txn: &Transaction, + ) -> IotaResult<VerifiedCertificate> { + for client in authority_clients.iter().take(3) { + client + .handle_transaction(txn.clone(), Some(make_socket_addr())) + .await?; + } + extract_cert(authority_clients, committee, txn.digest()) + .await + .try_into_verified_for_testing(committee, &Default::default()) + } - // Initialize a network with 1 account and 2000 gas objects. - let (addr, key): (_, AccountKeyPair) = get_key_pair(); - const NUM_GAS_OBJECTS_PER_ACCOUNT: usize = 2000; - let gas_objects = (0..NUM_GAS_OBJECTS_PER_ACCOUNT) - .map(|_| Object::with_owner_for_testing(addr)) - .collect_vec(); - let (aggregator, authorities, _genesis, package) = - init_local_authorities(4, gas_objects.clone()).await; - let rgp = authorities - .first() - .unwrap() - .reference_gas_price_for_testing() + #[tokio::test(flavor = "current_thread", start_paused = true)] + async fn test_per_object_overload() { + telemetry_subscribers::init_for_testing(); + + // Initialize a network with 1 account and 2000 gas objects. + let (addr, key): (_, AccountKeyPair) = get_key_pair(); + const NUM_GAS_OBJECTS_PER_ACCOUNT: usize = 2000; + let gas_objects = (0..NUM_GAS_OBJECTS_PER_ACCOUNT) + .map(|_| Object::with_owner_for_testing(addr)) + .collect_vec(); + let (aggregator, authorities, _genesis, package) = + init_local_authorities(4, gas_objects.clone()).await; + let rgp = authorities + .first() + .unwrap() + .reference_gas_price_for_testing() + .unwrap(); + let authority_clients: Vec<_> = authorities + .iter() + .map(|a| aggregator.authority_clients[&a.name].clone()) + .collect(); + + // Create a shared counter. + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0].id()).await; + let create_counter_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) + .call_counter_create(package) + .build_and_sign(&key); + let create_counter_cert = try_sign_on_first_three_authorities( + &authority_clients, + &aggregator.committee, + &create_counter_txn, + ) + .await .unwrap(); - let authority_clients: Vec<_> = authorities - .iter() - .map(|a| aggregator.authority_clients[&a.name].clone()) - .collect(); - - // Create a shared counter. - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0].id()).await; - let create_counter_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) - .call_counter_create(package) - .build_and_sign(&key); - let create_counter_cert = try_sign_on_first_three_authorities( - &authority_clients, - &aggregator.committee, - &create_counter_txn, - ) - .await - .unwrap(); - for authority in authorities.iter().take(3) { - send_consensus(authority, &create_counter_cert).await; - } - for authority in authorities.iter().take(3) { - authority + for authority in authorities.iter().take(3) { + send_consensus(authority, &create_counter_cert).await; + } + for authority in authorities.iter().take(3) { + authority + .get_transaction_cache_reader() + .notify_read_executed_effects(&[*create_counter_cert.digest()]) + .await + .unwrap() + .pop() + .unwrap(); + } + + // Signing and executing this transaction on the last authority should succeed. + authority_clients[3] + .handle_transaction(create_counter_txn.clone(), Some(make_socket_addr())) + .await + .unwrap(); + send_consensus(&authorities[3], &create_counter_cert).await; + let create_counter_effects = authorities[3] .get_transaction_cache_reader() .notify_read_executed_effects(&[*create_counter_cert.digest()]) .await .unwrap() .pop() .unwrap(); - } + let (shared_counter_ref, owner) = create_counter_effects.created()[0]; + let Owner::Shared { + initial_shared_version: shared_counter_initial_version, + } = owner + else { + panic!("Not a shared object! {:?} {:?}", shared_counter_ref, owner); + }; - // Signing and executing this transaction on the last authority should succeed. - authority_clients[3] - .handle_transaction(create_counter_txn.clone(), Some(make_socket_addr())) - .await - .unwrap(); - send_consensus(&authorities[3], &create_counter_cert).await; - let create_counter_effects = authorities[3] - .get_transaction_cache_reader() - .notify_read_executed_effects(&[*create_counter_cert.digest()]) - .await - .unwrap() - .pop() - .unwrap(); - let (shared_counter_ref, owner) = create_counter_effects.created()[0]; - let Owner::Shared { - initial_shared_version: shared_counter_initial_version, - } = owner - else { - panic!("Not a shared object! {:?} {:?}", shared_counter_ref, owner); - }; + // Stop execution on the last authority, to simulate having a backlog. + authorities[3].shutdown_execution_for_test(); + // Make sure execution driver has exited. + sleep(Duration::from_secs(1)).await; + + // Sign and try execute 1000 txns on the first three authorities. And enqueue + // them on the last authority. First shared counter txn has input object + // available on authority 3. So to overload authority 3, 1 more + // txn is needed. + let num_txns = authorities[3] + .overload_config() + .max_transaction_manager_per_object_queue_length + + 1; + for gas_object in gas_objects.iter().take(num_txns) { + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_object.id()).await; + let shared_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) + .call_counter_increment( + package, + shared_counter_ref.0, + shared_counter_initial_version, + ) + .build_and_sign(&key); + let shared_cert = try_sign_on_first_three_authorities( + &authority_clients, + &aggregator.committee, + &shared_txn, + ) + .await + .unwrap(); + for authority in authorities.iter().take(3) { + send_consensus(authority, &shared_cert).await; + } + send_consensus(&authorities[3], &shared_cert).await; + } - // Stop execution on the last authority, to simulate having a backlog. - authorities[3].shutdown_execution_for_test(); - // Make sure execution driver has exited. - sleep(Duration::from_secs(1)).await; - - // Sign and try execute 1000 txns on the first three authorities. And enqueue - // them on the last authority. First shared counter txn has input object - // available on authority 3. So to overload authority 3, 1 more - // txn is needed. - let num_txns = authorities[3] - .overload_config() - .max_transaction_manager_per_object_queue_length - + 1; - for gas_object in gas_objects.iter().take(num_txns) { - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_object.id()).await; + // Trying to sign a new transaction would now fail. + let gas_ref = + get_latest_ref(authority_clients[0].clone(), gas_objects[num_txns].id()).await; let shared_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) .call_counter_increment( package, @@ -567,125 +599,129 @@ async fn test_per_object_overload() { shared_counter_initial_version, ) .build_and_sign(&key); - let shared_cert = try_sign_on_first_three_authorities( + let res = authorities[3] + .transaction_manager() + .check_execution_overload(authorities[3].overload_config(), shared_txn.data()); + let message = format!("{res:?}"); + assert!( + message.contains("TooManyTransactionsPendingOnObject"), + "{}", + message + ); + } + + #[tokio::test] + async fn test_txn_age_overload() { + telemetry_subscribers::init_for_testing(); + + // Initialize a network with 1 account and 3 gas objects. + let (addr, key): (_, AccountKeyPair) = get_key_pair(); + let gas_objects = (0..3) + .map(|_| Object::with_owner_for_testing(addr)) + .collect_vec(); + let (aggregator, authorities, _genesis, package) = + init_local_authorities_with_overload_thresholds( + 4, + gas_objects.clone(), + AuthorityOverloadConfig { + max_txn_age_in_queue: Duration::from_secs(5), + ..Default::default() + }, + ) + .await; + let rgp = authorities + .first() + .unwrap() + .reference_gas_price_for_testing() + .unwrap(); + let authority_clients: Vec<_> = authorities + .iter() + .map(|a| aggregator.authority_clients[&a.name].clone()) + .collect(); + + // Create a shared counter. + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0].id()).await; + let create_counter_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) + .call_counter_create(package) + .build_and_sign(&key); + let create_counter_cert = try_sign_on_first_three_authorities( &authority_clients, &aggregator.committee, - &shared_txn, + &create_counter_txn, ) .await .unwrap(); for authority in authorities.iter().take(3) { - send_consensus(authority, &shared_cert).await; + send_consensus(authority, &create_counter_cert).await; + } + for authority in authorities.iter().take(3) { + authority + .get_transaction_cache_reader() + .notify_read_executed_effects(&[*create_counter_cert.digest()]) + .await + .unwrap() + .pop() + .unwrap(); } - send_consensus(&authorities[3], &shared_cert).await; - } - - // Trying to sign a new transaction would now fail. - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[num_txns].id()).await; - let shared_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) - .call_counter_increment( - package, - shared_counter_ref.0, - shared_counter_initial_version, - ) - .build_and_sign(&key); - let res = authorities[3] - .transaction_manager() - .check_execution_overload(authorities[3].overload_config(), shared_txn.data()); - let message = format!("{res:?}"); - assert!( - message.contains("TooManyTransactionsPendingOnObject"), - "{}", - message - ); -} - -#[tokio::test] -async fn test_txn_age_overload() { - telemetry_subscribers::init_for_testing(); - // Initialize a network with 1 account and 3 gas objects. - let (addr, key): (_, AccountKeyPair) = get_key_pair(); - let gas_objects = (0..3) - .map(|_| Object::with_owner_for_testing(addr)) - .collect_vec(); - let (aggregator, authorities, _genesis, package) = - init_local_authorities_with_overload_thresholds( - 4, - gas_objects.clone(), - AuthorityOverloadConfig { - max_txn_age_in_queue: Duration::from_secs(5), - ..Default::default() - }, - ) - .await; - let rgp = authorities - .first() - .unwrap() - .reference_gas_price_for_testing() - .unwrap(); - let authority_clients: Vec<_> = authorities - .iter() - .map(|a| aggregator.authority_clients[&a.name].clone()) - .collect(); - - // Create a shared counter. - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[0].id()).await; - let create_counter_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) - .call_counter_create(package) - .build_and_sign(&key); - let create_counter_cert = try_sign_on_first_three_authorities( - &authority_clients, - &aggregator.committee, - &create_counter_txn, - ) - .await - .unwrap(); - for authority in authorities.iter().take(3) { - send_consensus(authority, &create_counter_cert).await; - } - for authority in authorities.iter().take(3) { - authority + // Signing and executing this transaction on the last authority should succeed. + authority_clients[3] + .handle_transaction(create_counter_txn.clone(), Some(make_socket_addr())) + .await + .unwrap(); + send_consensus(&authorities[3], &create_counter_cert).await; + let create_counter_effects = authorities[3] .get_transaction_cache_reader() .notify_read_executed_effects(&[*create_counter_cert.digest()]) .await .unwrap() .pop() .unwrap(); - } + let (shared_counter_ref, owner) = create_counter_effects.created()[0]; + let Owner::Shared { + initial_shared_version: shared_counter_initial_version, + } = owner + else { + panic!("Not a shared object! {:?} {:?}", shared_counter_ref, owner); + }; - // Signing and executing this transaction on the last authority should succeed. - authority_clients[3] - .handle_transaction(create_counter_txn.clone(), Some(make_socket_addr())) - .await - .unwrap(); - send_consensus(&authorities[3], &create_counter_cert).await; - let create_counter_effects = authorities[3] - .get_transaction_cache_reader() - .notify_read_executed_effects(&[*create_counter_cert.digest()]) - .await - .unwrap() - .pop() - .unwrap(); - let (shared_counter_ref, owner) = create_counter_effects.created()[0]; - let Owner::Shared { - initial_shared_version: shared_counter_initial_version, - } = owner - else { - panic!("Not a shared object! {:?} {:?}", shared_counter_ref, owner); - }; + // Stop execution on the last authority, to simulate having a backlog. + authorities[3].shutdown_execution_for_test(); + // Make sure execution driver has exited. + sleep(Duration::from_secs(1)).await; + + // Sign and try execute 2 txns on the first three authorities. And enqueue them + // on the last authority. First shared counter txn has input object + // available on authority 3. So to put a txn in the queue, we + // will need another txn. + for gas_object in gas_objects.iter().take(2) { + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_object.id()).await; + let shared_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) + .call_counter_increment( + package, + shared_counter_ref.0, + shared_counter_initial_version, + ) + .build_and_sign(&key); + let shared_cert = try_sign_on_first_three_authorities( + &authority_clients, + &aggregator.committee, + &shared_txn, + ) + .await + .unwrap(); + for authority in authorities.iter().take(3) { + send_consensus(authority, &shared_cert).await; + } + send_consensus(&authorities[3], &shared_cert).await; + } + + // Sleep for 6 seconds to make sure the transaction is old enough since our + // threshold is 5. + tokio::time::sleep(Duration::from_secs(6)).await; - // Stop execution on the last authority, to simulate having a backlog. - authorities[3].shutdown_execution_for_test(); - // Make sure execution driver has exited. - sleep(Duration::from_secs(1)).await; - - // Sign and try execute 2 txns on the first three authorities. And enqueue them - // on the last authority. First shared counter txn has input object - // available on authority 3. So to put a txn in the queue, we - // will need another txn. - for gas_object in gas_objects.iter().take(2) { - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_object.id()).await; + // Trying to sign a new transaction would now fail. + let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[2].id()).await; let shared_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) .call_counter_increment( package, @@ -693,41 +729,16 @@ async fn test_txn_age_overload() { shared_counter_initial_version, ) .build_and_sign(&key); - let shared_cert = try_sign_on_first_three_authorities( - &authority_clients, - &aggregator.committee, - &shared_txn, - ) - .await - .unwrap(); - for authority in authorities.iter().take(3) { - send_consensus(authority, &shared_cert).await; - } - send_consensus(&authorities[3], &shared_cert).await; + let res = authorities[3] + .transaction_manager() + .check_execution_overload(authorities[3].overload_config(), shared_txn.data()); + let message = format!("{res:?}"); + assert!( + message.contains("TooOldTransactionPendingOnObject"), + "{}", + message + ); } - - // Sleep for 6 seconds to make sure the transaction is old enough since our - // threshold is 5. - tokio::time::sleep(Duration::from_secs(6)).await; - - // Trying to sign a new transaction would now fail. - let gas_ref = get_latest_ref(authority_clients[0].clone(), gas_objects[2].id()).await; - let shared_txn = TestTransactionBuilder::new(addr, gas_ref, rgp) - .call_counter_increment( - package, - shared_counter_ref.0, - shared_counter_initial_version, - ) - .build_and_sign(&key); - let res = authorities[3] - .transaction_manager() - .check_execution_overload(authorities[3].overload_config(), shared_txn.data()); - let message = format!("{res:?}"); - assert!( - message.contains("TooOldTransactionPendingOnObject"), - "{}", - message - ); } // Tests that when validator is in load shedding mode, it can pushback txn diff --git a/crates/iota-core/src/unit_tests/transaction_manager_tests.rs b/crates/iota-core/src/unit_tests/transaction_manager_tests.rs index 7868b8d4335..2f6abcaac31 100644 --- a/crates/iota-core/src/unit_tests/transaction_manager_tests.rs +++ b/crates/iota-core/src/unit_tests/transaction_manager_tests.rs @@ -24,7 +24,7 @@ use crate::{ transaction_manager::{PendingCertificate, TransactionManager}, }; -#[allow(clippy::disallowed_methods)] // allow unbounded_channel() +#[expect(clippy::disallowed_methods)] // allow unbounded_channel() fn make_transaction_manager( state: &AuthorityState, ) -> (TransactionManager, UnboundedReceiver<PendingCertificate>) { diff --git a/crates/iota-core/src/unit_tests/transfer_to_object_tests.rs b/crates/iota-core/src/unit_tests/transfer_to_object_tests.rs index 9fab1b9a0ee..61795f7bbf5 100644 --- a/crates/iota-core/src/unit_tests/transfer_to_object_tests.rs +++ b/crates/iota-core/src/unit_tests/transfer_to_object_tests.rs @@ -425,7 +425,7 @@ async fn test_tto_invalid_receiving_arguments() { .find(|(_, owner)| matches!(owner, Owner::ObjectOwner(_))) .unwrap(); - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] let mutations: Vec<( Box<dyn FnOnce(ObjectRef) -> ObjectRef>, Box<dyn FnOnce(UserInputError) -> bool>, diff --git a/crates/iota-e2e-tests/tests/dynamic_committee_tests.rs b/crates/iota-e2e-tests/tests/dynamic_committee_tests.rs index 0558a0e0e7e..c76e4922dbe 100644 --- a/crates/iota-e2e-tests/tests/dynamic_committee_tests.rs +++ b/crates/iota-e2e-tests/tests/dynamic_committee_tests.rs @@ -59,7 +59,7 @@ trait StatePredicate { runner: &StressTestRunner, effects: &TransactionEffects, ); - #[allow(unused)] + #[expect(unused)] async fn post_epoch_post_condition( &mut self, runner: &StressTestRunner, @@ -67,7 +67,7 @@ trait StatePredicate { ); } -#[allow(dead_code)] +#[expect(dead_code)] struct StressTestRunner { pub post_epoch_predicates: Vec<Box<dyn StatePredicate + Send + Sync>>, pub test_cluster: TestCluster, @@ -234,7 +234,7 @@ impl StressTestRunner { self.get_from_effects(&effects.created(), name).await } - #[allow(dead_code)] + #[expect(dead_code)] pub async fn get_mutated_object_of_type_name( &self, effects: &TransactionEffects, diff --git a/crates/iota-e2e-tests/tests/full_node_migration_tests.rs b/crates/iota-e2e-tests/tests/full_node_migration_tests.rs index e6c79e17779..c7f8c281b77 100644 --- a/crates/iota-e2e-tests/tests/full_node_migration_tests.rs +++ b/crates/iota-e2e-tests/tests/full_node_migration_tests.rs @@ -1,11 +1,24 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::{path::PathBuf, str::FromStr}; +use std::{ + fs::File, + io::{BufWriter, Write}, + path::PathBuf, + str::FromStr, +}; use anyhow::anyhow; use bip32::DerivationPath; -use iota_genesis_builder::SnapshotSource; +use iota_genesis_builder::{ + SnapshotSource, + stardust::{ + migration::{Migration, MigrationTargetNetwork}, + parse::HornetSnapshotParser, + process_outputs::scale_amount_for_iota, + types::address_swap_map::AddressSwapMap, + }, +}; use iota_json_rpc_types::{ IotaObjectDataFilter, IotaObjectDataOptions, IotaObjectResponseQuery, IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, @@ -21,7 +34,7 @@ use iota_types::{ gas_coin::GAS, programmable_transaction_builder::ProgrammableTransactionBuilder, quorum_driver_types::ExecuteTransactionRequestType, - stardust::output::NftOutput, + stardust::{coin_type::CoinType, output::NftOutput}, transaction::{Argument, ObjectArg, Transaction, TransactionData}, }; use move_core_types::ident_str; @@ -29,7 +42,10 @@ use shared_crypto::intent::Intent; use tempfile::tempdir; use test_cluster::TestClusterBuilder; -const MIGRATION_DATA_PATH: &str = "tests/migration/stardust_object_snapshot.bin"; +const HORNET_SNAPSHOT_PATH: &str = "tests/migration/test_hornet_full_snapshot.bin"; +const ADDRESS_SWAP_MAP_PATH: &str = "tests/migration/address_swap.csv"; +const TEST_TARGET_NETWORK: &str = "alphanet-test"; +const MIGRATION_DATA_FILE_NAME: &str = "stardust_object_snapshot.bin"; /// Got from iota-genesis-builder/src/stardust/test_outputs/alias_ownership.rs const MAIN_ADDRESS_MNEMONIC: &str = "few hood high omit camp keep burger give happy iron evolve draft few dawn pulp jazz box dash load snake gown bag draft car"; @@ -39,26 +55,67 @@ const SPONSOR_ADDRESS_MNEMONIC: &str = "okay pottery arch air egg very cave cash #[sim_test] async fn test_full_node_load_migration_data() -> Result<(), anyhow::Error> { telemetry_subscribers::init_for_testing(); - let snapshot_source = SnapshotSource::Local(PathBuf::from_str(MIGRATION_DATA_PATH).unwrap()); + + // Setup the temporary dir and create the writer for the stardust object + // snapshot + let dir = tempdir()?; + let stardudst_object_snapshot_file_path = dir.path().join(MIGRATION_DATA_FILE_NAME); + let object_snapshot_writer = + BufWriter::new(File::create(&stardudst_object_snapshot_file_path)?); + + // Generate the stardust object snapshot + genesis_builder_snapshot_generation(object_snapshot_writer)?; + // Then load it + let snapshot_source = SnapshotSource::Local(stardudst_object_snapshot_file_path); + + // A new test cluster can be spawn with the stardust object snapshot let test_cluster = TestClusterBuilder::new() .with_migration_data(vec![snapshot_source]) .build() .await; + // Use a client to issue a test transaction let client = test_cluster.wallet.get_client().await.unwrap(); - let tx_response = address_unlock_condition(client).await?; - let IotaTransactionBlockResponse { confirmed_local_execution, errors, .. } = tx_response; + + // The transaction must be successful assert!(confirmed_local_execution.unwrap()); assert!(errors.is_empty()); Ok(()) } +fn genesis_builder_snapshot_generation( + object_snapshot_writer: impl Write, +) -> Result<(), anyhow::Error> { + let mut snapshot_parser = + HornetSnapshotParser::new::<false>(File::open(HORNET_SNAPSHOT_PATH)?)?; + let total_supply = scale_amount_for_iota(snapshot_parser.total_supply()?)?; + let target_network = MigrationTargetNetwork::from_str(TEST_TARGET_NETWORK)?; + let coin_type = CoinType::Iota; + let address_swap_map = AddressSwapMap::from_csv(ADDRESS_SWAP_MAP_PATH)?; + + // Migrate using the parser output stream + Migration::new( + snapshot_parser.target_milestone_timestamp(), + total_supply, + target_network, + coin_type, + address_swap_map, + )? + .run_for_iota( + snapshot_parser.target_milestone_timestamp(), + snapshot_parser.outputs(), + object_snapshot_writer, + )?; + + Ok(()) +} + async fn address_unlock_condition( iota_client: IotaClient, ) -> Result<IotaTransactionBlockResponse, anyhow::Error> { diff --git a/crates/iota-e2e-tests/tests/migration/address_swap.csv b/crates/iota-e2e-tests/tests/migration/address_swap.csv new file mode 100644 index 00000000000..7ece8894744 --- /dev/null +++ b/crates/iota-e2e-tests/tests/migration/address_swap.csv @@ -0,0 +1,2 @@ +Origin,Destination +iota1qp8h9augeh6tk3uvlxqfapuwv93atv63eqkpru029p6sgvr49eufyz7katr,0x4f72f788cdf4bb478cf9809e878e6163d5b351c82c11f1ea28750430752e7892 \ No newline at end of file diff --git a/crates/iota-e2e-tests/tests/migration/stardust_object_snapshot.bin b/crates/iota-e2e-tests/tests/migration/stardust_object_snapshot.bin deleted file mode 100644 index c7fa51debb7..00000000000 Binary files a/crates/iota-e2e-tests/tests/migration/stardust_object_snapshot.bin and /dev/null differ diff --git a/crates/iota-e2e-tests/tests/migration/test_hornet_full_snapshot.bin b/crates/iota-e2e-tests/tests/migration/test_hornet_full_snapshot.bin new file mode 100644 index 00000000000..bac42b91117 Binary files /dev/null and b/crates/iota-e2e-tests/tests/migration/test_hornet_full_snapshot.bin differ diff --git a/crates/iota-e2e-tests/tests/passkey_e2e_tests.rs b/crates/iota-e2e-tests/tests/passkey_e2e_tests.rs index 0f7a1cf583a..82c9c4a9d91 100644 --- a/crates/iota-e2e-tests/tests/passkey_e2e_tests.rs +++ b/crates/iota-e2e-tests/tests/passkey_e2e_tests.rs @@ -17,11 +17,11 @@ use iota_types::{ transaction::{Transaction, TransactionData}, }; use p256::pkcs8::DecodePublicKey; -use passkey_authenticator::{Authenticator, UserValidationMethod}; +use passkey_authenticator::{Authenticator, UserCheck, UserValidationMethod}; use passkey_client::Client; use passkey_types::{ Bytes, Passkey, - ctap2::Aaguid, + ctap2::{Aaguid, Ctap2Error}, rand::random_vec, webauthn::{ AttestationConveyancePreference, CredentialCreationOptions, CredentialRequestOptions, @@ -35,14 +35,21 @@ use test_cluster::{TestCluster, TestClusterBuilder}; use url::Url; struct MyUserValidationMethod {} + #[async_trait::async_trait] impl UserValidationMethod for MyUserValidationMethod { - async fn check_user_presence(&self) -> bool { - true - } + type PasskeyItem = Passkey; - async fn check_user_verification(&self) -> bool { - true + async fn check_user<'a>( + &self, + _credential: Option<&'a Self::PasskeyItem>, + _presence: bool, + _verification: bool, + ) -> Result<UserCheck, Ctap2Error> { + Ok(UserCheck { + presence: true, + verification: true, + }) } fn is_verification_enabled(&self) -> Option<bool> { diff --git a/crates/iota-e2e-tests/tests/shared_objects_tests.rs b/crates/iota-e2e-tests/tests/shared_objects_tests.rs index 8525b378580..cbabd65cf64 100644 --- a/crates/iota-e2e-tests/tests/shared_objects_tests.rs +++ b/crates/iota-e2e-tests/tests/shared_objects_tests.rs @@ -512,7 +512,6 @@ async fn access_clock_object_test() { assert!(event.timestamp_ms <= finish.as_millis() as u64); let mut attempt = 0; - #[allow(clippy::never_loop)] // seem to be a bug in clippy with let else statement loop { let checkpoint = test_cluster .fullnode_handle diff --git a/crates/iota-faucet/src/faucet/simple_faucet.rs b/crates/iota-faucet/src/faucet/simple_faucet.rs index eafb50d4a4a..b130b529e9d 100644 --- a/crates/iota-faucet/src/faucet/simple_faucet.rs +++ b/crates/iota-faucet/src/faucet/simple_faucet.rs @@ -65,7 +65,7 @@ pub struct SimpleFaucet { ttl_expiration: u64, coin_amount: u64, /// Shuts down the batch transfer task. Used only in testing. - #[allow(unused)] + #[cfg_attr(not(test), expect(unused))] batch_transfer_shutdown: parking_lot::Mutex<Option<oneshot::Sender<()>>>, } diff --git a/crates/iota-genesis-builder/src/lib.rs b/crates/iota-genesis-builder/src/lib.rs index b3725eded9b..ac464c309ce 100644 --- a/crates/iota-genesis-builder/src/lib.rs +++ b/crates/iota-genesis-builder/src/lib.rs @@ -470,7 +470,7 @@ impl Builder { } = self.parameters.to_genesis_chain_parameters(); // In non-testing code, genesis type must always be V1. - #[allow(clippy::infallible_destructuring_match)] + #[expect(clippy::infallible_destructuring_match)] let system_state = match unsigned_genesis.iota_system_object() { IotaSystemState::V1(inner) => inner, #[cfg(msim)] diff --git a/crates/iota-genesis-builder/src/main.rs b/crates/iota-genesis-builder/src/main.rs index e0adc0db630..3c79fdc329b 100644 --- a/crates/iota-genesis-builder/src/main.rs +++ b/crates/iota-genesis-builder/src/main.rs @@ -4,26 +4,20 @@ //! Creating a stardust objects snapshot out of a Hornet snapshot. //! TIP that defines the Hornet snapshot file format: //! https://github.com/iotaledger/tips/blob/main/tips/TIP-0035/tip-0035.md -use std::{collections::BTreeMap, fs::File, io::BufWriter}; +use std::{fs::File, io::BufWriter}; -use anyhow::{Result, anyhow}; +use anyhow::Result; use clap::{Parser, Subcommand}; use iota_genesis_builder::{ OBJECT_SNAPSHOT_FILE_PATH, stardust::{ migration::{Migration, MigrationTargetNetwork}, parse::HornetSnapshotParser, - types::{address_swap_map::AddressSwapMap, output_header::OutputHeader}, + process_outputs::scale_amount_for_iota, + types::address_swap_map::AddressSwapMap, }, }; -use iota_sdk::types::block::{ - address::Address, - output::{ - AliasOutputBuilder, BasicOutputBuilder, FoundryOutputBuilder, NftOutputBuilder, Output, - unlock_condition::{AddressUnlockCondition, StorageDepositReturnUnlockCondition}, - }, -}; -use iota_types::{stardust::coin_type::CoinType, timelock::timelock::is_vested_reward}; +use iota_types::stardust::coin_type::CoinType; use tracing::Level; use tracing_subscriber::FmtSubscriber; @@ -104,182 +98,13 @@ fn main() -> Result<()> { match coin_type { CoinType::Iota => { - struct MergingIterator<I> { - unlocked_address_balances: BTreeMap<Address, OutputHeaderWithBalance>, - snapshot_timestamp_s: u32, - outputs: I, - } - - impl<I> MergingIterator<I> { - fn new(snapshot_timestamp_s: u32, outputs: I) -> Self { - Self { - unlocked_address_balances: Default::default(), - snapshot_timestamp_s, - outputs, - } - } - } - - impl<I: Iterator<Item = Result<(OutputHeader, Output)>>> Iterator for MergingIterator<I> { - type Item = I::Item; - - fn next(&mut self) -> Option<Self::Item> { - // First process all the outputs, building the unlocked_address_balances map as - // we go. - for res in self.outputs.by_ref() { - if let Ok((header, output)) = res { - fn mergeable_address( - header: &OutputHeader, - output: &Output, - snapshot_timestamp_s: u32, - ) -> Option<Address> { - // ignore all non-basic outputs and non vesting outputs - if !output.is_basic() - || !is_vested_reward(header.output_id(), output.as_basic()) - { - return None; - } - - if let Some(unlock_conditions) = output.unlock_conditions() { - // check if vesting unlock period is already done - if unlock_conditions.is_time_locked(snapshot_timestamp_s) { - return None; - } - unlock_conditions.address().map(|uc| *uc.address()) - } else { - None - } - } - - if let Some(address) = - mergeable_address(&header, &output, self.snapshot_timestamp_s) - { - // collect the unlocked vesting balances - self.unlocked_address_balances - .entry(address) - .and_modify(|x| x.balance += output.amount()) - .or_insert(OutputHeaderWithBalance { - output_header: header, - balance: output.amount(), - }); - continue; - } else { - return Some(Ok((header, output))); - } - } else { - return Some(res); - } - } - - // Now that we are out - self.unlocked_address_balances.pop_first().map( - |(address, output_header_with_balance)| { - // create a new basic output which holds the aggregated balance from - // unlocked vesting outputs for this address - let basic = BasicOutputBuilder::new_with_amount( - output_header_with_balance.balance, - ) - .add_unlock_condition(AddressUnlockCondition::new(address)) - .finish() - .expect("should be able to create a basic output"); - - Ok((output_header_with_balance.output_header, basic.into())) - }, - ) - } - } - - let merged_outputs = MergingIterator::new( + migration.run_for_iota( snapshot_parser.target_milestone_timestamp(), snapshot_parser.outputs(), - ) - .map(|res| { - let (header, mut output) = res?; - scale_output_amount_for_iota(&mut output)?; - - Ok::<_, anyhow::Error>((header, output)) - }); - itertools::process_results(merged_outputs, |outputs| { - migration.run(outputs, object_snapshot_writer) - })??; + object_snapshot_writer, + )?; } } Ok(()) } - -struct OutputHeaderWithBalance { - output_header: OutputHeader, - balance: u64, -} - -fn scale_output_amount_for_iota(output: &mut Output) -> Result<()> { - *output = match output { - Output::Basic(ref basic_output) => { - // Update amount - let mut builder = BasicOutputBuilder::from(basic_output) - .with_amount(scale_amount_for_iota(basic_output.amount())?); - - // Update amount in potential storage deposit return unlock condition - if let Some(sdr_uc) = basic_output - .unlock_conditions() - .get(StorageDepositReturnUnlockCondition::KIND) - { - let sdr_uc = sdr_uc.as_storage_deposit_return(); - builder = builder.replace_unlock_condition( - StorageDepositReturnUnlockCondition::new( - sdr_uc.return_address(), - scale_amount_for_iota(sdr_uc.amount())?, - u64::MAX, - ) - .unwrap(), - ); - }; - - Output::from(builder.finish()?) - } - Output::Alias(ref alias_output) => Output::from( - AliasOutputBuilder::from(alias_output) - .with_amount(scale_amount_for_iota(alias_output.amount())?) - .finish()?, - ), - Output::Foundry(ref foundry_output) => Output::from( - FoundryOutputBuilder::from(foundry_output) - .with_amount(scale_amount_for_iota(foundry_output.amount())?) - .finish()?, - ), - Output::Nft(ref nft_output) => { - // Update amount - let mut builder = NftOutputBuilder::from(nft_output) - .with_amount(scale_amount_for_iota(nft_output.amount())?); - - // Update amount in potential storage deposit return unlock condition - if let Some(sdr_uc) = nft_output - .unlock_conditions() - .get(StorageDepositReturnUnlockCondition::KIND) - { - let sdr_uc = sdr_uc.as_storage_deposit_return(); - builder = builder.replace_unlock_condition( - StorageDepositReturnUnlockCondition::new( - sdr_uc.return_address(), - scale_amount_for_iota(sdr_uc.amount())?, - u64::MAX, - ) - .unwrap(), - ); - }; - - Output::from(builder.finish()?) - } - Output::Treasury(_) => return Ok(()), - }; - Ok(()) -} - -fn scale_amount_for_iota(amount: u64) -> Result<u64> { - const IOTA_MULTIPLIER: u64 = 1000; - - amount - .checked_mul(IOTA_MULTIPLIER) - .ok_or_else(|| anyhow!("overflow multiplying amount {amount} by {IOTA_MULTIPLIER}")) -} diff --git a/crates/iota-genesis-builder/src/stardust/migration/migration.rs b/crates/iota-genesis-builder/src/stardust/migration/migration.rs index 96ef8c7fd2a..a1e0d49dd6f 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/migration.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/migration.rs @@ -32,6 +32,7 @@ use crate::stardust::{ verification::{created_objects::CreatedObjects, verify_outputs}, }, native_token::package_data::NativeTokenPackageData, + process_outputs::get_merged_outputs_for_iota, types::{address_swap_map::AddressSwapMap, output_header::OutputHeader}, }; @@ -163,6 +164,20 @@ impl Migration { Ok(()) } + /// Run all stages of the migration coming from a Hornet snapshot with IOTA + /// coin type. + pub fn run_for_iota<'a>( + self, + target_milestone_timestamp: u32, + outputs: impl Iterator<Item = Result<(OutputHeader, Output)>> + 'a, + writer: impl Write, + ) -> Result<()> { + itertools::process_results( + get_merged_outputs_for_iota(target_milestone_timestamp, outputs), + |outputs| self.run(outputs, writer), + )? + } + /// The migration objects. /// /// The system packages and underlying `init` objects diff --git a/crates/iota-genesis-builder/src/stardust/migration/mod.rs b/crates/iota-genesis-builder/src/stardust/migration/mod.rs index d9a04628723..198da0e1561 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/mod.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 mod executor; -#[allow(clippy::module_inception)] +#[expect(clippy::module_inception)] mod migration; mod migration_target_network; #[cfg(test)] diff --git a/crates/iota-genesis-builder/src/stardust/mod.rs b/crates/iota-genesis-builder/src/stardust/mod.rs index 56a24edab30..b4e832eea96 100644 --- a/crates/iota-genesis-builder/src/stardust/mod.rs +++ b/crates/iota-genesis-builder/src/stardust/mod.rs @@ -8,6 +8,7 @@ pub mod migration; pub mod native_token; pub mod parse; +pub mod process_outputs; #[cfg(feature = "test-outputs")] pub mod test_outputs; pub mod types; diff --git a/crates/iota-genesis-builder/src/stardust/process_outputs.rs b/crates/iota-genesis-builder/src/stardust/process_outputs.rs new file mode 100644 index 00000000000..bb4c93b7ad0 --- /dev/null +++ b/crates/iota-genesis-builder/src/stardust/process_outputs.rs @@ -0,0 +1,208 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; + +use anyhow::{Result, anyhow}; +use iota_sdk::types::block::{ + address::Address, + output::{ + AliasOutputBuilder, BasicOutputBuilder, FoundryOutputBuilder, NftOutputBuilder, Output, + unlock_condition::{AddressUnlockCondition, StorageDepositReturnUnlockCondition}, + }, +}; +use iota_types::timelock::timelock::is_vested_reward; + +use super::types::output_header::OutputHeader; + +/// Take an `amount` and scale it by a multiplier defined for the IOTA token. +pub fn scale_amount_for_iota(amount: u64) -> Result<u64> { + const IOTA_MULTIPLIER: u64 = 1000; + + amount + .checked_mul(IOTA_MULTIPLIER) + .ok_or_else(|| anyhow!("overflow multiplying amount {amount} by {IOTA_MULTIPLIER}")) +} + +/// Processes and merges outputs from a Hornet snapshot considering balances as +/// IOTA tokens. +/// +/// This function uses the `MergingIterator` to filter and aggregate vesting +/// balances and then scales the output amounts. +pub fn get_merged_outputs_for_iota<'a>( + target_milestone_timestamp: u32, + outputs: impl Iterator<Item = Result<(OutputHeader, Output)>> + 'a, +) -> impl Iterator<Item = Result<(OutputHeader, Output), anyhow::Error>> + 'a { + MergingIterator::new(target_milestone_timestamp, outputs).map(|res| { + let (header, mut output) = res?; + // Scale the output amount according to IOTA token multiplier + scale_output_amount_for_iota(&mut output)?; + Ok((header, output)) + }) +} + +struct OutputHeaderWithBalance { + output_header: OutputHeader, + balance: u64, +} + +/// An iterator that processes outputs, aggregates balances for eligible +/// addresses, and generates new "basic" outputs for unlocked vesting rewards. +/// +/// `MergingIterator` filters outputs based on conditions: +/// - Must be "basic" outputs. +/// - Must represent vesting rewards that are timelocked relative to a snapshot +/// timestamp. +/// +/// Eligible balances are aggregated into a map, and once all inputs are +/// processed, the iterator produces new outputs consolidating these balances. +/// +/// Non-eligible outputs are returned as-is. +struct MergingIterator<I> { + /// Stores aggregated balances for eligible addresses. + unlocked_address_balances: BTreeMap<Address, OutputHeaderWithBalance>, + /// Timestamp used to evaluate timelock conditions. + snapshot_timestamp_s: u32, + /// Iterator over `(OutputHeader, Output)` pairs. + outputs: I, +} + +impl<I> MergingIterator<I> { + fn new(snapshot_timestamp_s: u32, outputs: I) -> Self { + Self { + unlocked_address_balances: Default::default(), + snapshot_timestamp_s, + outputs, + } + } +} + +impl<I: Iterator<Item = Result<(OutputHeader, Output)>>> Iterator for MergingIterator<I> { + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + // First process all the outputs, building the unlocked_address_balances map as + // we go. + for res in self.outputs.by_ref() { + if let Ok((header, output)) = res { + fn mergeable_address( + header: &OutputHeader, + output: &Output, + snapshot_timestamp_s: u32, + ) -> Option<Address> { + // ignore all non-basic outputs and non vesting outputs + if !output.is_basic() + || !is_vested_reward(header.output_id(), output.as_basic()) + { + return None; + } + + if let Some(unlock_conditions) = output.unlock_conditions() { + // check if vesting unlock period is already done + if unlock_conditions.is_time_locked(snapshot_timestamp_s) { + return None; + } + unlock_conditions.address().map(|uc| *uc.address()) + } else { + None + } + } + + if let Some(address) = + mergeable_address(&header, &output, self.snapshot_timestamp_s) + { + // collect the unlocked vesting balances + self.unlocked_address_balances + .entry(address) + .and_modify(|x| x.balance += output.amount()) + .or_insert(OutputHeaderWithBalance { + output_header: header, + balance: output.amount(), + }); + continue; + } else { + return Some(Ok((header, output))); + } + } else { + return Some(res); + } + } + + // Now that we are out + self.unlocked_address_balances + .pop_first() + .map(|(address, output_header_with_balance)| { + // create a new basic output which holds the aggregated balance from + // unlocked vesting outputs for this address + let basic = BasicOutputBuilder::new_with_amount(output_header_with_balance.balance) + .add_unlock_condition(AddressUnlockCondition::new(address)) + .finish() + .expect("should be able to create a basic output"); + + Ok((output_header_with_balance.output_header, basic.into())) + }) + } +} + +fn scale_output_amount_for_iota(output: &mut Output) -> Result<()> { + *output = match output { + Output::Basic(ref basic_output) => { + // Update amount + let mut builder = BasicOutputBuilder::from(basic_output) + .with_amount(scale_amount_for_iota(basic_output.amount())?); + + // Update amount in potential storage deposit return unlock condition + if let Some(sdr_uc) = basic_output + .unlock_conditions() + .get(StorageDepositReturnUnlockCondition::KIND) + { + let sdr_uc = sdr_uc.as_storage_deposit_return(); + builder = builder.replace_unlock_condition( + StorageDepositReturnUnlockCondition::new( + sdr_uc.return_address(), + scale_amount_for_iota(sdr_uc.amount())?, + u64::MAX, + ) + .unwrap(), + ); + }; + + Output::from(builder.finish()?) + } + Output::Alias(ref alias_output) => Output::from( + AliasOutputBuilder::from(alias_output) + .with_amount(scale_amount_for_iota(alias_output.amount())?) + .finish()?, + ), + Output::Foundry(ref foundry_output) => Output::from( + FoundryOutputBuilder::from(foundry_output) + .with_amount(scale_amount_for_iota(foundry_output.amount())?) + .finish()?, + ), + Output::Nft(ref nft_output) => { + // Update amount + let mut builder = NftOutputBuilder::from(nft_output) + .with_amount(scale_amount_for_iota(nft_output.amount())?); + + // Update amount in potential storage deposit return unlock condition + if let Some(sdr_uc) = nft_output + .unlock_conditions() + .get(StorageDepositReturnUnlockCondition::KIND) + { + let sdr_uc = sdr_uc.as_storage_deposit_return(); + builder = builder.replace_unlock_condition( + StorageDepositReturnUnlockCondition::new( + sdr_uc.return_address(), + scale_amount_for_iota(sdr_uc.amount())?, + u64::MAX, + ) + .unwrap(), + ); + }; + + Output::from(builder.finish()?) + } + Output::Treasury(_) => return Ok(()), + }; + Ok(()) +} diff --git a/crates/iota-graphql-config/src/lib.rs b/crates/iota-graphql-config/src/lib.rs index dac6e6ac1b2..236ef925b8f 100644 --- a/crates/iota-graphql-config/src/lib.rs +++ b/crates/iota-graphql-config/src/lib.rs @@ -17,7 +17,7 @@ use syn::{ /// field that ensures that if the field is not present during deserialization, /// it is replaced with its default value, from the `Default` implementation for /// the config struct. -#[allow(non_snake_case)] +#[expect(non_snake_case)] #[proc_macro_attribute] pub fn GraphQLConfig(_attr: TokenStream, input: TokenStream) -> TokenStream { let DeriveInput { diff --git a/crates/iota-graphql-rpc-client/src/response.rs b/crates/iota-graphql-rpc-client/src/response.rs index 40c24e93cf8..d26cab75d80 100644 --- a/crates/iota-graphql-rpc-client/src/response.rs +++ b/crates/iota-graphql-rpc-client/src/response.rs @@ -40,7 +40,7 @@ impl GraphqlResponse { }) } - #[allow(clippy::result_large_err)] + #[expect(clippy::result_large_err)] pub fn graphql_version(&self) -> Result<String, ClientError> { Ok(self .headers @@ -91,7 +91,7 @@ impl GraphqlResponse { self.full_response.errors.clone() } - #[allow(clippy::result_large_err)] + #[expect(clippy::result_large_err)] pub fn usage(&self) -> Result<Option<BTreeMap<String, u64>>, ClientError> { Ok(match self.full_response.extensions.get("usage").cloned() { Some(Value::Object(obj)) => Some( diff --git a/crates/iota-graphql-rpc-client/src/simple_client.rs b/crates/iota-graphql-rpc-client/src/simple_client.rs index f78f8ca3753..2b5dfac4649 100644 --- a/crates/iota-graphql-rpc-client/src/simple_client.rs +++ b/crates/iota-graphql-rpc-client/src/simple_client.rs @@ -117,7 +117,7 @@ impl SimpleClient { } } -#[allow(clippy::type_complexity, clippy::result_large_err)] +#[expect(clippy::type_complexity, clippy::result_large_err)] pub fn resolve_variables( vars: &[GraphqlQueryVariable], ) -> Result<(BTreeMap<String, String>, BTreeMap<String, Value>), ClientError> { diff --git a/crates/iota-graphql-rpc/src/raw_query.rs b/crates/iota-graphql-rpc/src/raw_query.rs index e6ed4a89b14..ce7bd23b2f6 100644 --- a/crates/iota-graphql-rpc/src/raw_query.rs +++ b/crates/iota-graphql-rpc/src/raw_query.rs @@ -66,7 +66,6 @@ impl RawQuery { /// Adds a `WHERE` condition to the query, combining it with existing /// conditions using `OR`. - #[allow(dead_code)] pub(crate) fn or_filter<T: std::fmt::Display>(mut self, condition: T) -> Self { self.where_ = match self.where_ { Some(where_) => Some(format!("({}) OR {}", where_, condition)), diff --git a/crates/iota-graphql-rpc/src/server/version.rs b/crates/iota-graphql-rpc/src/server/version.rs index ff65114fb7b..b1c58ddcb1d 100644 --- a/crates/iota-graphql-rpc/src/server/version.rs +++ b/crates/iota-graphql-rpc/src/server/version.rs @@ -17,7 +17,7 @@ use crate::{ pub(crate) static VERSION_HEADER: HeaderName = HeaderName::from_static("x-iota-rpc-version"); -#[allow(unused)] +#[expect(unused)] pub(crate) struct IotaRpcVersion(Vec<u8>, Vec<Vec<u8>>); const NAMED_VERSIONS: [&str; 3] = ["beta", "legacy", "stable"]; diff --git a/crates/iota-graphql-rpc/src/types/epoch.rs b/crates/iota-graphql-rpc/src/types/epoch.rs index 20a16d582c8..bbb9607c294 100644 --- a/crates/iota-graphql-rpc/src/types/epoch.rs +++ b/crates/iota-graphql-rpc/src/types/epoch.rs @@ -264,7 +264,6 @@ impl Epoch { ) -> Result<ScanConnection<String, TransactionBlock>> { let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; - #[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#9422 let Some(filter) = filter .unwrap_or_default() .intersect(TransactionBlockFilter { diff --git a/crates/iota-graphql-rpc/src/types/move_object.rs b/crates/iota-graphql-rpc/src/types/move_object.rs index 8f74494b841..405d9804be7 100644 --- a/crates/iota-graphql-rpc/src/types/move_object.rs +++ b/crates/iota-graphql-rpc/src/types/move_object.rs @@ -52,7 +52,7 @@ pub(crate) enum MoveObjectDowncastError { /// This interface is implemented by types that represent a Move object on-chain /// (A Move value whose type has `key`). -#[allow(clippy::duplicated_attributes)] +#[expect(clippy::duplicated_attributes)] #[derive(Interface)] #[graphql( name = "IMoveObject", diff --git a/crates/iota-graphql-rpc/src/types/object.rs b/crates/iota-graphql-rpc/src/types/object.rs index 4a57b49c764..ae2d53767f3 100644 --- a/crates/iota-graphql-rpc/src/types/object.rs +++ b/crates/iota-graphql-rpc/src/types/object.rs @@ -88,7 +88,7 @@ pub(crate) struct Object { pub(crate) struct ObjectImpl<'o>(pub &'o Object); #[derive(Clone, Debug)] -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] pub(crate) enum ObjectKind { /// An object loaded from serialized data, such as the contents of a /// transaction that hasn't been indexed yet. @@ -243,7 +243,7 @@ pub(crate) struct HistoricalObjectCursor { /// Interface implemented by on-chain values that are addressable by an ID (also /// referred to as its address). This includes Move objects and packages. -#[allow(clippy::duplicated_attributes)] +#[expect(clippy::duplicated_attributes)] #[derive(Interface)] #[graphql( name = "IObject", diff --git a/crates/iota-graphql-rpc/src/types/owner.rs b/crates/iota-graphql-rpc/src/types/owner.rs index 8f06599e691..d40b51e39bc 100644 --- a/crates/iota-graphql-rpc/src/types/owner.rs +++ b/crates/iota-graphql-rpc/src/types/owner.rs @@ -56,7 +56,7 @@ pub(crate) struct OwnerImpl { /// either the public key of an account or another object. The same address can /// only refer to an account or an object, never both, but it is not possible to /// know which up-front. -#[allow(clippy::duplicated_attributes)] +#[expect(clippy::duplicated_attributes)] #[derive(Interface)] #[graphql( name = "IOwner", diff --git a/crates/iota-indexer/src/apis/transaction_builder_api.rs b/crates/iota-indexer/src/apis/transaction_builder_api.rs index 274a57a64dc..c72ce245d12 100644 --- a/crates/iota-indexer/src/apis/transaction_builder_api.rs +++ b/crates/iota-indexer/src/apis/transaction_builder_api.rs @@ -21,7 +21,7 @@ pub(crate) struct TransactionBuilderApi<T: R2D2Connection + 'static> { } impl<T: R2D2Connection> TransactionBuilderApi<T> { - #[allow(clippy::new_ret_no_self)] + #[expect(clippy::new_ret_no_self)] pub fn new(inner: IndexerReader<T>) -> IotaTransactionBuilderApi { IotaTransactionBuilderApi::new_with_data_reader(std::sync::Arc::new(Self { inner })) } diff --git a/crates/iota-indexer/src/models/tx_indices.rs b/crates/iota-indexer/src/models/tx_indices.rs index 7ca593b6218..3bc562d01f8 100644 --- a/crates/iota-indexer/src/models/tx_indices.rs +++ b/crates/iota-indexer/src/models/tx_indices.rs @@ -96,7 +96,7 @@ pub struct StoredTxKind { pub tx_sequence_number: i64, } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] impl TxIndex { pub fn split( self: TxIndex, diff --git a/crates/iota-indexer/src/store/indexer_store.rs b/crates/iota-indexer/src/store/indexer_store.rs index 00281bc11cf..b41b250f28e 100644 --- a/crates/iota-indexer/src/store/indexer_store.rs +++ b/crates/iota-indexer/src/store/indexer_store.rs @@ -18,7 +18,7 @@ use crate::{ }, }; -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] pub enum ObjectChangeToCommit { MutatedObject(StoredObject), DeletedObject(StoredDeletedObject), diff --git a/crates/iota-indexer/src/store/pg_indexer_store.rs b/crates/iota-indexer/src/store/pg_indexer_store.rs index d721a5e95e4..007fa538120 100644 --- a/crates/iota-indexer/src/store/pg_indexer_store.rs +++ b/crates/iota-indexer/src/store/pg_indexer_store.rs @@ -135,7 +135,7 @@ SET object_version = EXCLUDED.object_version, pub struct PgIndexerStoreConfig { pub parallel_chunk_size: usize, pub parallel_objects_chunk_size: usize, - #[allow(unused)] + #[expect(unused)] pub epochs_to_keep: Option<u64>, } diff --git a/crates/iota-indexer/tests/ingestion_tests.rs b/crates/iota-indexer/tests/ingestion_tests.rs index 4ed48a2c0f2..9082703db4c 100644 --- a/crates/iota-indexer/tests/ingestion_tests.rs +++ b/crates/iota-indexer/tests/ingestion_tests.rs @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -#[allow(dead_code)] +#[expect(dead_code)] #[cfg(feature = "pg_integration")] mod common; #[cfg(feature = "pg_integration")] diff --git a/crates/iota-indexer/tests/rpc-tests/main.rs b/crates/iota-indexer/tests/rpc-tests/main.rs index 0479b12703e..2b6e61a280d 100644 --- a/crates/iota-indexer/tests/rpc-tests/main.rs +++ b/crates/iota-indexer/tests/rpc-tests/main.rs @@ -4,7 +4,7 @@ #[cfg(feature = "shared_test_runtime")] mod coin_api; -#[allow(dead_code)] +#[expect(dead_code)] #[path = "../common/mod.rs"] mod common; #[cfg(feature = "shared_test_runtime")] diff --git a/crates/iota-json-rpc-tests/tests/read_api.rs b/crates/iota-json-rpc-tests/tests/read_api.rs index 8602e391c05..a38376ce97b 100644 --- a/crates/iota-json-rpc-tests/tests/read_api.rs +++ b/crates/iota-json-rpc-tests/tests/read_api.rs @@ -1487,104 +1487,108 @@ async fn try_get_past_object_version_not_found() { assert!(at_least_one_version_not_found) } -#[sim_test] -async fn try_get_past_object_deleted() { - let cluster = TestClusterBuilder::new().build().await; - let http_client = cluster.rpc_client(); - let address = cluster.get_address_0(); - - let objects = cluster - .get_owned_objects(address, Some(IotaObjectDataOptions::full_content())) - .await - .unwrap(); +mod move_tests { + use super::*; - assert_eq!(5, objects.len()); + #[sim_test] + async fn try_get_past_object_deleted() { + let cluster = TestClusterBuilder::new().build().await; + let http_client = cluster.rpc_client(); + let address = cluster.get_address_0(); - let tx_block_response = publish_move_package(&cluster).await; + let objects = cluster + .get_owned_objects(address, Some(IotaObjectDataOptions::full_content())) + .await + .unwrap(); - let package_id = tx_block_response - .object_changes - .unwrap() - .iter() - .filter_map(|obj_change| match obj_change { - ObjectChange::Published { package_id, .. } => Some(*package_id), - _ => None, - }) - .collect::<Vec<ObjectID>>()[0]; + assert_eq!(5, objects.len()); - let tx_block_response = cluster - .sign_and_execute_transaction( - &cluster - .test_transaction_builder() - .await - .move_call(package_id, "object_basics", "create", vec![ - 1u64.into(), - CallArg::Pure(address.to_vec()), - ]) - .build(), - ) - .await; + let tx_block_response = publish_move_package(&cluster).await; - let created_object_id = tx_block_response - .object_changes - .unwrap() - .iter() - .filter_map(|obj_change| match obj_change { - ObjectChange::Created { object_id, .. } => Some(*object_id), - _ => None, - }) - .collect::<Vec<ObjectID>>()[0]; + let package_id = tx_block_response + .object_changes + .unwrap() + .iter() + .filter_map(|obj_change| match obj_change { + ObjectChange::Published { package_id, .. } => Some(*package_id), + _ => None, + }) + .collect::<Vec<ObjectID>>()[0]; + + let tx_block_response = cluster + .sign_and_execute_transaction( + &cluster + .test_transaction_builder() + .await + .move_call(package_id, "object_basics", "create", vec![ + 1u64.into(), + CallArg::Pure(address.to_vec()), + ]) + .build(), + ) + .await; - let objects = cluster - .get_owned_objects(address, Some(IotaObjectDataOptions::full_content())) - .await - .unwrap(); + let created_object_id = tx_block_response + .object_changes + .unwrap() + .iter() + .filter_map(|obj_change| match obj_change { + ObjectChange::Created { object_id, .. } => Some(*object_id), + _ => None, + }) + .collect::<Vec<ObjectID>>()[0]; - let object_ids = objects - .iter() - .map(|a| a.object_id().unwrap()) - .collect::<Vec<ObjectID>>(); + let objects = cluster + .get_owned_objects(address, Some(IotaObjectDataOptions::full_content())) + .await + .unwrap(); - assert_eq!(7, objects.len()); - assert!(object_ids.contains(&created_object_id)); + let object_ids = objects + .iter() + .map(|a| a.object_id().unwrap()) + .collect::<Vec<ObjectID>>(); - let created_object = http_client - .get_object(created_object_id, None) - .await - .unwrap() - .data - .unwrap(); + assert_eq!(7, objects.len()); + assert!(object_ids.contains(&created_object_id)); - let arg = CallArg::Object(iota_types::transaction::ObjectArg::ImmOrOwnedObject(( - created_object.object_id, - created_object.version, - created_object.digest, - ))); + let created_object = http_client + .get_object(created_object_id, None) + .await + .unwrap() + .data + .unwrap(); - let tx_block_response = cluster - .sign_and_execute_transaction( - &cluster - .test_transaction_builder() - .await - .move_call(package_id, "object_basics", "delete", vec![arg]) - .build(), - ) - .await; + let arg = CallArg::Object(iota_types::transaction::ObjectArg::ImmOrOwnedObject(( + created_object.object_id, + created_object.version, + created_object.digest, + ))); + + let tx_block_response = cluster + .sign_and_execute_transaction( + &cluster + .test_transaction_builder() + .await + .move_call(package_id, "object_basics", "delete", vec![arg]) + .build(), + ) + .await; - assert_eq!( - tx_block_response.effects.as_ref().unwrap().deleted().len(), - 1 - ); + assert_eq!( + tx_block_response.effects.as_ref().unwrap().deleted().len(), + 1 + ); - let seq_num = SequenceNumber::from_u64(4); - let rpc_past_obj = http_client - .try_get_past_object(created_object_id, seq_num, None) - .await - .unwrap(); + let seq_num = SequenceNumber::from_u64(4); + let rpc_past_obj = http_client + .try_get_past_object(created_object_id, seq_num, None) + .await + .unwrap(); - assert!( - matches!(rpc_past_obj, IotaPastObjectResponse::ObjectDeleted(obj) if obj.object_id == created_object_id && obj.version == seq_num) - ); + assert!( + matches!(rpc_past_obj, IotaPastObjectResponse::ObjectDeleted(obj) if obj.object_id == created_object_id && obj.version == seq_num) + ); + } } #[sim_test] diff --git a/crates/iota-json-rpc-tests/tests/transaction_builder_api.rs b/crates/iota-json-rpc-tests/tests/transaction_builder_api.rs index cf7dc69998d..558a8cfa92d 100644 --- a/crates/iota-json-rpc-tests/tests/transaction_builder_api.rs +++ b/crates/iota-json-rpc-tests/tests/transaction_builder_api.rs @@ -298,49 +298,53 @@ async fn test_pay_all_iota() -> Result<(), anyhow::Error> { Ok(()) } -#[sim_test] -async fn test_publish() -> Result<(), anyhow::Error> { - let cluster = TestClusterBuilder::new().build().await; - let http_client = cluster.rpc_client(); - let address = cluster.get_address_0(); - - let objects = http_client - .get_owned_objects( - address, - Some(IotaObjectResponseQuery::new_with_options( - IotaObjectDataOptions::new() - .with_type() - .with_owner() - .with_previous_transaction(), - )), - None, - None, - ) - .await?; - let gas = objects.data.first().unwrap().object().unwrap(); - - let compiled_package = - BuildConfig::new_for_testing().build(Path::new("../../examples/move/basics"))?; - let compiled_modules_bytes = - compiled_package.get_package_base64(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_storage_package_ids(); - - let transaction_bytes: TransactionBlockBytes = http_client - .publish( - address, - compiled_modules_bytes, - dependencies, - Some(gas.object_id), - 100_000_000.into(), - ) - .await?; +mod move_tests { + use super::*; + + #[sim_test] + async fn test_publish() -> Result<(), anyhow::Error> { + let cluster = TestClusterBuilder::new().build().await; + let http_client = cluster.rpc_client(); + let address = cluster.get_address_0(); + + let objects = http_client + .get_owned_objects( + address, + Some(IotaObjectResponseQuery::new_with_options( + IotaObjectDataOptions::new() + .with_type() + .with_owner() + .with_previous_transaction(), + )), + None, + None, + ) + .await?; + let gas = objects.data.first().unwrap().object().unwrap(); + + let compiled_package = + BuildConfig::new_for_testing().build(Path::new("../../examples/move/basics"))?; + let compiled_modules_bytes = + compiled_package.get_package_base64(/* with_unpublished_deps */ false); + let dependencies = compiled_package.get_dependency_storage_package_ids(); + + let transaction_bytes: TransactionBlockBytes = http_client + .publish( + address, + compiled_modules_bytes, + dependencies, + Some(gas.object_id), + 100_000_000.into(), + ) + .await?; - let tx_response = execute_tx(&cluster, http_client, transaction_bytes) - .await - .unwrap(); + let tx_response = execute_tx(&cluster, http_client, transaction_bytes) + .await + .unwrap(); - matches!(tx_response, IotaTransactionBlockResponse {effects, ..} if effects.as_ref().unwrap().created().len() == 6); - Ok(()) + matches!(tx_response, IotaTransactionBlockResponse {effects, ..} if effects.as_ref().unwrap().created().len() == 6); + Ok(()) + } } #[sim_test] diff --git a/crates/iota-json-rpc-types/src/iota_object.rs b/crates/iota-json-rpc-types/src/iota_object.rs index 45d1eaff03d..8a7947d2b67 100644 --- a/crates/iota-json-rpc-types/src/iota_object.rs +++ b/crates/iota-json-rpc-types/src/iota_object.rs @@ -209,7 +209,6 @@ pub struct IotaObjectData { /// The Display metadata for frontend UI rendering, default to be None /// unless IotaObjectDataOptions.showContent is set to true This can also /// be None if the struct type does not have Display defined - /// See more details in <https://forums.iota.io/t/nft-object-display-proposal/4872> #[serde(skip_serializing_if = "Option::is_none")] pub display: Option<DisplayFieldsResponse>, /// Move object content or package content, default to be None unless diff --git a/crates/iota-json-rpc/src/authority_state.rs b/crates/iota-json-rpc/src/authority_state.rs index ef03f6576dd..e2b7df71d96 100644 --- a/crates/iota-json-rpc/src/authority_state.rs +++ b/crates/iota-json-rpc/src/authority_state.rs @@ -354,7 +354,6 @@ impl StateRead for AuthorityState { .await?) } - #[allow(clippy::type_complexity)] async fn dry_exec_transaction( &self, transaction: TransactionData, diff --git a/crates/iota-json-rpc/src/axum_router.rs b/crates/iota-json-rpc/src/axum_router.rs index 5957efdcb0d..8037cda0c3f 100644 --- a/crates/iota-json-rpc/src/axum_router.rs +++ b/crates/iota-json-rpc/src/axum_router.rs @@ -446,7 +446,6 @@ pub mod ws { } async fn ws_json_rpc_handler<L: Logger>(mut socket: WebSocket, service: JsonRpcService<L>) { - #[allow(clippy::disallowed_methods)] let (tx, mut rx) = mpsc::channel::<String>(MAX_WS_MESSAGE_BUFFER); let sink = MethodSink::new_with_limit(tx, MAX_RESPONSE_SIZE); let bounded_subscriptions = BoundedSubscriptions::new(100); diff --git a/crates/iota-json-rpc/src/transaction_execution_api.rs b/crates/iota-json-rpc/src/transaction_execution_api.rs index 0d128dd1914..32c865b5281 100644 --- a/crates/iota-json-rpc/src/transaction_execution_api.rs +++ b/crates/iota-json-rpc/src/transaction_execution_api.rs @@ -71,7 +71,7 @@ impl TransactionExecutionApi { Ok(data) } - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] fn prepare_execute_transaction_block( &self, tx_bytes: Base64, diff --git a/crates/iota-json/src/tests.rs b/crates/iota-json/src/tests.rs index 6a899b12ebd..b97880f0efa 100644 --- a/crates/iota-json/src/tests.rs +++ b/crates/iota-json/src/tests.rs @@ -423,91 +423,95 @@ fn test_basic_args_linter_pure_args_good() { } } -#[test] -fn test_basic_args_linter_top_level() { - let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples/move/basics"); - let compiled_modules = BuildConfig::new_for_testing() - .build(&path) - .unwrap() - .into_modules(); - let example_package = Object::new_package_for_testing( - &compiled_modules, - TransactionDigest::genesis_marker(), - BuiltInFramework::genesis_move_packages(), - ) - .unwrap(); - let package = example_package.data.try_as_package().unwrap(); - - let module = Identifier::new("resolve_args").unwrap(); - let function = Identifier::new("foo").unwrap(); - - // Function signature: - // foo( - // _foo: &mut Foo, - // _bar: vector<Foo>, - // _name: vector<u8>, - // _index: u64, - // _flag: u8, - // _recipient: address, - // _ctx: &mut TxContext, - // ) - - let foo_id = ObjectID::random(); - let bar_id = ObjectID::random(); - let baz_id = ObjectID::random(); - let recipient_addr = IotaAddress::random_for_testing_only(); - - let foo = json!(foo_id.to_canonical_string(/* with_prefix */ true)); - let bar = json!([ - bar_id.to_canonical_string(/* with_prefix */ true), - baz_id.to_canonical_string(/* with_prefix */ true), - ]); - - let name = json!("Name"); - let index = json!("12345678"); - let flag = json!(89); - let recipient = json!(recipient_addr.to_string()); - - let args: Vec<_> = [ - foo.clone(), - bar.clone(), - name.clone(), - index.clone(), - flag, - recipient.clone(), - ] - .into_iter() - .map(|q| IotaJsonValue::new(q.clone()).unwrap()) - .collect(); - - let json_args: Vec<_> = - resolve_move_function_args(package, module.clone(), function.clone(), &[], args) +mod move_tests { + use super::*; + + #[test] + fn test_basic_args_linter_top_level() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples/move/basics"); + let compiled_modules = BuildConfig::new_for_testing() + .build(&path) .unwrap() - .into_iter() - .map(|(arg, _)| arg) - .collect(); - - use ResolvedCallArg as RCA; - fn pure<T: Serialize>(t: &T) -> RCA { - RCA::Pure(bcs::to_bytes(t).unwrap()) - } - - assert_eq!(json_args, vec![ - RCA::Object(foo_id), - RCA::ObjVec(vec![bar_id, baz_id]), - pure(&"Name"), - pure(&12345678u64), - pure(&89u8), - pure(&recipient_addr), - ],); - - // Flag is u8 so too large - let args: Vec<_> = [foo, bar, name, index, json!(10000u64), recipient] + .into_modules(); + let example_package = Object::new_package_for_testing( + &compiled_modules, + TransactionDigest::genesis_marker(), + BuiltInFramework::genesis_move_packages(), + ) + .unwrap(); + let package = example_package.data.try_as_package().unwrap(); + + let module = Identifier::new("resolve_args").unwrap(); + let function = Identifier::new("foo").unwrap(); + + // Function signature: + // foo( + // _foo: &mut Foo, + // _bar: vector<Foo>, + // _name: vector<u8>, + // _index: u64, + // _flag: u8, + // _recipient: address, + // _ctx: &mut TxContext, + // ) + + let foo_id = ObjectID::random(); + let bar_id = ObjectID::random(); + let baz_id = ObjectID::random(); + let recipient_addr = IotaAddress::random_for_testing_only(); + + let foo = json!(foo_id.to_canonical_string(/* with_prefix */ true)); + let bar = json!([ + bar_id.to_canonical_string(/* with_prefix */ true), + baz_id.to_canonical_string(/* with_prefix */ true), + ]); + + let name = json!("Name"); + let index = json!("12345678"); + let flag = json!(89); + let recipient = json!(recipient_addr.to_string()); + + let args: Vec<_> = [ + foo.clone(), + bar.clone(), + name.clone(), + index.clone(), + flag, + recipient.clone(), + ] .into_iter() .map(|q| IotaJsonValue::new(q.clone()).unwrap()) .collect(); - assert!(resolve_move_function_args(package, module, function, &[], args,).is_err()); + let json_args: Vec<_> = + resolve_move_function_args(package, module.clone(), function.clone(), &[], args) + .unwrap() + .into_iter() + .map(|(arg, _)| arg) + .collect(); + + use ResolvedCallArg as RCA; + fn pure<T: Serialize>(t: &T) -> RCA { + RCA::Pure(bcs::to_bytes(t).unwrap()) + } + + assert_eq!(json_args, vec![ + RCA::Object(foo_id), + RCA::ObjVec(vec![bar_id, baz_id]), + pure(&"Name"), + pure(&12345678u64), + pure(&89u8), + pure(&recipient_addr), + ],); + + // Flag is u8 so too large + let args: Vec<_> = [foo, bar, name, index, json!(10000u64), recipient] + .into_iter() + .map(|q| IotaJsonValue::new(q.clone()).unwrap()) + .collect(); + + assert!(resolve_move_function_args(package, module, function, &[], args,).is_err()); + } } #[test] diff --git a/crates/iota-light-client/src/bin/light_client.rs b/crates/iota-light-client/src/bin/light_client.rs index 64f6f97df0b..1db8daab15f 100644 --- a/crates/iota-light-client/src/bin/light_client.rs +++ b/crates/iota-light-client/src/bin/light_client.rs @@ -142,7 +142,7 @@ mod tests { } // clippy ignore dead-code - #[allow(dead_code)] + #[expect(dead_code)] async fn write_full_checkpoint( checkpoint_path: &Path, checkpoint: &CheckpointData, diff --git a/crates/iota-macros/src/lib.rs b/crates/iota-macros/src/lib.rs index 672b381ceb0..366067ff0a4 100644 --- a/crates/iota-macros/src/lib.rs +++ b/crates/iota-macros/src/lib.rs @@ -413,7 +413,6 @@ mod test { #[test] #[should_panic] fn test_macro_overflow() { - #[allow(arithmetic_overflow)] fn f() { println!("{}", i32::MAX + 1); } @@ -603,7 +602,6 @@ mod test { #[test] #[should_panic] fn test_macro_overflow() { - #[allow(arithmetic_overflow)] fn f() { println!("{}", i32::MAX + 1); } diff --git a/crates/iota-metrics/src/metrics_network.rs b/crates/iota-metrics/src/metrics_network.rs index f5cb37402a4..e8131f2434e 100644 --- a/crates/iota-metrics/src/metrics_network.rs +++ b/crates/iota-metrics/src/metrics_network.rs @@ -364,7 +364,7 @@ impl MakeCallbackHandler for MetricsMakeCallbackHandler { pub struct MetricsResponseHandler { metrics: Arc<NetworkMetrics>, // The timer is held on to and "observed" once dropped - #[allow(unused)] + #[expect(unused)] timer: HistogramTimer, route: String, excessive_message_size: usize, diff --git a/crates/iota-metrics/src/monitored_mpsc.rs b/crates/iota-metrics/src/monitored_mpsc.rs index ce15ff7fed3..0806d06590b 100644 --- a/crates/iota-metrics/src/monitored_mpsc.rs +++ b/crates/iota-metrics/src/monitored_mpsc.rs @@ -563,7 +563,7 @@ impl<T> Unpin for UnboundedReceiver<T> {} /// and `UnboundedReceiver` pub fn unbounded_channel<T>(name: &str) -> (UnboundedSender<T>, UnboundedReceiver<T>) { let metrics = get_metrics(); - #[allow(clippy::disallowed_methods)] + #[expect(clippy::disallowed_methods)] let (sender, receiver) = mpsc::unbounded_channel(); ( UnboundedSender { diff --git a/crates/iota-move-build/src/lib.rs b/crates/iota-move-build/src/lib.rs index 260c2ce2ef8..309885fcbf1 100644 --- a/crates/iota-move-build/src/lib.rs +++ b/crates/iota-move-build/src/lib.rs @@ -204,7 +204,9 @@ pub fn decorate_warnings(warning_diags: Diagnostics, files: Option<&MappedFiles> report_warnings(f, warning_diags); } if any_linter_warnings { - eprintln!("Please report feedback on the linter warnings at https://forums.iota.io\n"); + eprintln!( + "Please report feedback on the linter warnings at https://github.com/iotaledger/iota/issues\n" + ); } if filtered_diags_num > 0 { eprintln!( diff --git a/crates/iota-network/src/discovery/builder.rs b/crates/iota-network/src/discovery/builder.rs index 0c7a22ecf86..801d322e1fd 100644 --- a/crates/iota-network/src/discovery/builder.rs +++ b/crates/iota-network/src/discovery/builder.rs @@ -29,7 +29,6 @@ pub struct Builder { } impl Builder { - #[allow(clippy::new_without_default)] pub fn new(trusted_peer_change_rx: watch::Receiver<TrustedPeerChangeEvent>) -> Self { Self { config: None, diff --git a/crates/iota-network/src/randomness/mod.rs b/crates/iota-network/src/randomness/mod.rs index 50ed7c09ee1..3bed5180149 100644 --- a/crates/iota-network/src/randomness/mod.rs +++ b/crates/iota-network/src/randomness/mod.rs @@ -920,7 +920,7 @@ impl RandomnessEventLoop { } } - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] fn remove_partial_sigs_in_range( &mut self, range: ( @@ -952,7 +952,7 @@ impl RandomnessEventLoop { full_sig: Arc<OnceCell<RandomnessSignature>>, ) { // For simtests, we may test not sending partial signatures. - #[allow(unused_mut)] + #[expect(unused_mut)] let mut fail_point_skip_sending = false; fail_point_if!("rb-send-partial-signatures", || { fail_point_skip_sending = true; diff --git a/crates/iota-network/src/state_sync/builder.rs b/crates/iota-network/src/state_sync/builder.rs index 6734aceae26..496e65e08e0 100644 --- a/crates/iota-network/src/state_sync/builder.rs +++ b/crates/iota-network/src/state_sync/builder.rs @@ -32,7 +32,7 @@ pub struct Builder<S> { } impl Builder<()> { - #[allow(clippy::new_without_default)] + #[expect(clippy::new_without_default)] pub fn new() -> Self { Self { store: None, diff --git a/crates/iota-network/src/state_sync/server.rs b/crates/iota-network/src/state_sync/server.rs index 507b34310db..a213c44eec3 100644 --- a/crates/iota-network/src/state_sync/server.rs +++ b/crates/iota-network/src/state_sync/server.rs @@ -234,7 +234,7 @@ where } })?; - struct SemaphoreExtension(#[allow(unused)] OwnedSemaphorePermit); + struct SemaphoreExtension(#[expect(unused)] OwnedSemaphorePermit); inner.call(req).await.map(move |mut response| { // Insert permit as extension so it's not dropped until the response is sent. response diff --git a/crates/iota-node/src/lib.rs b/crates/iota-node/src/lib.rs index 1e2282ea92a..58443bb59e6 100644 --- a/crates/iota-node/src/lib.rs +++ b/crates/iota-node/src/lib.rs @@ -1896,7 +1896,7 @@ impl IotaNode { .store(new_value, Ordering::Relaxed); } - #[allow(unused_variables)] + #[expect(unused_variables)] async fn fetch_jwks( authority: AuthorityName, provider: &OIDCProvider, diff --git a/crates/iota-open-rpc/spec/openrpc.json b/crates/iota-open-rpc/spec/openrpc.json index 5a54a311381..95f08307724 100644 --- a/crates/iota-open-rpc/spec/openrpc.json +++ b/crates/iota-open-rpc/spec/openrpc.json @@ -9700,7 +9700,7 @@ ] }, "display": { - "description": "The Display metadata for frontend UI rendering, default to be None unless IotaObjectDataOptions.showContent is set to true This can also be None if the struct type does not have Display defined See more details in <https://forums.iota.io/t/nft-object-display-proposal/4872>", + "description": "The Display metadata for frontend UI rendering, default to be None unless IotaObjectDataOptions.showContent is set to true This can also be None if the struct type does not have Display defined", "anyOf": [ { "$ref": "#/components/schemas/DisplayFieldsResponse" diff --git a/crates/iota-proc-macros/src/lib.rs b/crates/iota-proc-macros/src/lib.rs index 68e16646ae2..e6542663586 100644 --- a/crates/iota-proc-macros/src/lib.rs +++ b/crates/iota-proc-macros/src/lib.rs @@ -234,7 +234,7 @@ pub fn sim_test(args: TokenStream, item: TokenStream) -> TokenStream { let sig = &input.sig; let body = &input.block; quote! { - #[allow(clippy::needless_return)] + #[expect(clippy::needless_return)] #[tokio::test] #ignore #sig { diff --git a/crates/iota-protocol-config-macros/src/lib.rs b/crates/iota-protocol-config-macros/src/lib.rs index 9d2497e6710..a479c8df658 100644 --- a/crates/iota-protocol-config-macros/src/lib.rs +++ b/crates/iota-protocol-config-macros/src/lib.rs @@ -153,7 +153,7 @@ pub fn accessors_macro(input: TokenStream) -> TokenStream { _ => panic!("Only structs supported."), }; - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] let ((getters, (test_setters, value_setters)), (value_lookup, field_names_str)): ( (Vec<_>, (Vec<_>, Vec<_>)), (Vec<_>, Vec<_>), @@ -201,7 +201,7 @@ pub fn accessors_macro(input: TokenStream) -> TokenStream { } } - #[allow(non_camel_case_types)] + #[expect(non_camel_case_types)] #[derive(Clone, Serialize, Debug, PartialEq, Deserialize, schemars::JsonSchema)] pub enum ProtocolConfigValue { #(#inner_types(#inner_types),)* diff --git a/crates/iota-protocol-config/src/lib.rs b/crates/iota-protocol-config/src/lib.rs index a216a4efefb..77c62e282f3 100644 --- a/crates/iota-protocol-config/src/lib.rs +++ b/crates/iota-protocol-config/src/lib.rs @@ -1150,7 +1150,7 @@ impl ProtocolConfig { /// potentially returning a protocol config that is incorrect for some /// feature flags. Definitely safe for testing and for protocol version /// 11 and prior. - #[allow(non_snake_case)] + #[expect(non_snake_case)] pub fn get_for_max_version_UNSAFE() -> Self { if Self::load_poison_get_for_min_version() { panic!("get_for_max_version_UNSAFE called on validator"); diff --git a/crates/iota-replay/src/fuzz.rs b/crates/iota-replay/src/fuzz.rs index cc2d5a09c58..d041e740ce8 100644 --- a/crates/iota-replay/src/fuzz.rs +++ b/crates/iota-replay/src/fuzz.rs @@ -183,7 +183,7 @@ impl ReplayFuzzer { } } -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] #[derive(Debug, Error, Clone)] pub enum ReplayFuzzError { #[error( diff --git a/crates/iota-replay/src/replay.rs b/crates/iota-replay/src/replay.rs index 0c40b5c52ff..b012b4c2cfc 100644 --- a/crates/iota-replay/src/replay.rs +++ b/crates/iota-replay/src/replay.rs @@ -512,7 +512,7 @@ impl LocalExec { } // TODO: remove this after `futures::executor::block_on` is removed. - #[allow(clippy::disallowed_methods)] + #[expect(clippy::disallowed_methods)] pub fn download_object( &self, object_id: &ObjectID, @@ -557,7 +557,7 @@ impl LocalExec { } // TODO: remove this after `futures::executor::block_on` is removed. - #[allow(clippy::disallowed_methods)] + #[expect(clippy::disallowed_methods)] pub fn download_latest_object( &self, object_id: &ObjectID, @@ -593,7 +593,7 @@ impl LocalExec { } } - #[allow(clippy::disallowed_methods)] + #[expect(clippy::disallowed_methods)] pub fn download_object_by_upper_bound( &self, object_id: &ObjectID, diff --git a/crates/iota-replay/src/types.rs b/crates/iota-replay/src/types.rs index 05c90fffe71..43efba9777d 100644 --- a/crates/iota-replay/src/types.rs +++ b/crates/iota-replay/src/types.rs @@ -75,7 +75,6 @@ fn unspecified_chain() -> Chain { Chain::Unknown } -#[allow(clippy::large_enum_variant)] #[derive(Debug, Error, Clone)] pub enum ReplayEngineError { #[error("IotaError: {:#?}", err)] diff --git a/crates/iota-rest-api/src/reader.rs b/crates/iota-rest-api/src/reader.rs index b387f30f832..0446a499a3f 100644 --- a/crates/iota-rest-api/src/reader.rs +++ b/crates/iota-rest-api/src/reader.rs @@ -265,7 +265,7 @@ impl Iterator for CheckpointTransactionsIter { pub struct CursorInfo { pub checkpoint: CheckpointSequenceNumber, pub timestamp_ms: u64, - #[allow(unused)] + #[expect(unused)] pub index: u64, // None if there are no more transactions in the store diff --git a/crates/iota-rosetta/resources/rosetta_cli.json b/crates/iota-rosetta/resources/rosetta_cli.json index 142f39a68e9..9c65a1d32d3 100644 --- a/crates/iota-rosetta/resources/rosetta_cli.json +++ b/crates/iota-rosetta/resources/rosetta_cli.json @@ -34,7 +34,7 @@ "log_balance_changes": false, "log_reconciliations": false, "ignore_reconciliation_error": false, - "historical_balance_disabled": false, + "historical_balance_disabled": true, "exempt_accounts": "", "bootstrap_balances": "", "interesting_accounts": "", @@ -55,4 +55,4 @@ } }, "perf": null -} \ No newline at end of file +} diff --git a/crates/iota-rosetta/src/errors.rs b/crates/iota-rosetta/src/errors.rs index 8cda43d91b5..854be4f1967 100644 --- a/crates/iota-rosetta/src/errors.rs +++ b/crates/iota-rosetta/src/errors.rs @@ -29,7 +29,6 @@ use crate::types::{BlockHash, IotaEnv, OperationType, PublicKey}; derive(Display, EnumIter), strum(serialize_all = "kebab-case") )] -#[allow(clippy::enum_variant_names)] pub enum Error { #[error("Unsupported blockchain: {0}")] UnsupportedBlockchain(String), diff --git a/crates/iota-rosetta/src/types.rs b/crates/iota-rosetta/src/types.rs index 8f84205fa94..eadf326efdd 100644 --- a/crates/iota-rosetta/src/types.rs +++ b/crates/iota-rosetta/src/types.rs @@ -753,7 +753,6 @@ pub struct BalanceExemption { #[derive(Serialize)] #[serde(rename_all = "snake_case")] -#[allow(dead_code)] pub enum ExemptionType { GreaterOrEqual, LessOrEqual, @@ -762,7 +761,6 @@ pub enum ExemptionType { #[derive(Serialize)] #[serde(rename_all = "snake_case")] -#[allow(clippy::enum_variant_names, dead_code)] pub enum Case { UpperCase, LowerCase, @@ -799,7 +797,6 @@ pub struct RelatedTransaction { #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "lowercase")] -#[allow(dead_code)] pub enum Direction { Forward, Backward, diff --git a/crates/iota-rosetta/src/unit_tests/balance_changing_tx_tests.rs b/crates/iota-rosetta/src/unit_tests/balance_changing_tx_tests.rs index f6e3f1904f5..88c4cb16308 100644 --- a/crates/iota-rosetta/src/unit_tests/balance_changing_tx_tests.rs +++ b/crates/iota-rosetta/src/unit_tests/balance_changing_tx_tests.rs @@ -139,98 +139,102 @@ async fn test_transfer_object() { .await; } -#[tokio::test] -async fn test_publish_and_move_call() { - let network = TestClusterBuilder::new().build().await; - let client = network.wallet.get_client().await.unwrap(); - let keystore = network.wallet.config().keystore(); - let rgp = network.get_reference_gas_price().await; - - // Test publish - let addresses = network.get_addresses(); - let sender = get_random_address(&addresses, vec![]); - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.extend(["..", "..", "examples", "move", "coin"]); - let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap(); - let compiled_modules_bytes = - compiled_package.get_package_bytes(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_storage_package_ids(); +mod move_tests { + use super::*; + + #[tokio::test] + async fn test_publish_and_move_call() { + let network = TestClusterBuilder::new().build().await; + let client = network.wallet.get_client().await.unwrap(); + let keystore = network.wallet.config().keystore(); + let rgp = network.get_reference_gas_price().await; + + // Test publish + let addresses = network.get_addresses(); + let sender = get_random_address(&addresses, vec![]); + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.extend(["..", "..", "examples", "move", "coin"]); + let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap(); + let compiled_modules_bytes = + compiled_package.get_package_bytes(/* with_unpublished_deps */ false); + let dependencies = compiled_package.get_dependency_storage_package_ids(); + + let pt = { + let mut builder = ProgrammableTransactionBuilder::new(); + builder.publish_immutable(compiled_modules_bytes, dependencies); + builder.finish() + }; + let response = test_transaction( + &client, + keystore, + vec![], + sender, + pt, + vec![], + rgp * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE, + rgp, + false, + ) + .await; + let object_changes = response.object_changes.unwrap(); - let pt = { - let mut builder = ProgrammableTransactionBuilder::new(); - builder.publish_immutable(compiled_modules_bytes, dependencies); - builder.finish() - }; - let response = test_transaction( - &client, - keystore, - vec![], - sender, - pt, - vec![], - rgp * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE, - rgp, - false, - ) - .await; - let object_changes = response.object_changes.unwrap(); + // Test move call (reuse published module from above test) + let package = object_changes + .iter() + .find_map(|change| { + if let ObjectChange::Published { package_id, .. } = change { + Some(package_id) + } else { + None + } + }) + .unwrap(); - // Test move call (reuse published module from above test) - let package = object_changes - .iter() - .find_map(|change| { - if let ObjectChange::Published { package_id, .. } = change { - Some(package_id) - } else { - None + let treasury = find_module_object(&object_changes, |type_| { + if type_.name.as_str() != "TreasuryCap" { + return false; } - }) - .unwrap(); - let treasury = find_module_object(&object_changes, |type_| { - if type_.name.as_str() != "TreasuryCap" { - return false; - } + let Some(TypeTag::Struct(otw)) = type_.type_params.first() else { + return false; + }; - let Some(TypeTag::Struct(otw)) = type_.type_params.first() else { - return false; + otw.name.as_str() == "MY_COIN" + }); + + let treasury = treasury.clone().reference.to_object_ref(); + let recipient = *addresses.choose(&mut OsRng).unwrap(); + let pt = { + let mut builder = ProgrammableTransactionBuilder::new(); + builder + .move_call( + *package, + Identifier::from_str("my_coin").unwrap(), + Identifier::from_str("mint").unwrap(), + vec![], + vec![ + CallArg::Object(ObjectArg::ImmOrOwnedObject(treasury)), + CallArg::Pure(bcs::to_bytes(&10000u64).unwrap()), + CallArg::Pure(bcs::to_bytes(&recipient).unwrap()), + ], + ) + .unwrap(); + builder.finish() }; - otw.name.as_str() == "MY_COIN" - }); - - let treasury = treasury.clone().reference.to_object_ref(); - let recipient = *addresses.choose(&mut OsRng).unwrap(); - let pt = { - let mut builder = ProgrammableTransactionBuilder::new(); - builder - .move_call( - *package, - Identifier::from_str("my_coin").unwrap(), - Identifier::from_str("mint").unwrap(), - vec![], - vec![ - CallArg::Object(ObjectArg::ImmOrOwnedObject(treasury)), - CallArg::Pure(bcs::to_bytes(&10000u64).unwrap()), - CallArg::Pure(bcs::to_bytes(&recipient).unwrap()), - ], - ) - .unwrap(); - builder.finish() - }; - - test_transaction( - &client, - keystore, - vec![], - sender, - pt, - vec![], - rgp * TEST_ONLY_GAS_UNIT_FOR_GENERIC, - rgp, - false, - ) - .await; + test_transaction( + &client, + keystore, + vec![], + sender, + pt, + vec![], + rgp * TEST_ONLY_GAS_UNIT_FOR_GENERIC, + rgp, + false, + ) + .await; + } } #[tokio::test] diff --git a/crates/iota-rosetta/tests/gas_budget_tests.rs b/crates/iota-rosetta/tests/gas_budget_tests.rs index 2029cfaa9ef..6c4ee97465f 100644 --- a/crates/iota-rosetta/tests/gas_budget_tests.rs +++ b/crates/iota-rosetta/tests/gas_budget_tests.rs @@ -24,13 +24,13 @@ use test_cluster::TestClusterBuilder; use crate::rosetta_client::RosettaEndpoint; -#[allow(dead_code)] +#[expect(dead_code)] mod rosetta_client; #[derive(Deserialize, Debug)] #[serde(untagged)] enum TransactionIdentifierResponseResult { - #[allow(unused)] + #[expect(unused)] Success(TransactionIdentifierResponse), Error(RosettaSubmitGasError), } diff --git a/crates/iota-rpc-loadgen/src/payload/mod.rs b/crates/iota-rpc-loadgen/src/payload/mod.rs index 64dfa96d5df..57ec8890d06 100644 --- a/crates/iota-rpc-loadgen/src/payload/mod.rs +++ b/crates/iota-rpc-loadgen/src/payload/mod.rs @@ -173,7 +173,6 @@ impl Command { } #[derive(Clone)] -#[allow(dead_code)] pub enum CommandData { DryRun(DryRun), GetCheckpoints(GetCheckpoints), diff --git a/crates/iota-sdk/examples/utils.rs b/crates/iota-sdk/examples/utils.rs index 8c6b914143b..d9de6f91187 100644 --- a/crates/iota-sdk/examples/utils.rs +++ b/crates/iota-sdk/examples/utils.rs @@ -320,7 +320,7 @@ pub async fn sign_and_execute_transaction( // this function should not be used. It is only used to make clippy happy, // and to reduce the number of allow(dead_code) annotations to just this one -#[allow(dead_code)] +#[expect(dead_code)] async fn just_for_clippy() -> Result<(), anyhow::Error> { let (client, sender, _recipient) = setup_for_write().await?; let _digest = split_coin_digest(&client, &sender).await?; diff --git a/crates/iota-source-validation-service/tests/tests.rs b/crates/iota-source-validation-service/tests/tests.rs index 75c934b2e41..c0104ccaacc 100644 --- a/crates/iota-source-validation-service/tests/tests.rs +++ b/crates/iota-source-validation-service/tests/tests.rs @@ -38,7 +38,7 @@ use tokio::sync::oneshot; const LOCALNET_PORT: u16 = 9000; const TEST_FIXTURES_DIR: &str = "tests/fixture"; -#[allow(clippy::await_holding_lock)] +#[expect(clippy::await_holding_lock)] #[tokio::test] #[ignore] async fn test_end_to_end() -> anyhow::Result<()> { diff --git a/crates/iota-swarm/src/memory/swarm.rs b/crates/iota-swarm/src/memory/swarm.rs index 0824335e0e2..11d20ab51c2 100644 --- a/crates/iota-swarm/src/memory/swarm.rs +++ b/crates/iota-swarm/src/memory/swarm.rs @@ -69,7 +69,7 @@ pub struct SwarmBuilder<R = OsRng> { } impl SwarmBuilder { - #[allow(clippy::new_without_default)] + #[expect(clippy::new_without_default)] pub fn new() -> Self { Self { rng: OsRng, diff --git a/crates/iota-tool/src/commands.rs b/crates/iota-tool/src/commands.rs index 18416b2401f..f0c8625a389 100644 --- a/crates/iota-tool/src/commands.rs +++ b/crates/iota-tool/src/commands.rs @@ -488,7 +488,6 @@ async fn check_locked_object( } impl ToolCommand { - #[allow(clippy::format_in_format_args)] pub async fn execute(self, tracing_handle: TracingHandle) -> Result<(), anyhow::Error> { match self { ToolCommand::LockedObject { diff --git a/crates/iota-tool/src/lib.rs b/crates/iota-tool/src/lib.rs index 27fbe1d2839..da8e0de6d03 100644 --- a/crates/iota-tool/src/lib.rs +++ b/crates/iota-tool/src/lib.rs @@ -148,7 +148,7 @@ where } } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] pub struct GroupedObjectOutput { pub grouped_results: BTreeMap< Option<( @@ -221,7 +221,6 @@ impl GroupedObjectOutput { } } -#[allow(clippy::format_in_format_args)] impl std::fmt::Display for GroupedObjectOutput { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "available stake: {}", self.available_voting_power)?; diff --git a/crates/iota-transactional-test-runner/src/lib.rs b/crates/iota-transactional-test-runner/src/lib.rs index 7e0667e4889..b0527d3b9bf 100644 --- a/crates/iota-transactional-test-runner/src/lib.rs +++ b/crates/iota-transactional-test-runner/src/lib.rs @@ -56,7 +56,6 @@ pub struct ValidatorWithFullnode { pub kv_store: Arc<TransactionKeyValueStore>, } -#[allow(unused_variables)] /// TODO: better name? #[async_trait::async_trait] pub trait TransactionalAdapter: Send + Sync + ReadStore { diff --git a/crates/iota-types/src/authenticator_state.rs b/crates/iota-types/src/authenticator_state.rs index c350b756723..054af6d6d9e 100644 --- a/crates/iota-types/src/authenticator_state.rs +++ b/crates/iota-types/src/authenticator_state.rs @@ -94,11 +94,10 @@ fn jwk_ord(a: &ActiveJwk, b: &ActiveJwk) -> std::cmp::Ordering { } } -#[allow(clippy::non_canonical_partial_ord_impl)] impl std::cmp::PartialOrd for ActiveJwk { // This must match the sort order defined by jwk_lt in authenticator_state.move fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - Some(jwk_ord(self, other)) + Some(self.cmp(other)) } } diff --git a/crates/iota-types/src/crypto.rs b/crates/iota-types/src/crypto.rs index a78c50b3ca7..7ea6d2cdece 100644 --- a/crates/iota-types/src/crypto.rs +++ b/crates/iota-types/src/crypto.rs @@ -152,7 +152,7 @@ pub fn verify_proof_of_possession( /// * accounts to interact with Iota. /// * Currently we support eddsa and ecdsa on Iota. -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] #[derive(Debug, From, PartialEq, Eq)] pub enum IotaKeyPair { Ed25519(Ed25519KeyPair), diff --git a/crates/iota-types/src/effects/effects_v1.rs b/crates/iota-types/src/effects/effects_v1.rs index 3f3458d42b0..5b7c5848c22 100644 --- a/crates/iota-types/src/effects/effects_v1.rs +++ b/crates/iota-types/src/effects/effects_v1.rs @@ -453,7 +453,6 @@ impl TransactionEffectsV1 { .unwrap() as u32 }); - #[allow(clippy::let_and_return)] let result = Self { status, executed_epoch, diff --git a/crates/iota-types/src/effects/mod.rs b/crates/iota-types/src/effects/mod.rs index 0843ffe0d55..2cab0289a9d 100644 --- a/crates/iota-types/src/effects/mod.rs +++ b/crates/iota-types/src/effects/mod.rs @@ -54,7 +54,6 @@ pub const APPROX_SIZE_OF_OWNER: usize = 48; /// The response from processing a transaction or a certified transaction #[enum_dispatch(TransactionEffectsAPI)] #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -#[allow(clippy::large_enum_variant)] pub enum TransactionEffects { V1(TransactionEffectsV1), } diff --git a/crates/iota-types/src/error.rs b/crates/iota-types/src/error.rs index bb89bc3444d..1b5eab9e489 100644 --- a/crates/iota-types/src/error.rs +++ b/crates/iota-types/src/error.rs @@ -675,7 +675,7 @@ pub enum IotaError { } #[repr(u64)] -#[allow(non_camel_case_types)] +#[expect(non_camel_case_types)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] /// Sub-status codes for the `UNKNOWN_VERIFICATION_ERROR` VM Status Code which /// provides more context TODO: add more Vm Status errors. We use @@ -686,7 +686,7 @@ pub enum VMMVerifierErrorSubStatusCode { } #[repr(u64)] -#[allow(non_camel_case_types)] +#[expect(non_camel_case_types)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] /// Sub-status codes for the `MEMORY_LIMIT_EXCEEDED` VM Status Code which /// provides more context diff --git a/crates/iota-types/src/gas.rs b/crates/iota-types/src/gas.rs index 5763c16de35..b8996eae528 100644 --- a/crates/iota-types/src/gas.rs +++ b/crates/iota-types/src/gas.rs @@ -201,7 +201,7 @@ pub mod checked { self.gas_used() as i64 - self.storage_rebate as i64 } - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] pub fn new_from_txn_effects<'a>( transactions: impl Iterator<Item = &'a TransactionEffects>, ) -> GasCostSummary { diff --git a/crates/iota-types/src/gas_model/gas_v1.rs b/crates/iota-types/src/gas_model/gas_v1.rs index da872086f33..f173d7f7425 100644 --- a/crates/iota-types/src/gas_model/gas_v1.rs +++ b/crates/iota-types/src/gas_model/gas_v1.rs @@ -26,7 +26,7 @@ mod checked { /// After execution a call to `GasStatus::bucketize` will round the /// computation cost to `cost` for the bucket ([`min`, `max`]) the gas /// used falls into. - #[allow(dead_code)] + #[expect(dead_code)] pub(crate) struct ComputationBucket { min: u64, max: u64, @@ -165,7 +165,6 @@ mod checked { pub new_size: u64, } - #[allow(dead_code)] #[derive(Debug)] pub struct IotaGasStatus { // GasStatus as used by the VM, that is all the VM sees diff --git a/crates/iota-types/src/gas_model/tables.rs b/crates/iota-types/src/gas_model/tables.rs index 5959704e61b..77ad4819e9b 100644 --- a/crates/iota-types/src/gas_model/tables.rs +++ b/crates/iota-types/src/gas_model/tables.rs @@ -49,7 +49,6 @@ pub static INITIAL_COST_SCHEDULE: Lazy<CostTable> = Lazy::new(initial_cost_sched /// Provide all the proper guarantees about gas metering in the Move VM. /// /// Every client must use an instance of this type to interact with the Move VM. -#[allow(dead_code)] #[derive(Debug)] pub struct GasStatus { pub gas_model_version: u64, @@ -151,7 +150,7 @@ impl GasStatus { InternalGas::new(val * Self::INTERNAL_UNIT_MULTIPLIER) } - #[allow(dead_code)] + #[expect(dead_code)] fn to_nanos(&self, val: InternalGas) -> u64 { let gas: Gas = InternalGas::to_unit_round_down(val); u64::from(gas) * self.gas_price diff --git a/crates/iota-types/src/messages_checkpoint.rs b/crates/iota-types/src/messages_checkpoint.rs index 50a9f25d252..78cdffaeba2 100644 --- a/crates/iota-types/src/messages_checkpoint.rs +++ b/crates/iota-types/src/messages_checkpoint.rs @@ -60,7 +60,7 @@ pub struct CheckpointRequest { pub certified: bool, } -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CheckpointSummaryResponse { Certified(CertifiedCheckpointSummary), @@ -76,7 +76,6 @@ impl CheckpointSummaryResponse { } } -#[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CheckpointResponse { pub checkpoint: Option<CheckpointSummaryResponse>, diff --git a/crates/iota-types/src/messages_consensus.rs b/crates/iota-types/src/messages_consensus.rs index 77323a54518..a87ede12052 100644 --- a/crates/iota-types/src/messages_consensus.rs +++ b/crates/iota-types/src/messages_consensus.rs @@ -217,7 +217,6 @@ impl ConsensusTransactionKind { } #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -#[allow(clippy::large_enum_variant)] pub enum VersionedDkgMessage { V1(dkg_v1::Message<bls12381::G2Element, bls12381::G2Element>), } diff --git a/crates/iota-types/src/object.rs b/crates/iota-types/src/object.rs index 4cfe292d222..63ef7c06824 100644 --- a/crates/iota-types/src/object.rs +++ b/crates/iota-types/src/object.rs @@ -375,7 +375,6 @@ impl MoveObject { } #[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)] -#[allow(clippy::large_enum_variant)] pub enum Data { /// An object whose governing logic lives in a published Move module Move(MoveObject), @@ -1058,7 +1057,6 @@ pub fn generate_test_gas_objects() -> Vec<Object> { GAS_OBJECTS.with(|v| v.clone()) } -#[allow(clippy::large_enum_variant)] #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "status", content = "details")] pub enum ObjectRead { @@ -1117,7 +1115,6 @@ impl Display for ObjectRead { } } -#[allow(clippy::large_enum_variant)] #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "status", content = "details")] pub enum PastObjectRead { diff --git a/crates/iota-types/src/timelock/mod.rs b/crates/iota-types/src/timelock/mod.rs index ad30def1808..9fa70613509 100644 --- a/crates/iota-types/src/timelock/mod.rs +++ b/crates/iota-types/src/timelock/mod.rs @@ -3,7 +3,7 @@ pub mod label; pub mod stardust_upgrade_label; -#[allow(clippy::module_inception)] +#[expect(clippy::module_inception)] pub mod timelock; pub mod timelocked_staked_iota; pub mod timelocked_staking; diff --git a/crates/iota-types/src/unit_tests/passkey_authenticator_test.rs b/crates/iota-types/src/unit_tests/passkey_authenticator_test.rs index 5d4f451ebe6..41645f17c22 100644 --- a/crates/iota-types/src/unit_tests/passkey_authenticator_test.rs +++ b/crates/iota-types/src/unit_tests/passkey_authenticator_test.rs @@ -11,11 +11,11 @@ use fastcrypto::{ traits::ToFromBytes, }; use p256::pkcs8::DecodePublicKey; -use passkey_authenticator::{Authenticator, UserValidationMethod}; +use passkey_authenticator::{Authenticator, UserCheck, UserValidationMethod}; use passkey_client::Client; use passkey_types::{ Bytes, Passkey, - ctap2::Aaguid, + ctap2::{Aaguid, Ctap2Error}, rand::random_vec, webauthn::{ AttestationConveyancePreference, CredentialCreationOptions, CredentialRequestOptions, @@ -41,15 +41,10 @@ use crate::{ /// Helper struct to initialize passkey client. pub struct MyUserValidationMethod {} + #[async_trait::async_trait] impl UserValidationMethod for MyUserValidationMethod { - async fn check_user_presence(&self) -> bool { - true - } - - async fn check_user_verification(&self) -> bool { - true - } + type PasskeyItem = Passkey; fn is_verification_enabled(&self) -> Option<bool> { Some(true) @@ -58,6 +53,18 @@ impl UserValidationMethod for MyUserValidationMethod { fn is_presence_enabled(&self) -> bool { true } + + async fn check_user<'a>( + &self, + _credential: Option<&'a Self::PasskeyItem>, + _presence: bool, + _verification: bool, + ) -> Result<UserCheck, Ctap2Error> { + Ok(UserCheck { + presence: true, + verification: true, + }) + } } /// Response with fields from passkey authentication. diff --git a/crates/iota-util-mem-derive/lib.rs b/crates/iota-util-mem-derive/lib.rs index d2f308c2e52..83939225beb 100644 --- a/crates/iota-util-mem-derive/lib.rs +++ b/crates/iota-util-mem-derive/lib.rs @@ -76,7 +76,7 @@ fn malloc_size_of_derive(s: synstructure::Structure) -> proc_macro2::TokenStream let tokens = quote! { impl #impl_generics iota_util_mem::MallocSizeOf for #name #ty_generics #where_clause { #[inline] - #[allow(unused_variables, unused_mut, unreachable_code)] + #[expect(unused_variables, unused_mut, unreachable_code)] fn size_of(&self, ops: &mut iota_util_mem::MallocSizeOfOps) -> usize { let mut sum = 0; match *self { diff --git a/crates/iota/src/client_ptb/builder.rs b/crates/iota/src/client_ptb/builder.rs index b8678f11e26..c64514e2f48 100644 --- a/crates/iota/src/client_ptb/builder.rs +++ b/crates/iota/src/client_ptb/builder.rs @@ -1163,7 +1163,7 @@ pub(crate) fn display_did_you_mean<S: AsRef<str> + std::fmt::Display>( // This lint is disabled because it's not good and doesn't look at what you're // actually iterating over. This seems to be a common problem with this lint. // See e.g., https://github.com/rust-lang/rust-clippy/issues/6075 -#[allow(clippy::needless_range_loop)] +#[expect(clippy::needless_range_loop)] fn edit_distance(a: &str, b: &str) -> usize { let mut cache = vec![vec![0; b.len() + 1]; a.len() + 1]; diff --git a/crates/iota/src/genesis_inspector.rs b/crates/iota/src/genesis_inspector.rs index 6cba3824f43..5ebfacb50f1 100644 --- a/crates/iota/src/genesis_inspector.rs +++ b/crates/iota/src/genesis_inspector.rs @@ -27,7 +27,6 @@ const STR_IOTA_DISTRIBUTION: &str = "Iota Distribution"; const STR_OBJECTS: &str = "Objects"; const STR_VALIDATORS: &str = "Validators"; -#[allow(clippy::or_fun_call)] pub(crate) fn examine_genesis_checkpoint(genesis: UnsignedGenesis) { let system_object = genesis .iota_system_object() @@ -52,7 +51,7 @@ pub(crate) fn examine_genesis_checkpoint(genesis: UnsignedGenesis) { let mut iota_distribution = BTreeMap::new(); let entry = iota_distribution .entry("Iota System".to_string()) - .or_insert(BTreeMap::new()); + .or_insert_with(BTreeMap::new); entry.insert( "Storage Fund".to_string(), ( @@ -150,7 +149,7 @@ pub(crate) fn examine_genesis_checkpoint(genesis: UnsignedGenesis) { } } -#[allow(clippy::ptr_arg)] +#[expect(clippy::ptr_arg)] fn examine_validators( validator_options: &Vec<&str>, validator_map: &BTreeMap<&str, &IotaValidatorGenesis>, diff --git a/crates/iota/src/iota_commands.rs b/crates/iota/src/iota_commands.rs index 54cad050dae..e607b8dc183 100644 --- a/crates/iota/src/iota_commands.rs +++ b/crates/iota/src/iota_commands.rs @@ -146,7 +146,6 @@ impl IndexerFeatureArgs { } } -#[allow(clippy::large_enum_variant)] #[derive(Parser)] #[clap(rename_all = "kebab-case")] pub enum IotaCommand { diff --git a/crates/iota/src/keytool.rs b/crates/iota/src/keytool.rs index 246848e4c10..95b5c427139 100644 --- a/crates/iota/src/keytool.rs +++ b/crates/iota/src/keytool.rs @@ -59,7 +59,6 @@ use crate::key_identity::{KeyIdentity, get_identity_address_from_keystore}; #[path = "unit_tests/keytool_tests.rs"] mod keytool_tests; -#[allow(clippy::large_enum_variant)] #[derive(Subcommand)] #[clap(rename_all = "kebab-case")] pub enum KeyToolCommand { diff --git a/crates/simulacrum/src/lib.rs b/crates/simulacrum/src/lib.rs index 6a09576cc08..2165600de93 100644 --- a/crates/simulacrum/src/lib.rs +++ b/crates/simulacrum/src/lib.rs @@ -67,7 +67,7 @@ use self::{epoch_state::EpochState, store::in_mem_store::KeyStore}; pub struct Simulacrum<R = OsRng, Store: SimulatorStore = InMemoryStore> { rng: R, keystore: KeyStore, - #[allow(unused)] + #[expect(unused)] genesis: genesis::Genesis, store: Store, checkpoint_builder: MockCheckpointBuilder, @@ -83,7 +83,7 @@ pub struct Simulacrum<R = OsRng, Store: SimulatorStore = InMemoryStore> { impl Simulacrum { /// Create a new, random Simulacrum instance using an `OsRng` as the source /// of randomness. - #[allow(clippy::new_without_default)] + #[expect(clippy::new_without_default)] pub fn new() -> Self { Self::new_with_rng(OsRng) } diff --git a/crates/simulacrum/src/store/in_mem_store.rs b/crates/simulacrum/src/store/in_mem_store.rs index cd05dc3e8a4..9433000e28d 100644 --- a/crates/simulacrum/src/store/in_mem_store.rs +++ b/crates/simulacrum/src/store/in_mem_store.rs @@ -329,7 +329,6 @@ impl ObjectStore for InMemoryStore { #[derive(Debug)] pub struct KeyStore { validator_keys: BTreeMap<AuthorityName, AuthorityKeyPair>, - #[allow(unused)] account_keys: BTreeMap<IotaAddress, AccountKeyPair>, } diff --git a/crates/telemetry-subscribers/src/lib.rs b/crates/telemetry-subscribers/src/lib.rs index acebb2fb213..03ce9b0ba6c 100644 --- a/crates/telemetry-subscribers/src/lib.rs +++ b/crates/telemetry-subscribers/src/lib.rs @@ -69,9 +69,10 @@ pub struct TelemetryConfig { } #[must_use] -#[allow(dead_code)] pub struct TelemetryGuards { + #[expect(unused)] worker_guard: WorkerGuard, + #[expect(unused)] provider: Option<TracerProvider>, } diff --git a/crates/typed-store-derive/src/lib.rs b/crates/typed-store-derive/src/lib.rs index 9c61652616a..e1997e40824 100644 --- a/crates/typed-store-derive/src/lib.rs +++ b/crates/typed-store-derive/src/lib.rs @@ -380,7 +380,7 @@ pub fn derive_dbmap_utils_general(input: TokenStream) -> TokenStream { /// Only one process is allowed to do this at a time /// `global_db_options_override` apply to the whole DB /// `tables_db_options_override` apply to each table. If `None`, the attributes from `default_options_override_fn` are used if any - #[allow(unused_parens)] + #[expect(unused_parens)] pub fn open_tables_read_write( path: std::path::PathBuf, metric_conf: typed_store::rocks::MetricConf, @@ -395,7 +395,7 @@ pub fn derive_dbmap_utils_general(input: TokenStream) -> TokenStream { } } - #[allow(unused_parens)] + #[expect(unused_parens)] pub fn open_tables_read_write_with_deprecation_option( path: std::path::PathBuf, metric_conf: typed_store::rocks::MetricConf, @@ -415,7 +415,7 @@ pub fn derive_dbmap_utils_general(input: TokenStream) -> TokenStream { /// Only one process is allowed to do this at a time /// `global_db_options_override` apply to the whole DB /// `tables_db_options_override` apply to each table. If `None`, the attributes from `default_options_override_fn` are used if any - #[allow(unused_parens)] + #[expect(unused_parens)] pub fn open_tables_transactional( path: std::path::PathBuf, metric_conf: typed_store::rocks::MetricConf, @@ -794,7 +794,7 @@ pub fn derive_sallydb_general(input: TokenStream) -> TokenStream { /// Only one process is allowed to do this at a time /// `global_db_options_override` apply to the whole DB /// `tables_db_options_override` apply to each table. If `None`, the attributes from `default_options_override_fn` are used if any - #[allow(unused_parens)] + #[expect(unused_parens)] pub fn init( db_options: typed_store::sally::SallyDBOptions ) -> Self { diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index d142afddf15..3787e540cfd 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -351,7 +351,7 @@ impl RocksDB { fail_point!("delete-cf-before"); let ret = delegate_call!(self.delete_cf_opt(cf, key, writeopts)); fail_point!("delete-cf-after"); - #[allow(clippy::let_and_return)] + #[expect(clippy::let_and_return)] ret } @@ -373,7 +373,7 @@ impl RocksDB { fail_point!("put-cf-before"); let ret = delegate_call!(self.put_cf_opt(cf, key, value, writeopts)); fail_point!("put-cf-after"); - #[allow(clippy::let_and_return)] + #[expect(clippy::let_and_return)] ret } @@ -414,7 +414,7 @@ impl RocksDB { )), }; fail_point!("batch-write-after"); - #[allow(clippy::let_and_return)] + #[expect(clippy::let_and_return)] ret } @@ -2837,7 +2837,7 @@ fn is_max(v: &[u8]) -> bool { v.iter().all(|&x| x == u8::MAX) } -#[allow(clippy::assign_op_pattern)] +#[expect(clippy::assign_op_pattern)] #[test] fn test_helpers() { let v = vec![]; diff --git a/deny.toml b/deny.toml index 73541741be8..04e8c2cb6f6 100644 --- a/deny.toml +++ b/deny.toml @@ -88,6 +88,7 @@ allow = [ "BSL-1.0", "Unicode-DFS-2016", "OpenSSL", + "Unicode-3.0", # "Apache-2.0 WITH LLVM-exception", ] # The confidence threshold for detecting a license from license text. @@ -98,7 +99,7 @@ confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ - { allow = ["GPL-2.0"], name = "mysqlclient-src" }, + # Each entry is the crate and version constraint, and its specific allow # list diff --git a/docs/content/_snippets/info-BST-2f1.mdx b/docs/content/_snippets/info-BST-2f1.mdx new file mode 100644 index 00000000000..7c6161c2ad8 --- /dev/null +++ b/docs/content/_snippets/info-BST-2f1.mdx @@ -0,0 +1,10 @@ +:::info + +In Byzantine Fault Tolerant (BFT) `f` represents the number of faulty validators. `2f+1` represents the minimum number of validators +needed to agree on a transaction to ensure integrity and finality, even if up to `f` validators are faulty. + +For example, if `f = 1`, this means 3 validators must confirm a transaction (a supermajority) to guarantee its +integrity, +ensuring that even if one validator is faulty, the correct decision still stands. + +::: \ No newline at end of file diff --git a/docs/content/_snippets/info-high-traffic.mdx b/docs/content/_snippets/info-high-traffic.mdx new file mode 100644 index 00000000000..734ea92ab9e --- /dev/null +++ b/docs/content/_snippets/info-high-traffic.mdx @@ -0,0 +1,7 @@ +:::info + +It is suggested to use dedicated nodes/shared services rather than public endpoints for production apps with high traffic volume. + +You can either run your own Full nodes, or outsource this to a professional infrastructure provider (preferred for apps that have high traffic). + +::: \ No newline at end of file diff --git a/docs/content/_snippets/warn-ml-rpcs.mdx b/docs/content/_snippets/warn-ml-rpcs.mdx deleted file mode 100644 index d54a7bd25aa..00000000000 --- a/docs/content/_snippets/warn-ml-rpcs.mdx +++ /dev/null @@ -1,9 +0,0 @@ -:::caution - -Use dedicated nodes/shared services rather than public endpoints for production apps. The public endpoints maintained -by the IOTA Foundation are rate-limited, and support only 100 requests -per 30 seconds. Do not use public endpoints in production applications with high traffic volume. - -You can either run your own Full nodes, or outsource this to a professional infrastructure provider (preferred for apps that have high traffic). - -::: \ No newline at end of file diff --git a/docs/content/about-iota/iota-architecture/epochs.mdx b/docs/content/about-iota/iota-architecture/epochs.mdx index e543edcc4d6..bfbbb3ade6d 100644 --- a/docs/content/about-iota/iota-architecture/epochs.mdx +++ b/docs/content/about-iota/iota-architecture/epochs.mdx @@ -1,3 +1,4 @@ +import BSTInfo from '../../_snippets/info-BST-2f1.mdx'; import Quiz from '@site/src/components/Quiz'; import {questions} from '../../../site/static/json/about-iota/iota-architecture/epochs.json'; @@ -37,6 +38,8 @@ validator set and stake distribution can be altered. ### 4. Protocol Upgrade +<BSTInfo/> + If 2f+1 validators agree, the network may upgrade to a new protocol version, which includes new features, bug fixes, and updates to the Move framework libraries. diff --git a/docs/content/about-iota/iota-architecture/iota-security.mdx b/docs/content/about-iota/iota-architecture/iota-security.mdx index bb6633c8fd9..f524ccf7030 100644 --- a/docs/content/about-iota/iota-architecture/iota-security.mdx +++ b/docs/content/about-iota/iota-architecture/iota-security.mdx @@ -47,7 +47,7 @@ Move allows for shared assets. Although these shared assets are public in princi ### Certification and Finalization -When you submit a transaction in IOTA, all the validators must agree that it's valid. Once they agree, they create a certificate to confirm its validity, and this certificate must also be shared with all validators. Even if some validators don't follow the rules, the transaction can still be finalized by the majority of validators who do follow the IOTA protocol. This process uses cryptographic methods to ensure that validators who don't follow the rules can't trick the system into accepting false information and that misbehaving validators can't stop the system from processing transactions. +When you submit a transaction in IOTA, all the validators must agree that it's valid. Once they agree, they create a _certificate_ to confirm its validity, and this _certificate_ must also be shared with all validators. Even if some validators don't follow the rules, the transaction can still be finalized by the majority of validators who do follow the IOTA protocol. This process uses cryptographic methods to ensure that validators who don't follow the rules can't trick the system into accepting false information and that misbehaving validators can't stop the system from processing transactions. ### Gas and Transaction Execution diff --git a/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx b/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx index f2146ee274a..599dff115f7 100644 --- a/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx +++ b/docs/content/about-iota/iota-architecture/transaction-lifecycle.mdx @@ -18,13 +18,13 @@ import ThemedImage from '@theme/ThemedImage'; 2. **Submit to Validators**: The IOTA protocol sends the transaction to every validator. The validators validate the transaction. If valid, the validators sign it and return the signed transaction to the client. -3. **Form a Transaction Certificate**: After collecting responses from a supermajority of validators, the client can form a transaction certificate. Unlike consensus-based blockchains, IOTA validators are not burdened by needing to propagate signatures or aggregate certificates; this is the responsibility of the client or gateway. +3. **Form a Transaction Certificate**: After collecting responses from a supermajority of validators, the client can form a transaction _certificate_. Unlike consensus-based blockchains, IOTA validators are not burdened by needing to propagate signatures or aggregate _certificate_s; this is the responsibility of the client or gateway. -4. **Send the Certificate**: The client sends the assembled certificate back to all validators. The validators check its validity and acknowledge receipt. If the transaction involves only owned objects, IOTA can process and execute it immediately without waiting for consensus (**fast path consensus**). All certificates are forwarded to the IOTA DAG-based consensus protocol. +4. **Send the Certificate**: The client sends the assembled _certificate_ back to all validators. The validators check its validity and acknowledge receipt. If the transaction involves only owned objects, IOTA can process and execute it immediately without waiting for consensus (**fast path consensus**). All _certificate_s are forwarded to the IOTA DAG-based consensus protocol. -5. **Consensus and Execution**: The IOTA DAG-based consensus protocol eventually produces a total order of certificates. Validators check and execute certificates that involve shared objects. +5. **Consensus and Execution**: The IOTA DAG-based consensus protocol eventually produces a total order of _certificate_s. Validators check and execute _certificate_s that involve shared objects. -6. **Effect Certificate**: Clients can gather responses from a supermajority of validators, assemble them into an effect certificate, and use it as proof that the transaction is settled. +6. **Effect Certificate**: Clients can gather responses from a supermajority of validators, assemble them into an effect _certificate_, and use it as proof that the transaction is settled. 7. **Checkpoints and Reconfiguration**: IOTA forms checkpoints for every consensus commit, which are also used to drive the reconfiguration protocol. @@ -42,13 +42,13 @@ Your wallet app sends the signed transaction to a full node, which then broadcas ### Transaction Certification -Validators receive the transaction from the full node. They check if it's valid and lock the involved objects to prevent double-spending. After validation, they send their signature back to the full node. When the full node collects enough validator signatures (a quorum), it creates a transaction certificate, which includes the transaction and the validator signatures. +Validators receive the transaction from the full node. They check if it's valid and lock the involved objects to prevent double-spending. After validation, they send their signature back to the full node. When the full node collects enough validator signatures (a quorum), it creates a transaction _certificate_, which includes the transaction and the validator signatures. ### Transaction Finalization -The full node broadcasts this transaction certificate to all validators. Validators verify the certificate, execute the transaction, and unlock the previously locked objects. They then sign the transaction effects (a detailed list of changes) and return these signed effects to the full node. The full node verifies these effects and, once it has enough signatures, forms an effects certificate. +The full node broadcasts this transaction _certificate_ to all validators. Validators verify the _certificate_, execute the transaction, and unlock the previously locked objects. They then sign the transaction effects (a detailed list of changes) and return these signed effects to the full node. The full node verifies these effects and, once it has enough signatures, forms an effects _certificate_. -Your wallet app receives this effects certificate, which you can show to the coffee shop to prove that the transaction is complete and irreversible. +Your wallet app receives this effects _certificate_, which you can show to the coffee shop to prove that the transaction is complete and irreversible. ## Transaction Life Cycle @@ -67,13 +67,13 @@ After submission to a full node, the certification process begins. The full node If these checks pass, the validator locks the input objects to the transaction, preventing double-spending, and proceeds to sign the transaction and returns the signature to the node. The node needs signatures from a majority of validators to certify the transaction. -The full node collects these signatures in parallel to speed up the process. Once it has enough signatures (a quorum), the transaction is certified, forming a **transaction certificate**. +The full node collects these signatures in parallel to speed up the process. Once it has enough signatures (a quorum), the transaction is certified, forming a **transaction _certificate_**. Even if some validators are dishonest, the principle of "quorum intersection" ensures that as long as most validators are honest, double-spending is prevented. An honest validator will not sign two transactions that try to use the same object at the same time. ### Execution -Certified transactions are then sent to validators for **execution**. Validators verify the certificate signatures to ensure the transaction is valid and not attempting to double-spend. +Certified transactions are then sent to validators for **execution**. Validators verify the _certificate_ signatures to ensure the transaction is valid and not attempting to double-spend. Depending on whether the transaction uses shared input objects, the validators will either: @@ -90,9 +90,9 @@ After the transaction is executed, the validator signs off on the effects of the * The amount of gas spent. * The transaction's execution status (either success or an error code). -The full node then gathers these signed effects from a majority of validators, forming an **effects certificate**, which guarantees that the transaction is final. +The full node then gathers these signed effects from a majority of validators, forming an **effects _certificate_**, which guarantees that the transaction is final. -Once you or the full node sees an effects certificate, you can be sure that the transaction will be included in a [checkpoint](#checkpoints), meaning it can't be undone. This certificate can also serve as proof that you sent the NFT to your friend since it can't be faked due to the validator signatures. +Once you or the full node sees an effects _certificate_, you can be sure that the transaction will be included in a [checkpoint](#checkpoints), meaning it can't be undone. This _certificate_ can also serve as proof that you sent the NFT to your friend since it can't be faked due to the validator signatures. ### Checkpoints @@ -109,25 +109,25 @@ Transaction finality means that once a transaction is executed, it can't be reve ### Settlement Finality -After executing a transaction, validators send back the signed effects to the network. When a majority of validators have executed the transaction and the [effects certificate](#effects-certificate) exists, the transaction's effects (like transfers or newly created objects) are implemented. This allows the network to process new transactions that depend on these effects. +After executing a transaction, validators send back the signed effects to the network. When a majority of validators have executed the transaction and the [effects _certificate_](#effects-certificate) exists, the transaction's effects (like transfers or newly created objects) are implemented. This allows the network to process new transactions that depend on these effects. Settlement finality depends on [object ownership](../../developer/iota-101/objects/object-ownership/object-ownership.mdx). For transactions involving only owned objects, this happens quickly, in under half a second. For those involving shared objects, it happens shortly after consensus, which can take a few seconds. At this point, the transaction reaches settlement finality, meaning the network can now process more transactions using the same input objects. ### Checkpoint Processing -The [real-world example](#real-world-example) at the beginning of the article demonstrates a finalized transaction through an effects certificate. However, if a full node goes offline before collecting all signatures, your wallet app will try another full node. If your phone dies during this process, the coffee shop will still see your payment on its terminal connected to a different full node, thanks to checkpoints. +The [real-world example](#real-world-example) at the beginning of the article demonstrates a finalized transaction through an effects _certificate_. However, if a full node goes offline before collecting all signatures, your wallet app will try another full node. If your phone dies during this process, the coffee shop will still see your payment on its terminal connected to a different full node, thanks to checkpoints. A checkpoint contains a list of transactions and is signed by a majority of validators, making it final. If a full node learns about your transaction through checkpoints, it ensures that the transaction will be finalized. ### Local Execution on a Full Node -Before sending back an effects certificate, a full node might execute the transaction locally if the request asks it to. This is more important for high-frequency applications like gaming but can add unnecessary delay for simple transactions like buying coffee. The `WaitForLocalExecution` parameter requests this local execution, while you can use the `WaitForEffects` parameter for a quicker response. +Before sending back an effects _certificate_, a full node might execute the transaction locally if the request asks it to. This is more important for high-frequency applications like gaming but can add unnecessary delay for simple transactions like buying coffee. The `WaitForLocalExecution` parameter requests this local execution, while you can use the `WaitForEffects` parameter for a quicker response. Additionally, when any app builds a transaction, the full node is usually in charge of choosing the object that is used to pay for the transaction's gas. Since gas is paid in IOTA, which is a shared object, if the full node is not up-to-date, it could potentially lead to an invalid transaction or even a [client equivocation](../../developer/iota-101/transactions/sponsored-transactions/about-sponsored-transactions.mdx#risk-considerations). You can avoid this unwanted behavior by sending the `WaitForLocalExecution` parameter. ### Epoch Change -Every ~24 hours, the IOTA network undergoes an epoch change, during which staking rewards are calculated and distributed, validator metadata is updated, and other network processes occur. User transactions are paused during this time. If your transaction is submitted at the epoch boundary, it may need resubmission in the new epoch. Transactions that were certified but not yet finalized will be reverted. Any transaction certificate from the previous epoch will become invalid, so the full node will resubmit the invalid transactions. +Every ~24 hours, the IOTA network undergoes an epoch change, during which staking rewards are calculated and distributed, validator metadata is updated, and other network processes occur. User transactions are paused during this time. If your transaction is submitted at the epoch boundary, it may need resubmission in the new epoch. Transactions that were certified but not yet finalized will be reverted. Any transaction _certificate_ from the previous epoch will become invalid, so the full node will resubmit the invalid transactions. ### Verifying Finality diff --git a/docs/content/about-iota/iota-wallet/how-to/integrate-ledger.mdx b/docs/content/about-iota/iota-wallet/how-to/integrate-ledger.mdx index 093140c0f72..735c8d02253 100644 --- a/docs/content/about-iota/iota-wallet/how-to/integrate-ledger.mdx +++ b/docs/content/about-iota/iota-wallet/how-to/integrate-ledger.mdx @@ -17,7 +17,10 @@ Before connecting your Ledger device to IOTA Wallet, ensure the following: ## Install the IOTA Rebased App on Your Ledger Device -To use IOTA Wallet with Ledger, install the IOTA Rebased app on your device through Ledger Live. +To use IOTA Wallet with Ledger, install the IOTA Rebased app on your device through Ledger Live or manually. +Ledger Live is preferred, but you need to install the app manually for deprecated devices like the Ledger Nano S. + +### Ledger Live (preferred) 1. Unlock your Ledger device. 2. Open Ledger Live and navigate to **My Ledger** in the left panel. @@ -26,6 +29,25 @@ To use IOTA Wallet with Ledger, install the IOTA Rebased app on your device thro 5. Click **Install** to download the IOTA Rebased app to your device. 6. Your device will show the installation progress. +### Manually (for deprecated devices such as Ledger Nano S) + +:::warning + +Make sure you have enough space left for an additional app on your device. + +::: + +1. Go to the latest release on https://github.com/iotaledger/ledger-app-iota/releases. +2. Download the archive matching your device, e.g. `nanos.tar.gz` for the Ledger Nano S. +3. Verify that the checksum (`shasum -a 256 nanos.tar.gz`) matches the one from the release. +4. Extract the archive `tar -xvzf nanos.tar.gz`. +5. Move to the app folder `cd nanos`. +6. Follow the steps on https://github.com/LedgerHQ/ledgerctl to install `ledgerctl`. +7. Install the app on your device `ledgerctl install -f app_nanos.json`. +8. Accept `Allow unsafe manager` on your device. +9. Accept `Perform installation` on your device. +10. Enter your pin on your device. + ## Import Accounts from Your Ledger Device To import accounts from your Ledger into IOTA Wallet: diff --git a/docs/content/developer/getting-started/getting-started.mdx b/docs/content/developer/getting-started/getting-started.mdx index 76a0fa944ef..ae2bdb4fff8 100644 --- a/docs/content/developer/getting-started/getting-started.mdx +++ b/docs/content/developer/getting-started/getting-started.mdx @@ -10,7 +10,7 @@ tags: # Getting Started IOTA Rebased introduces layer 1 Move smart contracts to the IOTA ecosystem. This valuable addition enhances IOTA by -offering programmability on layer1, complementing [IOTA EVM](../../about-iota/about-iota.mdx#iota-evm-and-shimmer-evm) on layer 2. +offering programmability on layer 1, complementing [IOTA EVM](../../about-iota/about-iota.mdx#iota-evm-and-shimmer-evm) on layer 2. The guides in this section will guide you as you start your IOTA Rebased journey. We recommend that you start by [setting up your development environment](iota-environment.mdx), and then move on diff --git a/docs/content/developer/getting-started/local-network.mdx b/docs/content/developer/getting-started/local-network.mdx index ff670dacf55..8451b219379 100644 --- a/docs/content/developer/getting-started/local-network.mdx +++ b/docs/content/developer/getting-started/local-network.mdx @@ -274,5 +274,4 @@ pnpm add <PATH_TO_YOUR_REPO>/sdk/typescript and the locally compiled version of `@iota/iota-sdk` package will be installed for your application. - <Quiz questions={questions} /> diff --git a/docs/content/developer/iota-101/using-events.mdx b/docs/content/developer/iota-101/using-events.mdx index e287fd30612..4505faeb763 100644 --- a/docs/content/developer/iota-101/using-events.mdx +++ b/docs/content/developer/iota-101/using-events.mdx @@ -28,13 +28,13 @@ An event object in IOTA consists of the following attributes: - `bcs`: Binary canonical serialization value. - `timestampMs`: Unix epoch timestamp in milliseconds. -## Exploring Available Events +### Exploring Available Events -To subscribe to on-chain events, you first need to identify which events are available. While you can easily track events emitted by your own code, discovering events from external packages can be more challenging. The IOTA RPC provides the [`queryEvents`](/iota-api-ref#iotax_queryevents) method, which allows you to query on-chain packages and obtain a list of events you can subscribe to. +To [subscribe to on-chain events](#subscribing-to-events), you first need to identify which events are available. While you can easily track events emitted by your own code, discovering events from external packages can be more challenging. The IOTA RPC provides the [`queryEvents`](/iota-api-ref#iotax_queryevents) method, which allows you to query on-chain packages and obtain a list of events you can subscribe to. -## Applying Event Filters +### Applying Event Filters -When targeting specific events for querying or subscribing, you can use filters to refine your results. Although the filtering options for querying and subscribing are similar, there are notable differences to be aware of. +When targeting specific events for [querying](#querying-events) or [subscribing](#subscribing-to-events), you can use [filters](#filtering-events) to refine your results. Although the filtering options for querying and subscribing are similar, there are notable differences to be aware of. ## Emitting Events in Move @@ -49,65 +49,117 @@ use iota::event; Then, within your function, you can emit an event using the [`emit`](../../references/framework/iota-framework/event.mdx#function-emit) function. For example: -```move -/// Take coin from `DonutShop` and transfer it to tx sender. -/// Requires authorization with `ShopOwnerCap`. -public fun collect_profits( _: &ShopOwnerCap, shop: &mut DonutShop, ctx: &mut TxContext ) { - let amount = balance::value(&shop.balance); - let profits = coin::take(&mut shop.balance, amount, ctx); - // simply create new type instance and emit it. - event::emit(ProfitsCollected { amount }); - transfer::public_transfer(profits, tx_context::sender(ctx)); -} +```move file=<rootDir>/examples/trading/contracts/escrow/sources/lock.move#L42-L63 ``` -## Subscribing to Events +## Querying Events + +<Tabs> + +<TabItem value="RPC" label="IOTA RPC"> +The IOTA RPC provides a [`queryEvents`](/iota-api-ref#iotax_queryevents) method to query on-chain packages and return available events. As an example, the following `curl` command queries the Deepbook package on Mainnet for a specific type of event: + +```sh +curl -X POST https://api.testnet.iota.cafe:443 \ +-H "Content-Type: application/json" \ +-d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "iotax_queryEvents", + "params": [ + { + "MoveModule": { + "package": "0x0000000000000000000000000000000000000000000000000000000000000002", + "module": "display", + "type": "0x0000…0002::display::Display<0xba68…286b::testnet_nft::TestnetNFT>" + } + }, + null, + 3, + false + ] +}' +``` + +The TypeScript SDK provides a wrapper for the `iotax_queryEvents` method: +[`client.queryEvents`](../../ts-sdk/api/client/classes/IotaClient#queryevents). -To react to events emitted by Move modules, you need to subscribe to them. -IOTA full nodes support event subscriptions via JSON-RPC notifications over WebSocket. You can interact with the [RPC directly][iotax_subscribeEvent](/iota-api-ref#iotax_subscribeevent), [iotax_subscribeTransaction](/iota-api-ref#iotax_subscribetransaction) or use an SDK like the [IOTA TypeScript SDK](../../ts-sdk/typescript/index.mdx). +</TabItem> +<TabItem value="rs" label="Rust"> -The following excerpt from one of the examples uses the TypeScript SDK to create an asynchronous subscription to the filter identified in the filter. +You can use the following as an example on how to query for events using the `query_events` function. You should update +the `PACKAGE_ID_CONST` with a package of your choice. -```move -let unsubscribe = await provider.subscribeEvent({ - filter: { <PACKAGE_ID> }, - onMessage: (event) => { - console.log("subscribeEvent", JSON.stringify(event, null, 2)) +```rust +use iota_sdk::{rpc_types::EventFilter, types::Identifier, IotaClientBuilder}; + +const PACKAGE_ID_CONST: &str = ""; + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + let iota_testnet = IotaClientBuilder::default() + .build("https://api.testnet.iota.cafe:443") + .await?; + + let events = iota_testnet + .event_api() + .query_events( + EventFilter::MoveModule { + package: PACKAGE_ID_CONST.parse()?, + module: Identifier::new("dev_trophy")?, + }, + None, + None, + false, + ) + .await?; + + for event in events.data { + println!("Event: {:?}", event.parsed_json); } -}); + + Ok(()) +} ``` +</TabItem> +<TabItem value="graphql" label="GraphQL"> -Move smart contracts can call other smart contracts that emit events. For example, `0x107a::nft` calls the `0x2::display::new_with_fields` smart contract and emits a `0x2::display::DisplayCreated` event. Note that using package and transaction module to query for `0x2::display` misses the following event even though it is actually an event the `0x2` package emits. The current workaround for this issue is to know all the `packageId`s you care about and search those in the `queryEvent` call. +You can use GraphQL to query events instead of JSON RPC. The following example queries are in the +[`iota-graphql-rpc` crate](https://github.com/iotaledger/iota/tree/develop/crates/iota-graphql-rpc/examples/event_connection) in the IOTA repo. -```json -{ - "id": { - "txDigest": "DrZmtQDDCUKooLzFCi29VhUB4w6AT8phCsT9d62BAf8g", - "eventSeq": "0" - }, - "packageId": "0x000000000000000000000000000000000000000000000000000000000000107a", - "transactionModule": "nft", - "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", - "type": "0x2::display::DisplayCreated<0x107a::nft::Nft>", - "parsedJson": { - "id": "0xa12d72c159d57d4c7f540b2b9e985327628d856b20c1d6cdfd3028a2a605abfe" - }, - "bcs": "CFbAeqXAwwkyTxUD36FtzTGEcMGrVj4zgcTR1G7AaRjb", - "timestampMs": "1521456213521" -} +#### Event Connection + +```graphql file=<rootDir>/crates/iota-graphql-rpc/examples/event_connection/event_connection.graphql +``` + +#### Filter Events By Sender + +```graphql file=<rootDir>/crates/iota-graphql-rpc/examples/event_connection/filter_by_sender.graphql ``` -## Examples +#### Filter Events By Emitting Package and Type -### Subscribe to Event +```graphql file=<rootDir>/crates/iota-graphql-rpc/examples/event_connection/filter_by_sender.graphql +``` + +:::tip + +The [TypeScript SDK](../../ts-sdk/api/graphql/classes/IotaGraphQLClient) provides functionality to +interact with the IOTA GraphQL service. + +::: + +</TabItem> +</Tabs> + + +## Subscribing to Events This example leverages the IOTA TypeScript SDK to subscribe to events the package with ID `<PACKAGE_ID>` emits. Each time the event fires, the code displays the response to the console. <Tabs> <TabItem value="rs" label="Rust"> -### Rust - See [Rust SDK](../../references/rust-sdk.mdx). ```rust @@ -132,8 +184,6 @@ async fn main() -> Result<()> { </TabItem> <TabItem value="ts" label="TypeScript"> -### TypeScript - To create the event subscription, you can use a basic Node.js app. You need the [IOTA TypeScript SDK](../../ts-sdk/typescript/index.mdx), so install the module using `npm install @iota/iota-sdk` at the root of your project. In your TypeScript code, import `JsonRpcProvider` and a connection from the library. ```ts @@ -199,9 +249,19 @@ subscribeEvent { </TabItem> </Tabs> +## Monitoring Events + +Firing events is not very useful in a vacuum. You also need the ability to respond to those events. +There are two methods from which to choose when you need to monitor on-chain events: +- Incorporate a [custom indexer](../advanced/custom-indexer.mdx) to take advantage of IOTA's micro-data ingestion framework. +- Poll the IOTA network on a schedule to query events. + +Using a custom indexer provides a near-real time monitoring of events, so is most useful when your project requires immediate reaction to the firing of events. Polling the network is most useful when the events you're monitoring don't fire often or the need to act on those events are not immediate. The following section provides a polling example. + + ## Filtering Events -You can filter events when querying or subscribing to receive only the events you are interested in. +You can filter events when [querying](#querying-events) or [subscribing](#subscribing-to-events) to receive only the events you are interested in. :::info diff --git a/docs/content/operator/genesis.mdx b/docs/content/operator/genesis.mdx index cd5d30a6146..c2f87446c06 100644 --- a/docs/content/operator/genesis.mdx +++ b/docs/content/operator/genesis.mdx @@ -2,7 +2,7 @@ title: Genesis --- -Genesis is the initial state of the IOTA blockchain. To launch a network, the initial committee of validators collaborate by providing their validator information (public keys, network addresses, and so on) to a shared workspace. After all of the initial validators have contributed their information, IOTA generates the initial, unsigned genesis checkpoint (checkpoint with sequence number 0) and each validator provides their signature. IOTA aggregates these signatures to form a certificate on the genesis checkpoint. IOTA bundles this checkpoint, as well as the initial objects, together into a single genesis.blob file that is used to initialize the state when running the `iota-node` binary for both validators and Full nodes. +Genesis is the initial state of the IOTA blockchain. To launch a network, the initial committee of validators collaborate by providing their validator information (public keys, network addresses, and so on) to a shared workspace. After all of the initial validators have contributed their information, IOTA generates the initial, unsigned genesis checkpoint (checkpoint with sequence number 0) and each validator provides their signature. IOTA aggregates these signatures to form a _certificate_ on the genesis checkpoint. IOTA bundles this checkpoint, as well as the initial objects, together into a single genesis.blob file that is used to initialize the state when running the `iota-node` binary for both validators and Full nodes. ## Genesis blob locations diff --git a/docs/content/operator/iota-full-node/overview.mdx b/docs/content/operator/iota-full-node/overview.mdx index 00f91922651..cdd63215d70 100644 --- a/docs/content/operator/iota-full-node/overview.mdx +++ b/docs/content/operator/iota-full-node/overview.mdx @@ -3,6 +3,7 @@ title: Overview description: Operate an IOTA Full Node to validate blockchain activities, like transactions, checkpoints, and epoch changes. --- import Quiz from '@site/src/components/Quiz'; +import BSTInfo from '../../_snippets/info-BST-2f1.mdx'; import questions from '/json/node-operators/iota-full-node/overview.json'; import WarningAdvanced from './../../_snippets/warning-advanced-instructions-node-setup.mdx' @@ -24,10 +25,12 @@ IOTA Full nodes: IOTA Full nodes sync with validators to receive new transactions on the network. -A transaction requires a few round trips to 2f+1 validators to form a transaction certificate (TxCert). +A transaction requires a few round trips to 2f+1 validators to form a transaction _certificate_ (TxCert). This synchronization process includes: +<BSTInfo/> + 1. Following 2f+1 validators and listening for newly committed transactions. 1. Making sure that 2f+1 validators recognize the transaction and that it reaches finality. 1. Executing the transaction locally and updating the local DB. diff --git a/docs/content/operator/observability.mdx b/docs/content/operator/observability.mdx index 03676ee1447..6467539eed0 100644 --- a/docs/content/operator/observability.mdx +++ b/docs/content/operator/observability.mdx @@ -99,7 +99,7 @@ To generate detailed span start and end logs, define the `IOTA_JSON_SPAN_LOGS` e You can send this output to a tool or service for indexing, alerts, aggregation, and analysis. -The following example output shows certificate processing in the authority with span logging. Note the `START` and `END` annotations, and notice how `DB_UPDATE_STATE` which is nested is embedded within `PROCESS_CERT`. Also notice `elapsed_milliseconds`, which logs the duration of each span. +The following example output shows _certificate_ processing in the authority with span logging. Note the `START` and `END` annotations, and notice how `DB_UPDATE_STATE` which is nested is embedded within `PROCESS_CERT`. Also notice `elapsed_milliseconds`, which logs the duration of each span. ```bash {"v":0,"name":"iota","msg":"[PROCESS_CERT - START]","level":20,"hostname":"Evan-MLbook.lan","pid":51425,"time":"2022-03-08T22:48:11.241421Z","target":"iota_core::authority_server","line":67,"file":"iota_core/src/authority_server.rs","tx_digest":"t#d1385064287c2ad67e4019dd118d487a39ca91a40e0fd8e678dbc32e112a1493"} diff --git a/docs/content/operator/validator-committee.mdx b/docs/content/operator/validator-committee.mdx index 598939eeffb..85920009733 100644 --- a/docs/content/operator/validator-committee.mdx +++ b/docs/content/operator/validator-committee.mdx @@ -1,6 +1,6 @@ --- title: Validator Committee -description: IOTA has a committee of validators to verify on-chain transactions. Epochs, quorums, transactions, certificates, and consensus are touch points for this committee. +description: IOTA has a committee of validators to verify on-chain transactions. Epochs, quorums, transactions, _certificate_s, and consensus are touch points for this committee. --- import Quiz from '@site/src/components/Quiz'; @@ -20,38 +20,38 @@ Operation of the IOTA network is temporally partitioned into non-overlapping, ap A quorum is a set of validators whose combined voting power is greater than two-thirds (>2/3) of the total during a particular epoch. For example, in an IOTA instance operated by four validators that all have the same voting power, any group containing three validators is a quorum. -The quorum size of >2/3 ensures Byzantine fault tolerance (BFT). A validator commits a transaction (durably store the transaction and update its internal state with the effects of the transaction) only if it is accompanied by cryptographic signatures from a quorum. IOTA calls the combination of the transaction and the quorum signatures on its bytes a *certificate*. The policy of committing only certificates ensures Byzantine fault tolerance: if >2/3 of the validators faithfully follow the protocol, they are guaranteed to eventually agree on both the set of committed certificates and their effects. +The quorum size of >2/3 ensures Byzantine fault tolerance (BFT). A validator commits a transaction (durably store the transaction and update its internal state with the effects of the transaction) only if it is accompanied by cryptographic signatures from a quorum. IOTA calls the combination of the transaction and the quorum signatures on its bytes a _certificate_. The policy of committing only _certificate_s ensures Byzantine fault tolerance: if >2/3 of the validators faithfully follow the protocol, they are guaranteed to eventually agree on both the set of committed _certificate_s and their effects. ## Write requests -A validator can handle two types of write requests: transactions and certificates. At a high level, a client: +A validator can handle two types of write requests: transactions and _certificate_s. At a high level, a client: -- Communicates a transaction to a quorum of validators to collect the signatures required to form a certificate. -- Submits a certificate to a validator to commit state changes on that validator. +- Communicates a transaction to a quorum of validators to collect the signatures required to form a _certificate_. +- Submits a _certificate_ to a validator to commit state changes on that validator. ### Transactions -When a validator receives a transaction from a client, it first performs transaction validity checks (validity of the sender's signature). If the checks pass, the validator locks all owned-objects and signs the transaction bytes, then returns the signature to the client. The client repeats this process with multiple validators until it has collected signatures on its transaction from a quorum, thereby forming a certificate. +When a validator receives a transaction from a client, it first performs transaction validity checks (validity of the sender's signature). If the checks pass, the validator locks all owned-objects and signs the transaction bytes, then returns the signature to the client. The client repeats this process with multiple validators until it has collected signatures on its transaction from a quorum, thereby forming a _certificate_. -The process of collecting validator signatures on a transaction into a certificate and the process of submitting certificates can be performed in parallel. The client can simultaneously multicast transactions/certificates to an arbitrary number of validators. Alternatively, a client can outsource either or both of these tasks to a third-party service provider. This provider must be trusted for liveness (it can refuse to form a certificate), but not for safety (it cannot change the effects of the transaction, and does not need the user's secret key). +The process of collecting validator signatures on a transaction into a _certificate_ and the process of submitting _certificate_s can be performed in parallel. The client can simultaneously multicast transactions/_certificate_s to an arbitrary number of validators. Alternatively, a client can outsource either or both of these tasks to a third-party service provider. This provider must be trusted for liveness (it can refuse to form a _certificate_), but not for safety (it cannot change the effects of the transaction, and does not need the user's secret key). ### Certificates -After the client forms a certificate, it submits it to the validators, which perform certificate validity checks. These checks ensure the signers are validators in the current epoch, and the signatures are cryptographically valid. If the checks pass, the validators execute the transaction inside the certificate. Execution of a transaction either succeeds and commits all of its effects or aborts and has no effect other than debiting the transaction's gas input. Some reasons a transaction might abort include an explicit abort instruction, a runtime error such as division by zero, or exceeding the maximum gas budget. Whether it succeeds or aborts, the validator durably stores the certificate indexed by the hash of its inner transaction. +After the client forms a _certificate_, it submits it to the validators, which perform _certificate_ validity checks. These checks ensure the signers are validators in the current epoch, and the signatures are cryptographically valid. If the checks pass, the validators execute the transaction inside the _certificate_. Execution of a transaction either succeeds and commits all of its effects or aborts and has no effect other than debiting the transaction's gas input. Some reasons a transaction might abort include an explicit abort instruction, a runtime error such as division by zero, or exceeding the maximum gas budget. Whether it succeeds or aborts, the validator durably stores the _certificate_ indexed by the hash of its inner transaction. -If a client collects a quorum of signatures on the effects of the transaction, then the client has a promise of finality. This means that transaction effects persist on the shared database and are actually committed and visible to everyone by the end of the epoch. This does not mean that the latency is a full epoch, because you can use the effects certificate to convince anyone of the transactions finality, as well as to access the effects and issue new transactions. As with transactions, you can parallelize the process of sharing a certificate with validators and (if desired) outsource to a third-party service provider. +If a client collects a quorum of signatures on the effects of the transaction, then the client has a promise of finality. This means that transaction effects persist on the shared database and are actually committed and visible to everyone by the end of the epoch. This does not mean that the latency is a full epoch, because you can use the effects _certificate_ to convince anyone of the transactions finality, as well as to access the effects and issue new transactions. As with transactions, you can parallelize the process of sharing a _certificate_ with validators and (if desired) outsource to a third-party service provider. ## The role of Narwhal and Bullshark IOTA takes advantage of Narwhal and Bullshark as its mempool and consensus engines. Narwhal/Bullshark (N/B) is also implemented in IOTA so that when Byzantine agreement is required it uses a high-throughput DAG-based consensus to manage shared locks while execution on different shared objects is parallelized. -Narwhal enables the parallel ordering of transactions into batches that are collected into concurrently proposed blocks, and Bullshark defines an algorithm for executing the DAG that these blocks form. N/B combined builds a DAG of blocks, concurrently proposed, and creates an order between those blocks as a byproduct of the building of the DAG. But that order is overlaid on top of the causal order of IOTA transactions (the "payload" of Narwhal/Bullshark here), and does not substitute for it: +Narwhal enables the parallel ordering of transactions into batches that are collected into concurrently proposed blocks, and Bullshark defines an algorithm for executing the DAG that these blocks form. N/B combined builds a DAG of blocks, concurrently proposed, and creates an order between those blocks as a byproduct of the building of the DAG. But that order is overlaid on top of the _causal order_ of IOTA transactions (the "payload" of Narwhal/Bullshark here), and does not substitute for it: - N/B operates in OX, rather than XO mode (O = order, X = execute); the execution occurs after the Narwhal/Bullshark ordering. - The output of N/B is therefore a sequence of transactions, with interdependencies stored in the transaction data itself. -Consensus sequences certificates of transactions. These represent transactions that have already been presented to 2/3 of validators that checked that all their owned objects are available to be operated on and signed the transaction. Upon a certificate being sequenced, IOTA sets the lock of the shared objects at the next available version to map to the execution of that certificate. So for example if you have a shared object X at version 2, and you sequence certificate T, IOTA stores T -> [(X, 2)]. That is all you do when IOTA reaches consensus, and as a result IOTA can ingest a lot of sequenced transactions. +Consensus sequences _certificate_s of transactions. These represent transactions that have already been presented to 2/3 of validators that checked that all their owned objects are available to be operated on and signed the transaction. Upon a _certificate_ being sequenced, IOTA sets the lock of the shared objects at the next available version to map to the execution of that _certificate_. So for example if you have a shared object X at version 2, and you sequence _certificate_ T, IOTA stores T -> [(X, 2)]. That is all you do when IOTA reaches consensus, and as a result IOTA can ingest a lot of sequenced transactions. -Now, after this is done IOTA can execute all certificates that have their locks set, on one or multiple cores. Obviously, transactions for earlier versions of objects need to be processed first (causally), and that reduces the degree of concurrency. The read and write set of the transaction can be statically determined from its versioned object inputs--execution can only read/write an object that was an input to the transaction, or that was created by the transaction. +Now, after this is done IOTA can execute all _certificate_s that have their locks set, on one or multiple cores. Obviously, transactions for earlier versions of objects need to be processed first (causally), and that reduces the degree of concurrency. The read and write set of the transaction can be statically determined from its versioned object inputs--execution can only read/write an object that was an input to the transaction, or that was created by the transaction. -<Quiz questions={questions} /> \ No newline at end of file +<Quiz questions={questions} /> diff --git a/docs/content/references/iota-api.mdx b/docs/content/references/iota-api.mdx index ac1d15e9f42..01a192f4c31 100644 --- a/docs/content/references/iota-api.mdx +++ b/docs/content/references/iota-api.mdx @@ -5,7 +5,7 @@ title: IOTA RPC :::info -The IOTA RPC is upgrading from JSON-RPC to GraphQL. See [GraphQL for IOTA RPC](../developer/graphql-rpc.mdx) for more information. +Checkout our experimental [GraphQL for IOTA RPC](../developer/graphql-rpc.mdx) as an alternative option. ::: diff --git a/docs/content/references/iota-api/rpc-best-practices.mdx b/docs/content/references/iota-api/rpc-best-practices.mdx index f7da7c10835..cae50a7227b 100644 --- a/docs/content/references/iota-api/rpc-best-practices.mdx +++ b/docs/content/references/iota-api/rpc-best-practices.mdx @@ -2,11 +2,11 @@ title: RPC Best Practices --- -import WarnMlRpcs from "../../_snippets/warn-ml-rpcs.mdx"; +import HighTrafficRpcs from "../../_snippets/info-high-traffic.mdx"; This topic provides some best practices for configuring your RPC settings to ensure a reliable infrastructure for your projects and services built on IOTA. -<WarnMlRpcs /> +<HighTrafficRpcs /> ## RPC provisioning guidance diff --git a/docs/content/references/iota-compared.mdx b/docs/content/references/iota-compared.mdx index 90de98f1d04..0126c647a66 100644 --- a/docs/content/references/iota-compared.mdx +++ b/docs/content/references/iota-compared.mdx @@ -29,16 +29,16 @@ This doesn't mean that IOTA as a platform never orders transactions with respect ## A collaborative approach to transaction submission -IOTA validates transactions individually, rather than batching them in the traditional blocks. The key advantage of this approach is low latency; each successful transaction quickly obtains a certificate of finality that proves to anyone that the transaction will persists its effects on the IOTA network. +IOTA validates transactions individually, rather than batching them in the traditional blocks. The key advantage of this approach is low latency; each successful transaction quickly obtains a _certificate_ of finality that proves to anyone that the transaction will persists its effects on the IOTA network. But the process of submitting a transaction is a bit more involved. That little more work occurs on the network. (With bandwidth getting cheaper, this is less of a concern.) Whereas a usual blockchain can accept a bunch of transactions from the same author in a fire-and-forget mode, IOTA transaction submission follows these steps: 1. Sender broadcasts a transaction to all IOTA validators. 1. IOTA validators send individual votes on this transaction to the sender. 1. Each vote has a certain weight since each validator has weight based upon the rules of [Proof of Stake](https://en.wikipedia.org/wiki/Proof_of_work). -1. Sender collects a Byzantine-resistant-majority of these votes into a _certificate_ and broadcasts it to all IOTA validators. +1. Sender collects a Byzantine-resistant-majority of these votes into a __certificate__ and broadcasts it to all IOTA validators. 1. The validators execute the transaction and sign the results. When the client receives a Byzantine-resistant-majority of the results _finality_ is reached, ie., assurance the transaction will not be dropped (revoked). -1. Optionally, the sender assembles the votes to a certificate detailing the effects of the transaction. +1. Optionally, the sender assembles the votes to a _certificate_ detailing the effects of the transaction. While those steps demand more of the sender, performing them efficiently can still yield a cryptographic proof of finality with minimum latency. Aside from crafting the original transaction itself, the session management for a transaction does not require access to any private keys and can be delegated to a third party. @@ -49,7 +49,7 @@ Because IOTA focuses on managing specific objects rather than a single aggregate - Every object in IOTA has a unique version number. - Every new version is created from a transaction that may involve several dependencies, themselves versioned objects. -As a consequence, a IOTA Validator can exhibit a causal history of an object, showing its history since genesis. IOTA explicitly makes the bet that in many cases, the ordering of that causal history with the causal history of another object is irrelevant; and in the few cases where this information is relevant, IOTA makes this relationship explicit in the data. +As a consequence, a IOTA Validator can exhibit a _causal history_ of an object, showing its history since genesis. IOTA explicitly makes the bet that in many cases, the ordering of that _causal history_ with the _causal history_ of another object is irrelevant; and in the few cases where this information is relevant, IOTA makes this relationship explicit in the data. ## Causal order vs. total order diff --git a/docs/content/references/iota-glossary.mdx b/docs/content/references/iota-glossary.mdx index d6b592a54d5..9593a486845 100644 --- a/docs/content/references/iota-glossary.mdx +++ b/docs/content/references/iota-glossary.mdx @@ -2,96 +2,8 @@ title: Glossary slug: /iota-glossary --- +import Glossary from '@site/src/components/Glossary'; Find terms used in IOTA defined below. -### Causal history - -Causal history is the relationship between an object in IOTA and its direct predecessors and successors. This history is essential to the causal order IOTA uses to process transactions. In contrast, other blockchains read the entire state of their world for each transaction, -introducing latency. - -### Causal order - -[Causal order](https://www.scattered-thoughts.net/writing/causal-ordering/) is a representation of the relationship between transactions and the objects they produce, laid out as dependencies. Validators cannot execute a transaction dependent on objects created by a prior transaction that has not finished. Rather than total order, IOTA uses causal order (a partial order). - -### Certificate - -A certificate is the mechanism proving a transaction was approved or certified. Validators vote on transactions, and aggregators collect a Byzantine-resistant-majority of these votes into a certificate and broadcasts it to all IOTA validators, thereby ensuring finality. - -### Epoch - -Operation of the IOTA network is temporally partitioned into non-overlapping, fixed-duration epochs. During a particular epoch, the set of validators participating in the network is fixed. - -### Equivocation - -Equivocation in blockchains is the malicious action of dishonest actors giving conflicting information for the same message, such as inconsistent or duplicate voting. - -### Eventual consistency - -[Eventual consistency](https://en.wikipedia.org/wiki/Eventual_consistency) is the consensus model employed by IOTA; if one honest validator -certifies the transaction, all of the other honest validators will too eventually. - -### Finality - -[Finality](https://medium.com/mechanism-labs/finality-in-blockchain-consensus-d1f83c120a9a) is the assurance a transaction will not be revoked. This stage is considered closure for an exchange or other blockchain transaction. - -### Gas - -[Gas](https://ethereum.org/en/developers/docs/gas/) refers to the computational effort required for executing operations on the IOTA network. In IOTA, gas is paid with the network's native currency IOTA. The cost of executing a transaction in IOTA units is referred to as the transaction fee. - -### Genesis - -Genesis is the initial act of creating accounts and gas objects for a IOTA network. IOTA provides a `genesis` command that allows users to create and inspect the genesis object setting up the network for operation. - -### Multi-writer objects - -Multi-writer objects are objects that are owned by more than one address. Transactions affecting multi-writer objects require consensus in IOTA. This contrasts with transactions affecting only single-writer objects, which require only a confirmation of the owner's address contents. - -### Object - -The basic unit of storage in IOTA is object. In contrast to many other blockchains, where storage is centered around address and each address contains a key-value store, IOTA's storage is centered around objects. IOTA objects have one of the following primary states: - -- _Immutable_ - the object cannot be modified. -- _Mutable_ - the object can be changed. - -Further, mutable objects are divided into these categories: - -- _Owned_ - the object can be modified only by its owner. -- _Shared_ - the object can be modified by anyone. - -Immutable objects do not need this distinction because they have no owner. - -### Proof-of-stake - -[Proof-of-stake](https://en.wikipedia.org/wiki/Proof_of_stake) is a blockchain consensus mechanism where the voting weights of validators or validators is proportional to a bonded amount of the network's native currency (called their stake in the network). This mitigates [Sybil attacks](https://en.wikipedia.org/wiki/Sybil_attack) by forcing bad actors to gain a large stake in the blockchain first. - -### Single-writer objects - -Single-writer objects are owned by one address. In IOTA, transactions affecting only single-writer objects owned by the same address may proceed with only a verification of the sender's address, greatly speeding transaction times. These are _simple transactions_. See Single-Writer Apps for example applications of this simple transaction model. - -### Smart contract - -A [smart contract](https://en.wikipedia.org/wiki/Smart_contract) is an agreement based upon the protocol for conducting transactions in a blockchain. In IOTA, smart contracts are written in [Solidity/EVM or Move](../developer/evm-to-move/evm-to-move.mdx). - -### IOTA - -IOTA refers to the IOTA blockchain, and the [IOTA open source project](https://github.com/iotaledger/iota/) as a whole, or the native token to the IOTA network. - -### Total order - -[Total order](https://en.wikipedia.org/wiki/Total_order) refers to the ordered presentation of the history of all transactions processed by a traditional blockchain up to a given time. This is maintained by many blockchain systems, as the only way to process transactions. In contrast, IOTA uses a causal (partial) order wherever possible and safe. - -### Transaction - -A transaction in IOTA is a change to the blockchain. This may be a _simple transaction_ affecting only single-writer, single-address objects, such as minting an NFT or transferring an NFT or another token. These transactions may bypass the consensus protocol in IOTA. - -More _complex transactions_ affecting objects that are shared or owned by multiple addresses, such as asset management and other DeFi use cases, do go through consensus. - -### Transfer - -A transfer is switching the owner address of a token to a new one via command in IOTA. This is accomplished via the IOTA CLI client command line interface. It is one of the more common of many commands available in the CLI client. - -### Validator - -A validator in IOTA plays a passive role analogous to the more active role of validators and minors in other blockchains. In IOTA, validators do not continuously participate in the consensus protocol but are called into action only when receiving a transaction or -certificate. +<Glossary/> diff --git a/docs/content/references/references.mdx b/docs/content/references/references.mdx index 03facf3dd25..e0d5d599583 100644 --- a/docs/content/references/references.mdx +++ b/docs/content/references/references.mdx @@ -11,7 +11,7 @@ Reference the IOTA framework and IOTA RPC documentation for details of the code <Cards> -<Card title="JSON-RPC" href="references/iota-api"> +<Card title="JSON-RPC" href="/references/iota-api"> A public service that enables interacting with the IOTA network. </Card> </Cards> @@ -21,7 +21,7 @@ A public service that enables interacting with the IOTA network. Move powers smart contract logic for the IOTA blockchain. Use these resources to learn Move or refresh your memory. <Cards> -<Card title="IOTA framework" href="references/framework"/> +<Card title="IOTA framework" href="/references/framework"/> <Card title="Move language" href="https://github.com/move-language/move/blob/main/language/documentation/book/src/introduction.md"> Documentation for the Move programming language on GitHub. </Card> @@ -32,13 +32,13 @@ Documentation for the Move programming language on GitHub. Interact directly with IOTA networks and its features using the IOTA command line interface (CLI). The CLI is divided into separate base commands that target a specific set of features. <Cards> -<Card title="IOTA Client CLI" href="references/cli/client"> +<Card title="IOTA Client CLI" href="/references/cli/client"> Create a client on a IOTA network to generate addresses, access networks, and more with the IOTA Client CLI. </Card> -<Card title="IOTA Client PTB CLI" href="references/cli/ptb"> +<Card title="IOTA Client PTB CLI" href="/references/cli/ptb"> Build, preview, and execute programmable transaction blocks directly from your terminal with the IOTA Client PTB CLI. </Card> -<Card title="IOTA Move CLI" href="references/cli/move"> +<Card title="IOTA Move CLI" href="/references/cli/move"> Access IOTA Move functions on chain using the IOTA Move CLI. </Card> </Cards> @@ -51,5 +51,5 @@ Official software development kits (SDKs) available for IOTA include the TypeScr <Card title="IOTA TypeScript SDK" href="ts-sdk/typescript"> The IOTA TypeScript SDK has its own microsite. Click this box to go there. </Card> -<Card title="IOTA Rust SDK" href="references/rust-sdk"/> +<Card title="IOTA Rust SDK" href="/references/rust-sdk"/> </Cards> diff --git a/docs/content/references/research-papers.mdx b/docs/content/references/research-papers.mdx index 49b064fd219..53ca90d660e 100644 --- a/docs/content/references/research-papers.mdx +++ b/docs/content/references/research-papers.mdx @@ -42,7 +42,7 @@ This document contains a list of research papers that are relevant to IOTA. to add strong privacy to FastPay transactions (but IOTA does not plan to do this). - **Summary:** We introduce Zef, the first Byzantine-Fault Tolerant (BFT) protocol to support payments in anonymous digital coins at arbitrary scale. Zef follows the communication and security model of FastPay: both protocols are asynchronous, low-latency, linearly-scalable, and powered by partially-trusted - sharded validators. Zef further introduces opaque coins represented as off-chain certificates that are bound to user accounts. In order to hide the face + sharded validators. Zef further introduces opaque coins represented as off-chain _certificate_s that are bound to user accounts. In order to hide the face values of coins when a payment operation consumes or creates them, Zef uses random commitments and NIZK proofs. Created coins are made unlinkable using the blind and randomizable threshold anonymous credentials of [Coconut](https://arxiv.org/pdf/1802.07344.pdf). To control storage costs associated with coin replay prevention, Zef accounts are designed so that data can be safely removed once an account is deactivated. Besides the specifications and a detailed diff --git a/docs/content/sidebars/developer.js b/docs/content/sidebars/developer.js index 46e45534194..8cadb600f0c 100644 --- a/docs/content/sidebars/developer.js +++ b/docs/content/sidebars/developer.js @@ -57,7 +57,6 @@ const developer = [ label: 'Typescript SDK', href: '/ts-sdk/typescript', }, - 'developer/iota-101/using-events', ], }, { @@ -70,6 +69,8 @@ const developer = [ 'developer/iota-101/move-overview/init', 'developer/iota-101/move-overview/visibility', 'developer/iota-101/move-overview/entry-functions', + 'developer/iota-101/using-events', + 'developer/iota-101/access-time', { type: 'category', label: 'Structs and Abilities', @@ -107,7 +108,6 @@ const developer = [ 'developer/iota-101/move-overview/patterns/id-pointer', ], }, - 'developer/iota-101/access-time', 'developer/iota-101/move-overview/conventions', ], }, diff --git a/docs/content/ts-sdk/typescript/index.mdx b/docs/content/ts-sdk/typescript/index.mdx index f155c2b3822..f1bc88e8908 100644 --- a/docs/content/ts-sdk/typescript/index.mdx +++ b/docs/content/ts-sdk/typescript/index.mdx @@ -1,5 +1,6 @@ import NetworkInfo from "@site/src/components/NetworkInfo/index.tsx"; import { Networks } from '@site/src/components/constant.tsx'; +import HighTrafficRpcs from "@site/../content/_snippets/info-high-traffic.mdx"; # IOTA TypeScript SDK Quick Start @@ -16,6 +17,9 @@ npm i @iota/iota-sdk The following table lists the locations for IOTA networks. +{ /* TODO: https://github.com/iotaledger/iota/issues/4497 */ } +<HighTrafficRpcs /> + ## IOTA Testnet <NetworkInfo.Move {...Networks['iota_move_testnet']}/> @@ -30,19 +34,6 @@ To create a local IOTA network, you can refer to [Local Development](/developer/ <NetworkInfo.Move {...Networks['iota_localnet']}/> -:::warning - -Use dedicated nodes/shared services rather than public endpoints for production apps. The public -endpoints maintained by the IOTA Foundation (`api.<NETWORK>.iota.cafe:443`) are rate-limited, and support -only 100 requests per 30 seconds or so. Do not use public endpoints in production applications with -high traffic volume. - -You can either run your own Full nodes, or outsource this to a professional infrastructure provider -(preferred for apps that have high traffic). You can find a list of reliable RPC endpoint providers -for IOTA on the [IOTA Dev Portal](https://iota.io/developers#dev-tools) using the **Node Service** tab. - -::: - ## Migrate to version 0.38.0 The IOTA TypeScript SDK was refactored beginning with version 0.38.0. If you are updating from an diff --git a/docs/site/config/jargon.js b/docs/site/config/jargon.js new file mode 100644 index 00000000000..669bc6d8cff --- /dev/null +++ b/docs/site/config/jargon.js @@ -0,0 +1,35 @@ +module.exports = { + 'causal history': 'Causal history is the relationship between an object in IOTA and its direct predecessors and successors. This history is essential to the causal order IOTA uses to process transactions. In contrast, other blockchains read the entire state of their world for each transaction, introducing latency.', + 'causal order': '<a href="https://www.scattered-thoughts.net/writing/causal-ordering/">Causal order</a> is a representation of the relationship between transactions and the objects they produce, laid out as dependencies. Validators cannot execute a transaction dependent on objects created by a prior transaction that has not finished. Rather than total order, IOTA uses causal order (a partial order).', + certificate: 'A certificate is the mechanism proving a transaction was approved or certified. Validators vote on transactions, and aggregators collect a Byzantine-resistant majority of these votes into a certificate and broadcasts it to all IOTA validators, thereby ensuring finality.', + epoch: 'Operation of the IOTA network is temporally partitioned into non-overlapping, fixed-duration epochs. During a particular epoch, the set of validators participating in the network is fixed.', + equivocation: 'Equivocation in blockchains is the malicious action of dishonest actors giving conflicting information for the same message, such as inconsistent or duplicate voting.', + 'eventual consistency': '<a href="https://en.wikipedia.org/wiki/Eventual_consistency">Eventual consistency</a> is the consensus model employed by IOTA; if one honest validator certifies the transaction, all of the other honest validators will too eventually.', + finality: '<a href="https://medium.com/mechanism-labs/finality-in-blockchain-consensus-d1f83c120a9a">Finality</a> is the assurance a transaction will not be revoked. This stage is considered closure for an exchange or other blockchain transaction.', + gas: '<a href="https://ethereum.org/en/developers/docs/gas/">Gas</a> refers to the computational effort required for executing operations on the IOTA network. In IOTA, gas is paid with the network\'s native currency IOTA. The cost of executing a transaction in IOTA units is referred to as the transaction fee.', + genesis: 'Genesis is the initial act of creating accounts and gas objects for a IOTA network. IOTA provides a `genesis` command that allows users to create and inspect the genesis object setting up the network for operation.', + 'multi-writer objects': 'Multi-writer objects are objects that are owned by more than one address. Transactions affecting multi-writer objects require consensus in IOTA. This contrasts with transactions affecting only single-writer objects, which require only a confirmation of the owner\'s address contents.', + object: ` + The basic unit of storage in IOTA is object. In contrast to many other blockchains, where storage is centered around address and each address contains a key-value store, IOTA\'s storage is centered around objects. IOTA objects have one of the following primary states:<br/> + <br/> + - <i>Immutable</i> - the object cannot be modified.<br/> + - <i>Mutable</i> - the object can be changed.<br/> + <br/> + Further, mutable objects are divided into these categories:<br/> + <br/> + - <i>Owned</i> - the object can be modified only by its owner.<br/> + - <i>Shared</i> - the object can be modified by anyone.<br/> + <br/> + Immutable objects do not need this distinction because they have no owner. + `, + PoS: '<a href="https://en.wikipedia.org/wiki/Proof_of_stake">Proof-of-stake</a> is a blockchain consensus mechanism where the voting weights of validators or validators is proportional to a bonded amount of the network\'s native currency (called their stake in the network). This mitigates <a href="https://en.wikipedia.org/wiki/Sybil_attack">Sybil attacks</a> by forcing bad actors to gain a large stake in the blockchain first.', + 'single-writer objects': 'Single-writer objects are owned by one address. In IOTA, transactions affecting only single-writer objects owned by the same address may proceed with only a verification of the sender\'s address, greatly speeding transaction times. These are simple transactions. See Single-Writer Apps for example applications of this simple transaction model.', + 'smart contract': 'A <a href="https://en.wikipedia.org/wiki/Smart_contract">smart contract</a> is an agreement based upon the protocol for conducting transactions in a blockchain. In IOTA, smart contracts are written in <a href="../developer/evm-to-move">Solidity/EVM or Move</a>.', + iota: 'IOTA refers to the IOTA blockchain, and the <a href="https://github.com/iotaledger/iota/">IOTA open source project</a> as a whole, or the native token to the IOTA network.', + 'total order': '<a href="https://en.wikipedia.org/wiki/Total_order">Total order</a> refers to the ordered presentation of the history of all transactions processed by a traditional blockchain up to a given time. This is maintained by many blockchain systems, as the only way to process transactions. In contrast, IOTA uses a causal (partial) order wherever possible and safe.', + transaction: ` + A transaction in IOTA is a change to the blockchain. This may be a <i>simple transaction</i> affecting only single-writer, single-address objects, such as minting an NFT or transferring an NFT or another token. These transactions may bypass the consensus protocol in IOTA.<br/> + More <i>complex transactions</i> affecting objects that are shared or owned by multiple addresses, such as asset management and other DeFi use cases, go through the<a href = "https://github.com/iotaledger/iota/tree/develop/narwhal">Narwhal and Bullshark</a> DAG - based mempool and efficient Byzantine Fault Tolerant(BFT) consensus. + `, + transfer: 'A transfer is switching the owner address of a token to a new one via command in IOTA. This is accomplished via the IOTA CLI client command line interface. It is one of the more common of many commands available in the CLI client.', + validator: 'A validator in IOTA plays a passive role analogous to the more active role of validators and minors in other blockchains. In IOTA, validators do not continuously participate in the consensus protocol but are called into action only when receiving a transaction or certificate.'}; diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index f4034a646cf..b7a2e478b65 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -10,6 +10,8 @@ import codeImport from "remark-code-import"; require("dotenv").config(); +const jargonConfig = require('./config/jargon.js'); + /** @type {import('@docusaurus/types').Config} */ const config = { title: "IOTA Documentation", @@ -114,197 +116,9 @@ const config = { createRedirects(existingPath) { const redirects = [ { - to: 'ts-sdk/typescript/install', - from: '/references/ts-sdk/typescript/install', - }, - { - to: 'ts-sdk/typescript/hello-iota', - from: '/references/ts-sdk/typescript/hello-iota', - }, - { - to: 'ts-sdk/typescript/faucet', - from: '/references/ts-sdk/typescript/faucet', - }, - { - to: 'ts-sdk/typescript/iota-client', - from: '/references/ts-sdk/typescript/iota-client', - }, - { - to: 'ts-sdk/typescript/graphql', - from: '/references/ts-sdk/typescript/graphql', - }, - { - to: 'ts-sdk/typescript/transaction-building/basics', - from: '/references/ts-sdk/typescript/transaction-building/basics', - }, - { - to: 'ts-sdk/typescript/transaction-building/gas', - from: '/references/ts-sdk/typescript/transaction-building/gas', - }, - { - to: 'ts-sdk/typescript/transaction-building/sponsored-transactions', - from: '/references/ts-sdk/typescript/transaction-building/sponsored-transactions', - }, - { - to: 'ts-sdk/typescript/transaction-building/offline', - from: '/references/ts-sdk/typescript/transaction-building/offline', - }, - { - to: 'ts-sdk/typescript/cryptography/keypairs', - from: '/references/ts-sdk/typescript/cryptography/keypairs', - }, - { - to: 'ts-sdk/typescript/cryptography/multisig', - from: '/references/ts-sdk/typescript/cryptography/multisig', - }, - { - to: 'ts-sdk/typescript/utils', - from: '/references/ts-sdk/typescript/utils', - }, - { - to: 'ts-sdk/typescript/bcs', - from: '/references/ts-sdk/typescript/bcs', - }, - { - to: 'ts-sdk/typescript/executors', - from: '/references/ts-sdk/typescript/executors', - }, - { - to: 'ts-sdk/typescript/plugins', - from: '/references/ts-sdk/typescript/plugins', - }, - { - to: 'ts-sdk/typescript/owned-object-pool/overview', - from: '/references/ts-sdk/typescript/owned-object-pool/overview', - }, - { - to: 'ts-sdk/typescript/owned-object-pool/local-development', - from: '/references/ts-sdk/typescript/owned-object-pool/local-development', - }, - { - to: 'ts-sdk/typescript/owned-object-pool/custom-split-strategy', - from: '/references/ts-sdk/typescript/owned-object-pool/custom-split-strategy', - }, - { - to: 'ts-sdk/typescript/owned-object-pool/examples', - from: '/references/ts-sdk/typescript/owned-object-pool/examples', - }, - { - to: 'ts-sdk/dapp-kit/create-dapp', - from: '/references/ts-sdk/dapp-kit/create-dapp', - }, - { - to: 'ts-sdk/dapp-kit/iota-client-provider', - from: '/references/ts-sdk/dapp-kit/iota-client-provider', - }, - { - to: 'ts-sdk/dapp-kit/rpc-hooks', - from: '/references/ts-sdk/dapp-kit/rpc-hooks', - }, - { - to: 'ts-sdk/dapp-kit/wallet-provider', - from: '/references/ts-sdk/dapp-kit/wallet-provider', - }, - { - to: 'ts-sdk/dapp-kit/wallet-components/ConnectButton', - from: '/references/ts-sdk/dapp-kit/wallet-components/ConnectButton', - }, - { - to: 'ts-sdk/dapp-kit/wallet-components/ConnectModal', - from: '/references/ts-sdk/dapp-kit/wallet-components/ConnectModal', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useWallets', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useWallets', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useAccounts', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useAccounts', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useCurrentWallet', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentWallet', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useCurrentAccount', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useCurrentAccount', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useAutoConnectWallet', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useAutoConnectWallet', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useConnectWallet', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useConnectWallet', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useDisconnectWallet', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useDisconnectWallet', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useSwitchAccount', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useSwitchAccount', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useReportTransactionEffects', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useReportTransactionEffects', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useSignPersonalMessage', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useSignPersonalMessage', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useSignTransaction', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useSignTransaction', - }, - { - to: 'ts-sdk/dapp-kit/wallet-hooks/useSignAndExecuteTransaction', - from: '/references/ts-sdk/dapp-kit/wallet-hooks/useSignAndExecuteTransaction', - }, - { - to: 'ts-sdk/dapp-kit/themes', - from: '/references/ts-sdk/dapp-kit/themes', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/introduction', - from: '/references/ts-sdk/kiosk/kiosk-client/introduction', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/querying', - from: '/references/ts-sdk/kiosk/kiosk-client/querying', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/kiosk-transaction/kiosk-transaction', - from: '/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/kiosk-transaction', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/kiosk-transaction/managing', - from: '/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/managing', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/kiosk-transaction/purchasing', - from: '/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/purchasing', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/kiosk-transaction/examples', - from: '/references/ts-sdk/kiosk/kiosk-client/kiosk-transaction/examples', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/transfer-policy-transaction/introduction', - from: '/references/ts-sdk/kiosk/kiosk-client/transfer-policy-transaction/introduction', - }, - { - to: 'ts-sdk/kiosk/kiosk-client/transfer-policy-transaction/using-the-manager', - from: '/references/ts-sdk/kiosk/kiosk-client/transfer-policy-transaction/using-the-manager', - }, - { - to: 'ts-sdk/kiosk/advanced-examples', - from: '/references/ts-sdk/kiosk/advanced-examples', - }, - { - to: 'ts-sdk/bcs', - from: '/references/ts-sdk/bcs', - }, + from: '/references/ts-sdk', + to: '/ts-sdk', + }, ]; let paths = []; for (const redirect of redirects) { @@ -366,7 +180,10 @@ const config = { ], [codeImport, { rootDir: path.resolve(__dirname, `../../`) }], ], - rehypePlugins: [katex], + rehypePlugins: [ + katex, + [require('rehype-jargon'), { jargon: jargonConfig}] + ], }, theme: { customCss: [ @@ -394,29 +211,15 @@ const config = { type: "text/css", }, ], - themes: ["@docusaurus/theme-live-codeblock", "@docusaurus/theme-mermaid", 'docusaurus-theme-search-typesense', - '@saucelabs/theme-github-codeblock'], + themes: ["@docusaurus/theme-mermaid", + '@saucelabs/theme-github-codeblock', '@docusaurus/theme-live-codeblock'], themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ - typesense: { - // Replace this with the name of your index/collection. - // It should match the "index_name" entry in the scraper's "config.json" file. - typesenseCollectionName: 'IOTADocs', - typesenseServerConfig: { - nodes: [ - { - host: 'docs-search.iota.org', - port: '', - protocol: 'https', - }, - ], - apiKey: 'C!jA3iCujG*PjK!eUVWFBxnU', - }, - // Optional: Typesense search parameters: https://typesense.org/docs/0.24.0/api/search.html#search-parameters - typesenseSearchParameters: {}, - // Optional - contextualSearch: true, + algolia: { + apiKey: '24b141ea7e65db2181463e44dbe564a5', + appId: '9PMBZGRP3B', + indexName: 'iota', }, image: "img/iota-doc-og.png", docs: { diff --git a/docs/site/package.json b/docs/site/package.json index 6e0e328cdb4..410b2dd3bbf 100644 --- a/docs/site/package.json +++ b/docs/site/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@amplitude/analytics-browser": "^1.10.3", + "@artsy/to-title-case": "^1.1.0", "@docsearch/react": "^3.6.0", "@docusaurus/core": "3.5.2", "@docusaurus/plugin-client-redirects": "^3.5.2", @@ -43,12 +44,12 @@ "axios": "^1.7.4", "clsx": "^2.1.1", "copy-text-to-clipboard": "^3.2.0", - "docusaurus-theme-search-typesense": "0.20.0-0", "dotenv": "^16.4.5", "graphql": "^16.9.0", "graphql-config": "^5.0.3", "gray-matter": "^4.0.3", "hast-util-is-element": "^1.1.0", + "html-react-parser": "^5.1.18", "lodash": "^4.17.21", "markdown-to-jsx": "^7.4.7", "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", @@ -60,6 +61,7 @@ "react-scrollspy-navigation": "^1.0.3", "react-syntax-highlighter": "^15.5.0", "react-ui-scrollspy": "^2.3.0", + "rehype-jargon": "^3.1.0", "rehype-katex": "^7.0.0", "remark-math": "^6.0.0", "tailwindcss": "^3.3.3", diff --git a/docs/site/src/components/API/api-ref/refnav.js b/docs/site/src/components/API/api-ref/refnav.js index e88b41d0233..f594067ece1 100644 --- a/docs/site/src/components/API/api-ref/refnav.js +++ b/docs/site/src/components/API/api-ref/refnav.js @@ -44,7 +44,6 @@ const RefNav = (props) => { className={`menu__link font-medium block hover:no-underline text-base ${page === method.name && 'menu__link--active'}`} key={`link-${method.name.toLowerCase()}`} href={`#${method.name.toLowerCase()}`} - data-theme="dark" onClick={() => setPage(method.name)} > {method.name} diff --git a/docs/site/src/components/FeedbackForm/index.tsx b/docs/site/src/components/FeedbackForm/index.tsx index 8613ffdf7a3..5dcf5af6d40 100644 --- a/docs/site/src/components/FeedbackForm/index.tsx +++ b/docs/site/src/components/FeedbackForm/index.tsx @@ -20,7 +20,6 @@ const FeedbackForm = () => { return ( <div className="feedback-container"> - <div className="divider"></div> <div className={clsx("h3", "feedback-header")}>Feedback Form</div> <form onSubmit={handleSubmit}> <div className="form-group"> @@ -54,7 +53,6 @@ const FeedbackForm = () => { Submit Feedback </button> </form> - <div className="divider"></div> </div> ); }; diff --git a/docs/site/src/components/FeedbackForm/styles.css b/docs/site/src/components/FeedbackForm/styles.css index 4e742a9afb3..4aafcee2f8e 100644 --- a/docs/site/src/components/FeedbackForm/styles.css +++ b/docs/site/src/components/FeedbackForm/styles.css @@ -1,5 +1,15 @@ .feedback-container { - /* margin: 20px; */ + background-color: #0000001a; + padding-top: 0.25rem; + padding-left: 2rem; + padding-right: 2rem; + padding-bottom: 1.25rem; + margin-top: 1rem; + border-radius: 0.5rem; +} + +[data-theme="dark"] .feedback-container { + background-color: #1e1e1e; } .feedback-header { diff --git a/docs/site/src/components/Glossary/index.tsx b/docs/site/src/components/Glossary/index.tsx new file mode 100644 index 00000000000..1a495afe0a3 --- /dev/null +++ b/docs/site/src/components/Glossary/index.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import Heading from '@theme/Heading'; +import { toTitleCase } from '@artsy/to-title-case'; +import { clsx } from 'clsx'; +import parse from 'html-react-parser'; + +export default function Glossary() { + const glossary = require('@site/config/jargon.js'); + + const sortedGlossary = Object.keys(glossary) + .sort(function (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }) + .reduce((acc, key) => { + acc[key] = glossary[key]; + return acc; + }, {}); + + let char = ''; + return ( + <> + {Object.entries(sortedGlossary).map(([key, value]) => { + let heading = null; + if (key.charAt(0).toLowerCase() !== char.toLowerCase()) { + char = key.charAt(0); + heading = char; + } + + return ( + <> + {heading && ( + <Heading + as='h2' + id={char} + title={char} + > + {char.toUpperCase()} + </Heading> + )} + <Heading + as='h3' + id={key} + title={key} + > + {toTitleCase(key)} + </Heading> + <p>{parse(value)}</p> + </> + ); + })} + </> + ); +} \ No newline at end of file diff --git a/docs/site/src/css/custom.css b/docs/site/src/css/custom.css index 59c5136435d..eac62e74432 100644 --- a/docs/site/src/css/custom.css +++ b/docs/site/src/css/custom.css @@ -29,6 +29,7 @@ --ifm-font-family-base: "Inter", sans-serif; --ifm-menu-color: #011829; --ifm-menu-color-background-active: transparent; + --ifm-hover-overlay: var(--ifm-color-emphasis-100); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); --iota-black: #111111; --iota-blue: #6fbcf0; @@ -89,7 +90,7 @@ } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme="dark"] { +html[data-theme="dark"] { --ifm-color-primary: #4ca2ff; --ifm-color-primary-dark: #2b91ff; --ifm-color-primary-darker: #1a88ff; @@ -102,6 +103,7 @@ --ifm-menu-color-background-active: transparent; --ifm-navbar-sidebar-link-color: #f7f7f8; --ifm-navbar-sidebar-background-color: black; + --ifm-hover-overlay: var(--ifm-color-emphasis-100); } .active-scroll-spy { @@ -112,16 +114,20 @@ [data-theme="dark"] img.balance-coin-token { filter: invert(1); } + .table-of-contents__link--active { color: #525860; font-weight: 600; } +/* Hide ToC from imported READMEs */ .hidden-text{ display: none; } + [data-theme="dark"] .table-of-contents__link--active { color: white; } + /** navbar overrides */ [data-theme="light"] .clean-btn:hover { background-color: #525860; @@ -129,14 +135,14 @@ [data-theme="dark"] .menu__link--active { font-weight: bold; - color: white; + color: #3b82f6; } @media (min-width: 997px) { [data-theme="light"] .menu__link:hover, [data-theme="light"] .menu__link--active{ font-weight: bold; - color: var(--ifm-menu-color) + color: #3b82f6 } } @media (max-width: 996px) { @@ -148,7 +154,7 @@ [data-theme="light"] .menu__list-item-collapsible--active, [data-theme="light"] .menu__link--active { font-weight: bold; - color: white; + color: #3b82f6; } [data-theme="light"] .menu__list-item-collapsible:hover, @@ -172,17 +178,21 @@ color: white; font-weight: 500; } + .pagination-nav__label--blue, .pagination-nav__sublabel--blue { color: white; } + .pagination-nav { display: flex; justify-content: space-between; } + [data-theme="light"] .pagination-nav__link { border: 1px solid #d8e1f4; } + .pagination-nav__link { background-color: transparent; border-radius: 0px; @@ -193,6 +203,7 @@ width: fit-content; min-width: 240px; } + .pagination-nav__label { color: white; } @@ -204,6 +215,7 @@ flex-direction: column; justify-content: space-between !important; } + .page-nav-content-light { display: flex; align-items: center; @@ -212,6 +224,7 @@ color: white !important; flex-direction: row-reverse; } + .page-nav-content-blue { display: flex; align-items: center; @@ -219,31 +232,38 @@ width: 100%; color: white !important; } + .page-nav-arrow--light, .pagination-nav__sublabel--light { color: white; } + .pagination-nav__label--light { color: black; } + [data-theme="dark"] .page-nav-arrow--light, .pagination-nav__sublabel--light, .pagination-nav__label--light { color: white; } + [data-theme="light"] .page-nav-arrow--light, .pagination-nav__label--light { color: black; } + [data-theme="light"] .pagination-nav__sublabel--light { color: #909fbe; } + [data-theme="dark"] .pagination-nav__label--light { color: white; } html[data-theme="dark"] { background-color: var(--iota-black); } + /** setup global style overrides */ body { font-family: var(--primaryFont); @@ -256,6 +276,7 @@ h1 { font-weight: 500; letter-spacing: -0.04em; } + .h1 { letter-spacing: -2.88px; margin-top: 0; @@ -273,6 +294,7 @@ h2 { line-height: 2.125rem; letter-spacing: -0.04em; } + .h2 { letter-spacing: -1.62px; margin-top: 0; @@ -316,24 +338,30 @@ h4 { line-height: 1.75rem; letter-spacing: -2%; } + @media screen and (max-width: 767px) { + h1, .h1 { font-size: 3.5rem; } + h2, .h2 { font-size: 2rem; } + h3, .h3 { font-size: 1.5rem; } + h4, .h4 { font-size: 1.2rem; } } + @media screen and (max-width: 599px) { .pagination-nav{ display: flex; @@ -350,10 +378,12 @@ h4 { .h2 { font-size: 1.7rem; } + h3, .h3 { font-size: 1.2rem; } + h4, .h4 { font-size: 1rem; @@ -365,19 +395,24 @@ h4 { border-bottom: 1px solid var(--iota-line); background-color: black; } + .navbar__title { width: 10px; } + .navbar__toggle path { stroke: var(--iota-white); } + .navbar__items { color: rgba(255, 255, 255, 0.7); } + .navbar__items .navbar__brand { width: 17%; color: rgba(255, 255, 255, 0.7); } + .navbar__items .navbar__item.navbar__link { /* font-family: var(--headerFont); */ font-size: 0.9375rem; @@ -408,10 +443,12 @@ h4 { [data-theme='light'] .DocSearch { --docsearch-searchbox-background: var(--ifm-background-color-dark); } + .DocSearch-Button-Container { width: 100%; justify-content: space-between; } + .DocSearch-Button { width: 12.5rem; height: 2.625rem !important; @@ -423,18 +460,22 @@ h4 { font-family: var(--primaryFont); letter-spacing: -0.01em; } + @media screen and (max-width: 599px) { .DocSearch-Button { width: initial; } } + .DocSearch-Search-Icon { order: 2; margin-left: auto; } + .DocSearch-Search-Icon path { stroke: var(--iota-white); } + .DocSearch-Button-Keys { display: none !important; } @@ -443,6 +484,7 @@ h4 { row-gap: 1rem; column-gap: 1rem; } + .markdown h1:first-child { margin-top: 1rem; } @@ -453,12 +495,15 @@ h4 { color: var(--iota-white); background: var(--iota-hero-dark); } -.button--success{ + +.button--success { background: var(--iota-success-dark); } -.button--danger{ + +.button--danger { background: var(--iota-issue-dark); } + .button-cta { letter-spacing: -0.3px; cursor: pointer; @@ -480,22 +525,26 @@ h4 { justify-content: center; gap: 1rem; } + .button-cta:hover { background-color: var(--iota-button-hover); color: var(--iota-white); text-decoration: none; } + .button-cta:after { content: url("data:image/svg+xml,%3Csvg width='18' height='19' viewBox='0 0 18 19' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 5.5H14V14.5' stroke='%23ABBDCC' stroke-width='2'/%3E%3Cpath d='M14 5.5L3 16.5' stroke='%23ABBDCC' stroke-width='2'/%3E%3C/svg%3E"); width: 18px; height: 18px; padding-bottom: 2px; } + @media (max-width: 1050px) { .navbar .button-cta { display: none; } } + .footer { background-color: black; } @@ -504,10 +553,57 @@ h4 { .text-white { color: var(--iota-white); } + .text-gray { color: var(--iota-gray); } +/*------------Jargon---------------*/ +/* Add a dashed line under jargon terms */ +.jargon-term { + text-decoration: underline var(--ifm-color-primary-light); +} + +/* Add a question mark behind/above jargon terms */ +.jargon-term::after { + content: '?'; + font-weight: bold; + display: inline-block; + transform: translate(0, -0.5em); + font-size: 75%; + color: var(--ifm-color-primary-light); + margin-left: 3px; +} + +/* Hover behavior for the therm itself */ +.jargon-term:hover { + position: relative; + text-decoration: none; + cursor: help; +} + +/* Hide info by default */ +.jargon-term .jargon-info { + display: none; +} + +/* Show info on hover */ +.jargon-term:hover .jargon-info { + display: block; + position: absolute; + top: 1.5em; + left: 0; + background: var(--ifm-hover-overlay); + border: 1px solid var(--ifm-color-primary-light); + padding: 1rem; + border-radius: 4px; + font-size: 90%; + min-width: 250px; + max-width: 450px; + z-index: 1; +} +/*---------------------------------*/ + /* Ensure equations are scrollable on smaller screens */ .katex-display { overflow-x: scroll; @@ -526,4 +622,4 @@ h4 { .katex-display { max-width: 100%; overflow-wrap: break-word; -} \ No newline at end of file +} diff --git a/docs/site/src/theme/CodeBlock/index.tsx b/docs/site/src/theme/CodeBlock/index.tsx new file mode 100644 index 00000000000..11c54f01d83 --- /dev/null +++ b/docs/site/src/theme/CodeBlock/index.tsx @@ -0,0 +1,29 @@ +/** + * SWIZZLED VERSION: 3.5.2 + * REASONS: + * - Add check for reference CodeBlock so it can coexist with the original CodeBlock and the live code block + */ +import React from 'react'; +import CodeBlock from '@theme-original/CodeBlock'; +import ReferenceCodeBlock from '@saucelabs/theme-github-codeblock/build/theme/ReferenceCodeBlock'; +import { ReferenceCodeBlockProps } from '@saucelabs/theme-github-codeblock/build/theme/types'; +import type CodeBlockType from '@theme/CodeBlock'; +import type {WrapperProps} from '@docusaurus/types'; + +type Props = WrapperProps<typeof CodeBlockType>; + +function isReferenceCodeBlockType(props: object): props is ReferenceCodeBlockProps { + return 'reference' in props + || ('metastring' in props && typeof props.metastring === 'string' && props.metastring.split(' ').includes('reference')); +} + +// Wrap CodeBlock to check if it is a reference (saucepans) CodeBlock. +// If it isn't, we just return the live plugin CodeBlock which will check, +// if the CodeBlock is a live CodeBlock or the original CodeBlock +export default function CodeBlockWrapper(props: ReferenceCodeBlockProps | Props): JSX.Element { + return ( + <> + {isReferenceCodeBlockType(props) ? <ReferenceCodeBlock {...props} /> : <CodeBlock {...props} />} + </> + ); +} diff --git a/docs/site/static/img/concepts/execution-architecture/dark/iota-architecture.svg b/docs/site/static/img/concepts/execution-architecture/dark/iota-architecture.svg index 7b929d828ab..ab6607556e2 100644 --- a/docs/site/static/img/concepts/execution-architecture/dark/iota-architecture.svg +++ b/docs/site/static/img/concepts/execution-architecture/dark/iota-architecture.svg @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- Do not edit this file with editors other than draw.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1157px" height="747px" viewBox="-0.5 -0.5 1157 747" content="<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.17 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.17"> <diagram name="Page-1" id="jhOoJUkd9Zucpb-pnEYY"> <mxGraphModel dx="1623" dy="1938" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="none" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="3kIInuhs_YmBb0iZ2eFI-7" value="&lt;b&gt;Execution layer&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=west;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;strokeWidth=1;shadow=1;" parent="1" vertex="1"> <mxGeometry x="495" y="25" width="440" height="450" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-63" value="" style="rounded=1;whiteSpace=wrap;html=1;hachureGap=4;fontFamily=Helvetica;strokeColor=default;fillColor=default;glass=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="10.003376623376589" y="39.550041395623865" width="393.8931818181818" height="227.41573033707866" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-8" value="&lt;b&gt;Adapter&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry width="170" height="190" relative="1" as="geometry"> <mxPoint x="30" y="55" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-43" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="38.86363636363636" y="304.0449438202247" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-9" value="&lt;b&gt;MoveVM&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-41" value="move-execution/move-vm-runtime" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-44" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="296.59090909090907" y="306.51685393258424" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-13" value="&lt;b&gt;Framework&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-42" value="iota-framework crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-56" value="Init" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=45;entryPerimeter=0;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-39" target="3kIInuhs_YmBb0iZ2eFI-9" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-58" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="46.16883116883113" y="88.98824364281488" width="378.4090909090909" height="168.0904080425784" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-40" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1" connectable="0"> <mxGeometry width="128.57142857142858" height="138.42696629213484" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-10" value="&lt;b&gt;Execution-engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-40" vertex="1"> <mxGeometry width="128.57142857142856" height="92.63157894736842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-39" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-adapter/execution-engine" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1"> <mxGeometry x="5.849999999999999" y="92.6274157303371" width="109.575" height="45.79955056179776" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-61" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=45;exitDy=100;exitPerimeter=0;entryX=1;entryY=0.206;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-13" target="3kIInuhs_YmBb0iZ2eFI-8" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-62" value="&lt;h4&gt;&lt;font face=&quot;Helvetica&quot;&gt;native fns etc&lt;/font&gt;&lt;/h4&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-61" vertex="1" connectable="0"> <mxGeometry x="-0.1741" y="-1" relative="1" as="geometry"> <mxPoint as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-45" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="276.1363636363636" y="98.87640449438204" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-48" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-45" vertex="1" connectable="0"> <mxGeometry width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-46" value="&lt;b&gt;Object runtime&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-47" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-move-natives" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-64" value="&lt;span style=&quot;caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;&quot;&gt;&lt;b style=&quot;&quot;&gt;iota-execution crate&lt;/b&gt;&lt;/span&gt;" style="text;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;fillColor=default;fontColor=default;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="268.5363636363636" y="49.43820224719101" width="117.47045454545454" height="29.662921348314605" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-15" value="&lt;b&gt;Nodes network(Iota node)&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=south;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;shadow=1;strokeWidth=1;" parent="1" vertex="1"> <mxGeometry x="-210" y="30" width="380" height="430" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-16" value="&lt;b&gt;Node&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry width="330" height="370" relative="1" as="geometry"> <mxPoint x="20" y="40" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-17" value="&lt;b&gt;API&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="42.2239247311828" y="92.97297297297297" width="85.80645161290323" height="267.2972972972973" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-18" value="&lt;b&gt;Ledger storage&lt;/b&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="253.33129032258068" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-65" value="&lt;b&gt;Indexer&lt;/b&gt;&lt;div&gt;&lt;b&gt;(read/sync db)&lt;/b&gt;&lt;/div&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="153.22580645161293" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-19" value="&lt;b&gt;Consensus engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="142.5" y="81.91" width="97.5" height="78.09" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-20" value="&lt;b&gt;Network&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="141.25" y="185.91" width="97.5" height="81.43" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-1" value="&lt;b&gt;Core&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="251.77" y="150" width="75.11" height="60" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-34" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="500" width="120" height="74.55" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-31" value="&lt;b&gt;iota-json-rpc&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry width="120" height="50" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-32" value="&lt;div&gt;iota-json-rpc crate&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry x="20" y="50.00454545454545" width="80" height="24.545454545454547" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-36" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="670" width="100" height="70" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-28" value="&lt;b&gt;iota-cli&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry width="100" height="40" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-35" value="iota-cli crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry y="40" width="90" height="30" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-49" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=45;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;curved=0;strokeWidth=1;shadow=1;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-28" target="3kIInuhs_YmBb0iZ2eFI-32" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-50" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=55;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=1;shadow=1;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-31" target="3kIInuhs_YmBb0iZ2eFI-65" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;strokeWidth=1;shadow=1;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-7" target="3kIInuhs_YmBb0iZ2eFI-18" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-11" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=32.385;exitDy=0;exitPerimeter=0;entryX=0.407;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;strokeWidth=1;shadow=1;" parent="1" source="C7P2nbfdh_euxFz_vEXo-1" target="3kIInuhs_YmBb0iZ2eFI-7" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> "><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-7"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 710 31 L 1140 31 L 1150 41 L 1150 481 L 720 481 L 710 471 L 710 31 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(180,930,256)" pointer-events="all"/><path d="M 720 481 L 720 41 L 710 31 M 720 41 L 1150 41" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(180,930,256)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 423px; height: 1px; padding-top: 38px; margin-left: 717px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution layer</b></div></div></div></foreignObject><text x="717" y="50" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px">Execution layer</text></switch></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-63"><g><rect x="720" y="70.55" width="393.89" height="227.42" rx="34.11" ry="34.11" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-8"><g><path d="M 730 96 L 910 96 L 920 106 L 920 266 L 740 266 L 730 256 L 730 96 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,825,181)" pointer-events="all"/><path d="M 740 266 L 740 106 L 730 96 M 740 106 L 920 106" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,825,181)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 153px; height: 1px; padding-top: 103px; margin-left: 746px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Adapter</b></div></div></div></foreignObject><text x="823" y="115" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Adapter</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-43"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-9"><g><path d="M 760.45 323.46 L 829.55 323.46 L 839.55 333.46 L 839.55 425.73 L 770.45 425.73 L 760.45 415.73 L 760.45 323.46 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,800,374.6)" pointer-events="all"/><path d="M 770.45 425.73 L 770.45 333.46 L 760.45 323.46 M 770.45 333.46 L 839.55 333.46" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,800,374.6)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 352px; margin-left: 755px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>MoveVM</b></div></div></div></foreignObject><text x="798" y="364" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">MoveVM</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-41"><g><rect x="748.86" y="414.15" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 434px; margin-left: 750px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">move-execution/move-vm-runtime</div></div></div></foreignObject><text x="795" y="438" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">move-execution/...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-44"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-13"><g><path d="M 1018.18 325.93 L 1087.28 325.93 L 1097.28 335.93 L 1097.28 428.2 L 1028.18 428.2 L 1018.18 418.2 L 1018.18 325.93 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1057.73,377.07)" pointer-events="all"/><path d="M 1028.18 428.2 L 1028.18 335.93 L 1018.18 325.93 M 1028.18 335.93 L 1097.28 335.93" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1057.73,377.07)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 355px; margin-left: 1013px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Framework</b></div></div></div></foreignObject><text x="1055" y="367" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Framework</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-42"><g><rect x="1006.59" y="416.62" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 436px; margin-left: 1008px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota-framework crate</div></div></div></foreignObject><text x="1053" y="440" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-framework...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-56"><g><path d="M 816.81 258.42 L 816.8 286.7 Q 816.8 296.7 811.45 296.7 L 808.78 296.7 Q 806.1 296.7 806.11 306.7 L 806.13 328.68" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 806.14 333.93 L 802.63 326.93 L 806.13 328.68 L 809.63 326.92 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 297px; margin-left: 811px;"><div data-drawio-colors="color: rgb(240, 240, 240); background-color: rgb(24, 20, 29); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; background-color: rgb(24, 20, 29); white-space: nowrap;">Init</div></div></div></foreignObject><text x="811" y="300" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="11px" text-anchor="middle">Init</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-58"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-40"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-10"><g><path d="M 774.14 102.02 L 856.77 102.02 L 866.77 112.02 L 866.77 230.59 L 784.14 230.59 L 774.14 220.59 L 774.14 102.02 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,820.45,166.3)" pointer-events="all"/><path d="M 784.14 230.59 L 784.14 112.02 L 774.14 102.02 M 784.14 112.02 L 866.77 112.02" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,820.45,166.3)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 112px; height: 1px; padding-top: 137px; margin-left: 762px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution-engine</b></div></div></div></foreignObject><text x="818" y="149" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Execution-engine</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-39"><g><rect x="762.02" y="212.62" width="109.58" height="45.8" rx="6.87" ry="6.87" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 236px; margin-left: 763px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-adapter/execution-engine</div></div></div></foreignObject><text x="817" y="239" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">...execution/iota-...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-61"><g><path d="M 1008.86 382.52 L 885 382.5 Q 875 382.5 875 372.5 L 874.98 282.37" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 874.98 277.12 L 878.48 284.12 L 874.98 282.37 L 871.48 284.12 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-62"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 382px; margin-left: 910px;"><div data-drawio-colors="color: rgb(240, 240, 240); background-color: rgb(24, 20, 29); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: "Architects Daughter"; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; background-color: rgb(24, 20, 29); white-space: nowrap;"><h4><font face="Helvetica">native fns etc</font></h4></div></div></div></foreignObject><text x="910" y="385" fill="rgb(240, 240, 240)" font-family=""Architects Daughter"" font-size="11px" text-anchor="middle">native fns etc</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-45"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-48"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-46"><g><path d="M 997.72 118.29 L 1066.82 118.29 L 1076.82 128.29 L 1076.82 220.56 L 1007.72 220.56 L 997.72 210.56 L 997.72 118.29 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1037.27,169.43)" pointer-events="all"/><path d="M 1007.72 220.56 L 1007.72 128.29 L 997.72 118.29 M 1007.72 128.29 L 1076.82 128.29" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1037.27,169.43)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 147px; margin-left: 992px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Object runtime</b></div></div></div></foreignObject><text x="1035" y="159" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Object runtime</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-47"><g><rect x="986.14" y="208.98" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 229px; margin-left: 987px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-move-natives</div></div></div></foreignObject><text x="1032" y="232" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">...execution/io...</text></switch></g></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-64"><g><rect x="978.54" y="80.44" width="117.47" height="29.66" rx="4.45" ry="4.45" fill="rgb(24, 20, 29)" stroke="none" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 115px; height: 1px; padding-top: 87px; margin-left: 981px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: "Architects Daughter"; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;"><b style="">iota-execution crate</b></span></div></div></div></foreignObject><text x="981" y="99" fill="rgb(240, 240, 240)" font-family=""Architects Daughter"" font-size="12px">iota-execution crate</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-15"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M -20 61 L 400 61 L 410 71 L 410 441 L -10 441 L -20 431 L -20 61 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,251)" pointer-events="all"/><path d="M -10 441 L -10 71 L -20 61 M -10 71 L 410 71" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,251)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 363px; height: 1px; padding-top: 53px; margin-left: 12px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Nodes network(Iota node)</b></div></div></div></foreignObject><text x="12" y="65" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px">Nodes network(Iota node)</text></switch></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-16"><g><path d="M 5 96 L 365 96 L 375 106 L 375 426 L 15 426 L 5 416 L 5 96 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,190,261)" pointer-events="all"/><path d="M 15 426 L 15 106 L 5 96 M 15 106 L 375 106" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,190,261)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 313px; height: 1px; padding-top: 93px; margin-left: 31px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Node</b></div></div></div></foreignObject><text x="188" y="105" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Node</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-17"><g><path d="M -43.52 219.72 L 213.78 219.72 L 223.78 229.72 L 223.78 305.52 L -33.52 305.52 L -43.52 295.52 L -43.52 219.72 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,90.13,262.62)" pointer-events="all"/><path d="M -33.52 305.52 L -33.52 229.72 L -43.52 219.72 M -33.52 229.72 L 223.78 229.72" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,90.13,262.62)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 69px; height: 1px; padding-top: 146px; margin-left: 53px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>API</b></div></div></div></foreignObject><text x="88" y="158" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">API</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-18"><g><path d="M 258.33 347.9 C 258.33 339.62 274.8 332.9 295.11 332.9 C 304.86 332.9 314.21 334.48 321.11 337.3 C 328.01 340.11 331.88 343.93 331.88 347.9 L 331.88 410.88 C 331.88 419.16 315.42 425.88 295.11 425.88 C 274.8 425.88 258.33 419.16 258.33 410.88 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 331.88 347.9 C 331.88 356.19 315.42 362.9 295.11 362.9 C 274.8 362.9 258.33 356.19 258.33 347.9" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 392px; margin-left: 259px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Ledger storage</b></div></div></div></foreignObject><text x="295" y="395" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Ledger stora...</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-65"><g><path d="M 158.23 347.9 C 158.23 339.62 174.69 332.9 195 332.9 C 204.75 332.9 214.11 334.48 221 337.3 C 227.9 340.11 231.77 343.93 231.77 347.9 L 231.77 410.88 C 231.77 419.16 215.31 425.88 195 425.88 C 174.69 425.88 158.23 419.16 158.23 410.88 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 231.77 347.9 C 231.77 356.19 215.31 362.9 195 362.9 C 174.69 362.9 158.23 356.19 158.23 347.9" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 392px; margin-left: 159px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Indexer</b><div><b>(read/sync db)</b></div></div></div></div></foreignObject><text x="195" y="395" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Indexer...</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-19"><g><path d="M 157.2 108.2 L 225.29 108.2 L 235.29 118.2 L 235.29 205.7 L 167.2 205.7 L 157.2 195.7 L 157.2 108.2 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,196.25,156.95)" pointer-events="all"/><path d="M 167.2 205.7 L 167.2 118.2 L 157.2 108.2 M 167.2 118.2 L 235.29 118.2" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,196.25,156.95)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 135px; margin-left: 154px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Consensus engine</b></div></div></div></foreignObject><text x="194" y="147" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Consensus engi...</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-20"><g><path d="M 154.28 213.88 L 225.72 213.88 L 235.72 223.88 L 235.72 311.38 L 164.28 311.38 L 154.28 301.38 L 154.28 213.88 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,262.63)" pointer-events="all"/><path d="M 164.28 311.38 L 164.28 223.88 L 154.28 213.88 M 164.28 223.88 L 235.72 223.88" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,262.63)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 239px; margin-left: 152px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Network</b></div></div></div></foreignObject><text x="193" y="251" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Network</text></switch></g></g></g><g data-cell-id="C7P2nbfdh_euxFz_vEXo-1"><g><path d="M 264.32 178.44 L 314.32 178.44 L 324.32 188.44 L 324.32 253.56 L 274.32 253.56 L 264.32 243.56 L 264.32 178.44 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,294.33,216)" pointer-events="all"/><path d="M 274.32 253.56 L 274.32 188.44 L 264.32 178.44 M 274.32 188.44 L 324.32 188.44" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,294.33,216)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 203px; margin-left: 263px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Core</b></div></div></div></foreignObject><text x="292" y="215" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Core</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-34"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-31"><g><path d="M 100 471 L 140 471 L 150 481 L 150 591 L 110 591 L 100 581 L 100 471 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,125,531)" pointer-events="all"/><path d="M 110 591 L 110 481 L 100 471 M 110 481 L 150 481" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,125,531)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 523px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-json-rpc</b></div></div></div></foreignObject><text x="123" y="535" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-json-rpc</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-32"><g><rect x="85" y="556" width="80" height="24.55" rx="3.68" ry="3.68" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 568px; margin-left: 86px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>iota-json-rpc crate<br /></div></div></div></div></foreignObject><text x="125" y="572" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-json-rpc...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-36"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-28"><g><path d="M 95 646 L 125 646 L 135 656 L 135 746 L 105 746 L 95 736 L 95 646 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,115,696)" pointer-events="all"/><path d="M 105 746 L 105 656 L 95 646 M 105 656 L 135 656" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,115,696)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 83px; height: 1px; padding-top: 693px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-cli</b></div></div></div></foreignObject><text x="113" y="705" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-cli</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-35"><g><rect x="65" y="716" width="90" height="30" rx="4.5" ry="4.5" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 731px; margin-left: 66px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota-cli crate</div></div></div></foreignObject><text x="110" y="735" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-cli crate</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-49"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 120 676 L 120 638.3 Q 120 628.3 122.5 628.3 L 123.75 628.3 Q 125 628.3 125 618.3 L 125 586.92" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 125 581.67 L 128.5 588.67 L 125 586.92 L 121.5 588.67 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-50"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 130 506 L 130 476 Q 130 466 140 466 L 185 466 Q 195 466 195 456 L 195 432.25" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 195 427 L 198.5 434 L 195 432.25 L 191.5 434 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="C7P2nbfdh_euxFz_vEXo-6"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 710 256 L 530.9 256 Q 520.9 256 520.9 266 L 520.9 369.4 Q 520.9 379.4 510.9 379.4 L 338.25 379.39" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 333 379.39 L 340 375.89 L 338.25 379.39 L 340 382.89 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="C7P2nbfdh_euxFz_vEXo-11"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 331.88 218.38 L 511 218.4 Q 521 218.4 521 208.4 L 521 21 Q 521 11 531 11 L 960.9 11 Q 970.9 11 970.91 17.82 L 970.91 24.63" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 970.92 29.88 L 967.41 22.89 L 970.91 24.63 L 974.41 22.88 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1157px" height="747px" viewBox="-0.5 -0.5 1157 747" content="<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.17 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.17"> <diagram name="Page-1" id="jhOoJUkd9Zucpb-pnEYY"> <mxGraphModel dx="1623" dy="1938" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="none" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="3kIInuhs_YmBb0iZ2eFI-7" value="&lt;b&gt;Execution layer&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=west;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;strokeWidth=1;shadow=1;" parent="1" vertex="1"> <mxGeometry x="495" y="25" width="440" height="450" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-63" value="" style="rounded=1;whiteSpace=wrap;html=1;hachureGap=4;fontFamily=Helvetica;strokeColor=default;fillColor=default;glass=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="10.003376623376589" y="39.550041395623865" width="393.8931818181818" height="227.41573033707866" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-8" value="&lt;b&gt;Adapter&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry width="170" height="190" relative="1" as="geometry"> <mxPoint x="30" y="55" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-43" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="38.86363636363636" y="304.0449438202247" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-9" value="&lt;b&gt;MoveVM&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-41" value="move-execution/move-vm-runtime" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-44" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="296.59090909090907" y="306.51685393258424" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-13" value="&lt;b&gt;Framework&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-42" value="iota-framework crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-56" value="Init" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=45;entryPerimeter=0;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-39" target="3kIInuhs_YmBb0iZ2eFI-9" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-58" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="46.16883116883113" y="88.98824364281488" width="378.4090909090909" height="168.0904080425784" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-40" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1" connectable="0"> <mxGeometry width="128.57142857142858" height="138.42696629213484" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-10" value="&lt;b&gt;Execution-engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-40" vertex="1"> <mxGeometry width="128.57142857142856" height="92.63157894736842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-39" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-adapter/execution-engine" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1"> <mxGeometry x="5.849999999999999" y="92.6274157303371" width="109.575" height="45.79955056179776" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-61" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=45;exitDy=100;exitPerimeter=0;entryX=1;entryY=0.206;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-13" target="3kIInuhs_YmBb0iZ2eFI-8" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-62" value="&lt;h4&gt;&lt;font face=&quot;Helvetica&quot;&gt;native fns etc&lt;/font&gt;&lt;/h4&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-61" vertex="1" connectable="0"> <mxGeometry x="-0.1741" y="-1" relative="1" as="geometry"> <mxPoint as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-45" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="276.1363636363636" y="98.87640449438204" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-48" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-45" vertex="1" connectable="0"> <mxGeometry width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-46" value="&lt;b&gt;Object runtime&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-47" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-move-natives" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-64" value="&lt;span style=&quot;caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;&quot;&gt;&lt;b style=&quot;&quot;&gt;iota-execution crate&lt;/b&gt;&lt;/span&gt;" style="text;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;fillColor=default;fontColor=default;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="268.5363636363636" y="49.43820224719101" width="117.47045454545454" height="29.662921348314605" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-15" value="&lt;b&gt;Nodes network(Iota node)&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=south;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;shadow=1;strokeWidth=1;" parent="1" vertex="1"> <mxGeometry x="-210" y="30" width="380" height="430" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-16" value="&lt;b&gt;Node&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry width="330" height="370" relative="1" as="geometry"> <mxPoint x="20" y="40" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-17" value="&lt;b&gt;API&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="42.2239247311828" y="92.97297297297297" width="85.80645161290323" height="267.2972972972973" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-18" value="&lt;b&gt;Ledger storage&lt;/b&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="253.33129032258068" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-65" value="&lt;b&gt;Indexer&lt;/b&gt;&lt;div&gt;&lt;b&gt;(read/sync db)&lt;/b&gt;&lt;/div&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="153.22580645161293" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-19" value="&lt;b&gt;Consensus engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="142.5" y="81.91" width="97.5" height="78.09" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-20" value="&lt;b&gt;Network&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="141.25" y="185.91" width="97.5" height="81.43" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-1" value="&lt;b&gt;Core&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="251.77" y="150" width="75.11" height="60" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-34" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="500" width="120" height="74.55" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-31" value="&lt;b&gt;iota-json-rpc&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry width="120" height="50" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-32" value="&lt;div&gt;iota-json-rpc crate&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry x="20" y="50.00454545454545" width="80" height="24.545454545454547" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-36" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="670" width="100" height="70" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-28" value="&lt;b&gt;iota-cli&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry width="100" height="40" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-35" value="iota crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry y="40" width="90" height="30" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-49" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=45;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;curved=0;strokeWidth=1;shadow=1;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-28" target="3kIInuhs_YmBb0iZ2eFI-32" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-50" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=55;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=1;shadow=1;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-31" target="3kIInuhs_YmBb0iZ2eFI-65" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;strokeWidth=1;shadow=1;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-7" target="3kIInuhs_YmBb0iZ2eFI-18" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-11" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=32.385;exitDy=0;exitPerimeter=0;entryX=0.407;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;strokeWidth=1;shadow=1;" parent="1" source="C7P2nbfdh_euxFz_vEXo-1" target="3kIInuhs_YmBb0iZ2eFI-7" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> "><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-7"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 710 31 L 1140 31 L 1150 41 L 1150 481 L 720 481 L 710 471 L 710 31 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(180,930,256)" pointer-events="all"/><path d="M 720 481 L 720 41 L 710 31 M 720 41 L 1150 41" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(180,930,256)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 423px; height: 1px; padding-top: 38px; margin-left: 717px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution layer</b></div></div></div></foreignObject><text x="717" y="50" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px">Execution layer</text></switch></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-63"><g><rect x="720" y="70.55" width="393.89" height="227.42" rx="34.11" ry="34.11" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-8"><g><path d="M 730 96 L 910 96 L 920 106 L 920 266 L 740 266 L 730 256 L 730 96 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,825,181)" pointer-events="all"/><path d="M 740 266 L 740 106 L 730 96 M 740 106 L 920 106" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,825,181)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 153px; height: 1px; padding-top: 103px; margin-left: 746px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Adapter</b></div></div></div></foreignObject><text x="823" y="115" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Adapter</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-43"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-9"><g><path d="M 760.45 323.46 L 829.55 323.46 L 839.55 333.46 L 839.55 425.73 L 770.45 425.73 L 760.45 415.73 L 760.45 323.46 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,800,374.6)" pointer-events="all"/><path d="M 770.45 425.73 L 770.45 333.46 L 760.45 323.46 M 770.45 333.46 L 839.55 333.46" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,800,374.6)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 352px; margin-left: 755px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>MoveVM</b></div></div></div></foreignObject><text x="798" y="364" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">MoveVM</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-41"><g><rect x="748.86" y="414.15" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 434px; margin-left: 750px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">move-execution/move-vm-runtime</div></div></div></foreignObject><text x="795" y="438" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">move-execution/...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-44"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-13"><g><path d="M 1018.18 325.93 L 1087.28 325.93 L 1097.28 335.93 L 1097.28 428.2 L 1028.18 428.2 L 1018.18 418.2 L 1018.18 325.93 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1057.73,377.07)" pointer-events="all"/><path d="M 1028.18 428.2 L 1028.18 335.93 L 1018.18 325.93 M 1028.18 335.93 L 1097.28 335.93" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1057.73,377.07)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 355px; margin-left: 1013px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Framework</b></div></div></div></foreignObject><text x="1055" y="367" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Framework</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-42"><g><rect x="1006.59" y="416.62" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 436px; margin-left: 1008px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota-framework crate</div></div></div></foreignObject><text x="1053" y="440" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-framework...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-56"><g><path d="M 816.81 258.42 L 816.8 286.7 Q 816.8 296.7 811.45 296.7 L 808.78 296.7 Q 806.1 296.7 806.11 306.7 L 806.13 328.68" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 806.14 333.93 L 802.63 326.93 L 806.13 328.68 L 809.63 326.92 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 297px; margin-left: 811px;"><div data-drawio-colors="color: rgb(240, 240, 240); background-color: rgb(24, 20, 29); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; background-color: rgb(24, 20, 29); white-space: nowrap;">Init</div></div></div></foreignObject><text x="811" y="300" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="11px" text-anchor="middle">Init</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-58"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-40"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-10"><g><path d="M 774.14 102.02 L 856.77 102.02 L 866.77 112.02 L 866.77 230.59 L 784.14 230.59 L 774.14 220.59 L 774.14 102.02 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,820.45,166.3)" pointer-events="all"/><path d="M 784.14 230.59 L 784.14 112.02 L 774.14 102.02 M 784.14 112.02 L 866.77 112.02" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,820.45,166.3)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 112px; height: 1px; padding-top: 137px; margin-left: 762px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution-engine</b></div></div></div></foreignObject><text x="818" y="149" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Execution-engine</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-39"><g><rect x="762.02" y="212.62" width="109.58" height="45.8" rx="6.87" ry="6.87" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 236px; margin-left: 763px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-adapter/execution-engine</div></div></div></foreignObject><text x="817" y="239" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">...execution/iota-...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-61"><g><path d="M 1008.86 382.52 L 885 382.5 Q 875 382.5 875 372.5 L 874.98 282.37" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 874.98 277.12 L 878.48 284.12 L 874.98 282.37 L 871.48 284.12 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-62"><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 382px; margin-left: 910px;"><div data-drawio-colors="color: rgb(240, 240, 240); background-color: rgb(24, 20, 29); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: "Architects Daughter"; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; background-color: rgb(24, 20, 29); white-space: nowrap;"><h4><font face="Helvetica">native fns etc</font></h4></div></div></div></foreignObject><text x="910" y="385" fill="rgb(240, 240, 240)" font-family=""Architects Daughter"" font-size="11px" text-anchor="middle">native fns etc</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-45"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-48"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-46"><g><path d="M 997.72 118.29 L 1066.82 118.29 L 1076.82 128.29 L 1076.82 220.56 L 1007.72 220.56 L 997.72 210.56 L 997.72 118.29 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1037.27,169.43)" pointer-events="all"/><path d="M 1007.72 220.56 L 1007.72 128.29 L 997.72 118.29 M 1007.72 128.29 L 1076.82 128.29" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,1037.27,169.43)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 147px; margin-left: 992px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Object runtime</b></div></div></div></foreignObject><text x="1035" y="159" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Object runtime</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-47"><g><rect x="986.14" y="208.98" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 229px; margin-left: 987px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-move-natives</div></div></div></foreignObject><text x="1032" y="232" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">...execution/io...</text></switch></g></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-64"><g><rect x="978.54" y="80.44" width="117.47" height="29.66" rx="4.45" ry="4.45" fill="rgb(24, 20, 29)" stroke="none" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 115px; height: 1px; padding-top: 87px; margin-left: 981px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: "Architects Daughter"; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="caret-color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: none; float: none; display: inline !important;"><b style="">iota-execution crate</b></span></div></div></div></foreignObject><text x="981" y="99" fill="rgb(240, 240, 240)" font-family=""Architects Daughter"" font-size="12px">iota-execution crate</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-15"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M -20 61 L 400 61 L 410 71 L 410 441 L -10 441 L -20 431 L -20 61 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,251)" pointer-events="all"/><path d="M -10 441 L -10 71 L -20 61 M -10 71 L 410 71" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,251)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 363px; height: 1px; padding-top: 53px; margin-left: 12px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Nodes network(Iota node)</b></div></div></div></foreignObject><text x="12" y="65" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px">Nodes network(Iota node)</text></switch></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-16"><g><path d="M 5 96 L 365 96 L 375 106 L 375 426 L 15 426 L 5 416 L 5 96 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,190,261)" pointer-events="all"/><path d="M 15 426 L 15 106 L 5 96 M 15 106 L 375 106" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,190,261)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 313px; height: 1px; padding-top: 93px; margin-left: 31px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Node</b></div></div></div></foreignObject><text x="188" y="105" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Node</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-17"><g><path d="M -43.52 219.72 L 213.78 219.72 L 223.78 229.72 L 223.78 305.52 L -33.52 305.52 L -43.52 295.52 L -43.52 219.72 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,90.13,262.62)" pointer-events="all"/><path d="M -33.52 305.52 L -33.52 229.72 L -43.52 219.72 M -33.52 229.72 L 223.78 229.72" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,90.13,262.62)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 69px; height: 1px; padding-top: 146px; margin-left: 53px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>API</b></div></div></div></foreignObject><text x="88" y="158" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">API</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-18"><g><path d="M 258.33 347.9 C 258.33 339.62 274.8 332.9 295.11 332.9 C 304.86 332.9 314.21 334.48 321.11 337.3 C 328.01 340.11 331.88 343.93 331.88 347.9 L 331.88 410.88 C 331.88 419.16 315.42 425.88 295.11 425.88 C 274.8 425.88 258.33 419.16 258.33 410.88 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 331.88 347.9 C 331.88 356.19 315.42 362.9 295.11 362.9 C 274.8 362.9 258.33 356.19 258.33 347.9" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 392px; margin-left: 259px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Ledger storage</b></div></div></div></foreignObject><text x="295" y="395" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Ledger stora...</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-65"><g><path d="M 158.23 347.9 C 158.23 339.62 174.69 332.9 195 332.9 C 204.75 332.9 214.11 334.48 221 337.3 C 227.9 340.11 231.77 343.93 231.77 347.9 L 231.77 410.88 C 231.77 419.16 215.31 425.88 195 425.88 C 174.69 425.88 158.23 419.16 158.23 410.88 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 231.77 347.9 C 231.77 356.19 215.31 362.9 195 362.9 C 174.69 362.9 158.23 356.19 158.23 347.9" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 392px; margin-left: 159px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Indexer</b><div><b>(read/sync db)</b></div></div></div></div></foreignObject><text x="195" y="395" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Indexer...</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-19"><g><path d="M 157.2 108.2 L 225.29 108.2 L 235.29 118.2 L 235.29 205.7 L 167.2 205.7 L 157.2 195.7 L 157.2 108.2 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,196.25,156.95)" pointer-events="all"/><path d="M 167.2 205.7 L 167.2 118.2 L 157.2 108.2 M 167.2 118.2 L 235.29 118.2" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,196.25,156.95)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 135px; margin-left: 154px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Consensus engine</b></div></div></div></foreignObject><text x="194" y="147" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Consensus engi...</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-20"><g><path d="M 154.28 213.88 L 225.72 213.88 L 235.72 223.88 L 235.72 311.38 L 164.28 311.38 L 154.28 301.38 L 154.28 213.88 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,262.63)" pointer-events="all"/><path d="M 164.28 311.38 L 164.28 223.88 L 154.28 213.88 M 164.28 223.88 L 235.72 223.88" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,195,262.63)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 239px; margin-left: 152px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Network</b></div></div></div></foreignObject><text x="193" y="251" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Network</text></switch></g></g></g><g data-cell-id="C7P2nbfdh_euxFz_vEXo-1"><g><path d="M 264.32 178.44 L 314.32 178.44 L 324.32 188.44 L 324.32 253.56 L 274.32 253.56 L 264.32 243.56 L 264.32 178.44 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,294.33,216)" pointer-events="all"/><path d="M 274.32 253.56 L 274.32 188.44 L 264.32 178.44 M 274.32 188.44 L 324.32 188.44" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,294.33,216)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 203px; margin-left: 263px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Core</b></div></div></div></foreignObject><text x="292" y="215" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">Core</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-34"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-31"><g><path d="M 100 471 L 140 471 L 150 481 L 150 591 L 110 591 L 100 581 L 100 471 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,125,531)" pointer-events="all"/><path d="M 110 591 L 110 481 L 100 471 M 110 481 L 150 481" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,125,531)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 523px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-json-rpc</b></div></div></div></foreignObject><text x="123" y="535" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-json-rpc</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-32"><g><rect x="85" y="556" width="80" height="24.55" rx="3.68" ry="3.68" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 568px; margin-left: 86px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>iota-json-rpc crate<br /></div></div></div></div></foreignObject><text x="125" y="572" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-json-rpc...</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-36"><g/><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-28"><g><path d="M 95 646 L 125 646 L 135 656 L 135 746 L 105 746 L 95 736 L 95 646 Z" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,115,696)" pointer-events="all"/><path d="M 105 746 L 105 656 L 95 646 M 105 656 L 135 656" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" transform="rotate(90,115,696)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 83px; height: 1px; padding-top: 693px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-cli</b></div></div></div></foreignObject><text x="113" y="705" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota-cli</text></switch></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-35"><g><rect x="65" y="716" width="90" height="30" rx="4.5" ry="4.5" fill="rgb(24, 20, 29)" stroke="rgb(240, 240, 240)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 731px; margin-left: 66px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota crate</div></div></div></foreignObject><text x="110" y="735" fill="rgb(240, 240, 240)" font-family=""Helvetica"" font-size="12px" text-anchor="middle">iota crate</text></switch></g></g></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-49"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 120 676 L 120 638.3 Q 120 628.3 122.5 628.3 L 123.75 628.3 Q 125 628.3 125 618.3 L 125 586.92" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 125 581.67 L 128.5 588.67 L 125 586.92 L 121.5 588.67 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="3kIInuhs_YmBb0iZ2eFI-50"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 130 506 L 130 476 Q 130 466 140 466 L 185 466 Q 195 466 195 456 L 195 432.25" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 195 427 L 198.5 434 L 195 432.25 L 191.5 434 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="C7P2nbfdh_euxFz_vEXo-6"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 710 256 L 530.9 256 Q 520.9 256 520.9 266 L 520.9 369.4 Q 520.9 379.4 510.9 379.4 L 338.25 379.39" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 333 379.39 L 340 375.89 L 338.25 379.39 L 340 382.89 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g><g data-cell-id="C7P2nbfdh_euxFz_vEXo-11"><g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);"><path d="M 331.88 218.38 L 511 218.4 Q 521 218.4 521 208.4 L 521 21 Q 521 11 531 11 L 960.9 11 Q 970.9 11 970.91 17.82 L 970.91 24.63" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 970.92 29.88 L 967.41 22.89 L 970.91 24.63 L 974.41 22.88 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/></g></g></g></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/docs/site/static/img/concepts/execution-architecture/iota-architecture.svg b/docs/site/static/img/concepts/execution-architecture/iota-architecture.svg index 90113451977..ba261ecd774 100644 --- a/docs/site/static/img/concepts/execution-architecture/iota-architecture.svg +++ b/docs/site/static/img/concepts/execution-architecture/iota-architecture.svg @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- Do not edit this file with editors other than draw.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1151px" height="711px" viewBox="-0.5 -0.5 1151 711" content="<mxfile host="app.diagrams.net" modified="2024-06-06T13:06:20.290Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15" etag="_bzW2ujPehIvRME25DFx" version="24.4.13" type="device"> <diagram name="Page-1" id="jhOoJUkd9Zucpb-pnEYY"> <mxGraphModel dx="2074" dy="816" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="#ffffff" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="3kIInuhs_YmBb0iZ2eFI-7" value="&lt;b&gt;Execution layer&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=south;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="1" vertex="1"> <mxGeometry x="490" y="30" width="450" height="440" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-63" value="" style="rounded=1;whiteSpace=wrap;html=1;hachureGap=4;fontFamily=Helvetica;strokeColor=default;fillColor=default;glass=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="10.003376623376589" y="39.550041395623865" width="393.8931818181818" height="227.41573033707866" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-8" value="&lt;b&gt;Adapter&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry width="170" height="190" relative="1" as="geometry"> <mxPoint x="30" y="55" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-43" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="38.86363636363636" y="304.0449438202247" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-9" value="&lt;b&gt;MoveVM&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-41" value="move-execution/move-vm-runtime" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-44" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="296.59090909090907" y="306.51685393258424" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-13" value="&lt;b&gt;Framework&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-42" value="iota-framework crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-56" value="Init" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=45;entryPerimeter=0;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-39" target="3kIInuhs_YmBb0iZ2eFI-9" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-58" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="46.16883116883113" y="88.98824364281488" width="378.4090909090909" height="168.0904080425784" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-40" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1" connectable="0"> <mxGeometry width="128.57142857142858" height="138.42696629213484" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-10" value="&lt;b&gt;Execution-engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-40" vertex="1"> <mxGeometry width="128.57142857142856" height="92.63157894736842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-39" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-adapter/execution-engine" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1"> <mxGeometry x="5.849999999999999" y="92.6274157303371" width="109.575" height="45.79955056179776" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-61" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=45;exitDy=100;exitPerimeter=0;entryX=1;entryY=0.206;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-13" target="3kIInuhs_YmBb0iZ2eFI-8" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-62" value="&lt;h4&gt;&lt;font face=&quot;Helvetica&quot;&gt;native fns etc&lt;/font&gt;&lt;/h4&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-61" vertex="1" connectable="0"> <mxGeometry x="-0.1741" y="-1" relative="1" as="geometry"> <mxPoint as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-45" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="276.1363636363636" y="98.87640449438204" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-48" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-45" vertex="1" connectable="0"> <mxGeometry width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-46" value="&lt;b&gt;Object runtime&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-47" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-move-natives" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-64" value="&lt;span style=&quot;caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration: none; float: none; display: inline !important;&quot;&gt;&lt;b&gt;iota-execution crate&lt;/b&gt;&lt;/span&gt;" style="text;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="268.5363636363636" y="49.43820224719101" width="117.47045454545454" height="29.662921348314605" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-15" value="&lt;b&gt;Nodes network(Iota node)&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=south;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="1" vertex="1"> <mxGeometry x="-210" y="30" width="380" height="430" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-16" value="&lt;b&gt;Node&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry width="330" height="370" relative="1" as="geometry"> <mxPoint x="20" y="40" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-17" value="&lt;b&gt;API&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="42.2239247311828" y="92.97297297297297" width="85.80645161290323" height="267.2972972972973" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-18" value="&lt;b&gt;Ledger storage&lt;/b&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="253.33129032258068" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-65" value="&lt;b&gt;Indexer&lt;/b&gt;&lt;div&gt;&lt;b&gt;(read/sync db)&lt;/b&gt;&lt;/div&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="153.22580645161293" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-19" value="&lt;b&gt;Consensus engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="142.5" y="81.91" width="97.5" height="78.09" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-20" value="&lt;b&gt;Network&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="141.25" y="185.91" width="97.5" height="81.43" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-1" value="&lt;b&gt;Core&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" vertex="1" parent="3kIInuhs_YmBb0iZ2eFI-15"> <mxGeometry x="251.77" y="150" width="75.11" height="60" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-34" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="500" width="120" height="74.55" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-31" value="&lt;b&gt;iota-json-rpc&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry width="120" height="50" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-32" value="&lt;div&gt;iota-json-rpc crate&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry x="20" y="50.00454545454545" width="80" height="24.545454545454547" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-36" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="670" width="100" height="70" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-28" value="&lt;b&gt;iota-cli&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry width="100" height="40" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-35" value="iota-cli crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry y="40" width="90" height="30" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-49" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=45;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;curved=0;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-28" target="3kIInuhs_YmBb0iZ2eFI-32" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-50" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=55;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-31" target="3kIInuhs_YmBb0iZ2eFI-65" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" edge="1" parent="1" source="3kIInuhs_YmBb0iZ2eFI-7" target="3kIInuhs_YmBb0iZ2eFI-18"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-11" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=32.385;exitDy=0;exitPerimeter=0;entryX=0.407;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" edge="1" parent="1" source="C7P2nbfdh_euxFz_vEXo-1" target="3kIInuhs_YmBb0iZ2eFI-7"> <mxGeometry relative="1" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> " style="background-color: rgb(255, 255, 255);"><defs/><rect fill="#ffffff" width="100%" height="100%" x="0" y="0"/><g><g><path d="M 705 -5 L 1135 -5 L 1145 5 L 1145 445 L 715 445 L 705 435 L 705 -5 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,925,220)" pointer-events="all"/><path d="M 715 445 L 715 5 L 705 -5 M 715 5 L 1145 5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,925,220)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 433px; height: 1px; padding-top: 17px; margin-left: 707px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution layer</b></div></div></div></foreignObject><text x="707" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Execution layer</text></switch></g></g><g><rect x="710" y="39.55" width="393.89" height="227.42" rx="34.11" ry="34.11" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><path d="M 720 65 L 900 65 L 910 75 L 910 235 L 730 235 L 720 225 L 720 65 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,815,150)" pointer-events="all"/><path d="M 730 235 L 730 75 L 720 65 M 730 75 L 910 75" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,815,150)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 153px; height: 1px; padding-top: 72px; margin-left: 736px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Adapter</b></div></div></div></foreignObject><text x="813" y="84" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Adapter</text></switch></g></g><g/><g><path d="M 750.45 292.46 L 819.55 292.46 L 829.55 302.46 L 829.55 394.73 L 760.45 394.73 L 750.45 384.73 L 750.45 292.46 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,790,343.6)" pointer-events="all"/><path d="M 760.45 394.73 L 760.45 302.46 L 750.45 292.46 M 760.45 302.46 L 829.55 302.46" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,790,343.6)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 321px; margin-left: 745px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>MoveVM</b></div></div></div></foreignObject><text x="788" y="333" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">MoveVM</text></switch></g></g><g><rect x="738.86" y="383.15" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 403px; margin-left: 740px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">move-execution/move-vm-runtime</div></div></div></foreignObject><text x="785" y="407" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">move-execution/...</text></switch></g></g><g/><g><path d="M 1008.18 294.93 L 1077.28 294.93 L 1087.28 304.93 L 1087.28 397.2 L 1018.18 397.2 L 1008.18 387.2 L 1008.18 294.93 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1047.73,346.07)" pointer-events="all"/><path d="M 1018.18 397.2 L 1018.18 304.93 L 1008.18 294.93 M 1018.18 304.93 L 1087.28 304.93" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1047.73,346.07)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 324px; margin-left: 1003px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Framework</b></div></div></div></foreignObject><text x="1045" y="336" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Framework</text></switch></g></g><g><rect x="996.59" y="385.62" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 405px; margin-left: 998px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota-framework crate</div></div></div></foreignObject><text x="1043" y="409" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-framework...</text></switch></g></g><g><path d="M 806.81 227.42 L 806.8 255.7 Q 806.8 265.7 801.45 265.7 L 798.77 265.7 Q 796.1 265.7 796.11 275.7 L 796.13 297.68" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 796.14 302.93 L 792.63 295.93 L 796.13 297.68 L 799.63 295.92 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 266px; margin-left: 801px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">Init</div></div></div></foreignObject><text x="801" y="269" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">Init</text></switch></g></g><g/><g/><g><path d="M 764.14 71.02 L 846.77 71.02 L 856.77 81.02 L 856.77 199.59 L 774.14 199.59 L 764.14 189.59 L 764.14 71.02 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,810.45,135.3)" pointer-events="all"/><path d="M 774.14 199.59 L 774.14 81.02 L 764.14 71.02 M 774.14 81.02 L 856.77 81.02" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,810.45,135.3)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 112px; height: 1px; padding-top: 106px; margin-left: 752px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution-engine</b></div></div></div></foreignObject><text x="808" y="118" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Execution-engine</text></switch></g></g><g><rect x="752.02" y="181.62" width="109.58" height="45.8" rx="6.87" ry="6.87" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 205px; margin-left: 753px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-adapter/execution-engine</div></div></div></foreignObject><text x="807" y="208" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">...execution/iota-...</text></switch></g></g><g><path d="M 998.86 351.52 L 875 351.5 Q 865 351.5 865 341.5 L 864.98 251.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 864.98 246.12 L 868.48 253.12 L 864.98 251.37 L 861.48 253.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 351px; margin-left: 900px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: "Architects Daughter"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"><h4><font face="Helvetica">native fns etc</font></h4></div></div></div></foreignObject><text x="900" y="354" fill="rgb(0, 0, 0)" font-family="Architects Daughter" font-size="11px" text-anchor="middle">native fns etc</text></switch></g></g><g/><g/><g><path d="M 987.72 87.29 L 1056.82 87.29 L 1066.82 97.29 L 1066.82 189.56 L 997.72 189.56 L 987.72 179.56 L 987.72 87.29 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1027.27,138.43)" pointer-events="all"/><path d="M 997.72 189.56 L 997.72 97.29 L 987.72 87.29 M 997.72 97.29 L 1066.82 97.29" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1027.27,138.43)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 116px; margin-left: 982px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Object runtime</b></div></div></div></foreignObject><text x="1025" y="128" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Object runtime</text></switch></g></g><g><rect x="976.14" y="177.98" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 198px; margin-left: 977px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-move-natives</div></div></div></foreignObject><text x="1022" y="201" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">...execution/io...</text></switch></g></g><g><rect x="968.54" y="49.44" width="117.47" height="29.66" rx="4.45" ry="4.45" fill="none" stroke="none" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 115px; height: 1px; padding-top: 56px; margin-left: 971px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: "Architects Daughter"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration: none; float: none; display: inline !important;"><b>iota-execution crate</b></span></div></div></div></foreignObject><text x="971" y="68" fill="rgb(0, 0, 0)" font-family="Architects Daughter" font-size="12px">iota-execution crate</text></switch></g></g><g><path d="M -25 25 L 395 25 L 405 35 L 405 405 L -15 405 L -25 395 L -25 25 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,215)" pointer-events="all"/><path d="M -15 405 L -15 35 L -25 25 M -15 35 L 405 35" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,215)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 363px; height: 1px; padding-top: 17px; margin-left: 7px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Nodes network(Iota node)</b></div></div></div></foreignObject><text x="7" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Nodes network(Iota node)</text></switch></g></g><g><path d="M 0 60 L 360 60 L 370 70 L 370 390 L 10 390 L 0 380 L 0 60 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,185,225)" pointer-events="all"/><path d="M 10 390 L 10 70 L 0 60 M 10 70 L 370 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,185,225)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 313px; height: 1px; padding-top: 57px; margin-left: 26px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Node</b></div></div></div></foreignObject><text x="183" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Node</text></switch></g></g><g><path d="M -48.52 183.72 L 208.78 183.72 L 218.78 193.72 L 218.78 269.52 L -38.52 269.52 L -48.52 259.52 L -48.52 183.72 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,85.13,226.62)" pointer-events="all"/><path d="M -38.52 269.52 L -38.52 193.72 L -48.52 183.72 M -38.52 193.72 L 218.78 193.72" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,85.13,226.62)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 69px; height: 1px; padding-top: 110px; margin-left: 48px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>API</b></div></div></div></foreignObject><text x="83" y="122" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">API</text></switch></g></g><g><path d="M 253.33 311.9 C 253.33 303.62 269.8 296.9 290.11 296.9 C 299.86 296.9 309.21 298.48 316.11 301.3 C 323.01 304.11 326.88 307.93 326.88 311.9 L 326.88 374.88 C 326.88 383.16 310.42 389.88 290.11 389.88 C 269.8 389.88 253.33 383.16 253.33 374.88 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 326.88 311.9 C 326.88 320.19 310.42 326.9 290.11 326.9 C 269.8 326.9 253.33 320.19 253.33 311.9" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 356px; margin-left: 254px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Ledger storage</b></div></div></div></foreignObject><text x="290" y="359" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Ledger stora...</text></switch></g></g><g><path d="M 153.23 311.9 C 153.23 303.62 169.69 296.9 190 296.9 C 199.75 296.9 209.11 298.48 216 301.3 C 222.9 304.11 226.77 307.93 226.77 311.9 L 226.77 374.88 C 226.77 383.16 210.31 389.88 190 389.88 C 169.69 389.88 153.23 383.16 153.23 374.88 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 226.77 311.9 C 226.77 320.19 210.31 326.9 190 326.9 C 169.69 326.9 153.23 320.19 153.23 311.9" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 356px; margin-left: 154px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Indexer</b><div><b>(read/sync db)</b></div></div></div></div></foreignObject><text x="190" y="359" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Indexer...</text></switch></g></g><g><path d="M 152.2 72.2 L 220.29 72.2 L 230.29 82.2 L 230.29 169.7 L 162.2 169.7 L 152.2 159.7 L 152.2 72.2 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,191.25,120.95)" pointer-events="all"/><path d="M 162.2 169.7 L 162.2 82.2 L 152.2 72.2 M 162.2 82.2 L 230.29 82.2" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,191.25,120.95)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 99px; margin-left: 149px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Consensus engine</b></div></div></div></foreignObject><text x="189" y="111" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Consensus engi...</text></switch></g></g><g><path d="M 149.29 177.88 L 220.72 177.88 L 230.72 187.88 L 230.72 275.38 L 159.29 275.38 L 149.29 265.38 L 149.29 177.88 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,226.63)" pointer-events="all"/><path d="M 159.29 275.38 L 159.29 187.88 L 149.29 177.88 M 159.29 187.88 L 230.72 187.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,226.63)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 203px; margin-left: 147px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Network</b></div></div></div></foreignObject><text x="188" y="215" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Network</text></switch></g></g><g><path d="M 259.32 142.45 L 309.32 142.45 L 319.32 152.45 L 319.32 217.56 L 269.32 217.56 L 259.32 207.56 L 259.32 142.45 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,289.32,180)" pointer-events="all"/><path d="M 269.32 217.56 L 269.32 152.45 L 259.32 142.45 M 269.32 152.45 L 319.32 152.45" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,289.32,180)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 167px; margin-left: 258px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Core</b></div></div></div></foreignObject><text x="287" y="179" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Core</text></switch></g></g><g/><g><path d="M 95 435 L 135 435 L 145 445 L 145 555 L 105 555 L 95 545 L 95 435 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,120,495)" pointer-events="all"/><path d="M 105 555 L 105 445 L 95 435 M 105 445 L 145 445" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,120,495)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 487px; margin-left: 66px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-json-rpc</b></div></div></div></foreignObject><text x="118" y="499" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-json-rpc</text></switch></g></g><g><rect x="80" y="520" width="80" height="24.55" rx="3.68" ry="3.68" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 532px; margin-left: 81px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>iota-json-rpc crate<br /></div></div></div></div></foreignObject><text x="120" y="536" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-json-rpc...</text></switch></g></g><g/><g><path d="M 90 610 L 120 610 L 130 620 L 130 710 L 100 710 L 90 700 L 90 610 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,110,660)" pointer-events="all"/><path d="M 100 710 L 100 620 L 90 610 M 100 620 L 130 620" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,110,660)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 83px; height: 1px; padding-top: 657px; margin-left: 66px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-cli</b></div></div></div></foreignObject><text x="108" y="669" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-cli</text></switch></g></g><g><rect x="60" y="680" width="90" height="30" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 695px; margin-left: 61px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota-cli crate</div></div></div></foreignObject><text x="105" y="699" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-cli crate</text></switch></g></g><g><path d="M 115 640 L 115 602.3 Q 115 592.3 117.5 592.3 L 118.75 592.3 Q 120 592.3 120 582.3 L 120 550.92" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 120 545.67 L 123.5 552.67 L 120 550.92 L 116.5 552.67 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><path d="M 125 470 L 125 440 Q 125 430 135 430 L 180 430 Q 190 430 190 420 L 190 396.25" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 190 391 L 193.5 398 L 190 396.25 L 186.5 398 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><path d="M 700 220 L 523.4 220 Q 513.4 220 513.4 230 L 513.4 333.4 Q 513.4 343.4 503.4 343.4 L 333.25 343.39" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 328 343.39 L 335 339.89 L 333.25 343.39 L 335 346.89 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><path d="M 326.88 182.39 L 503.5 182.4 Q 513.5 182.4 513.5 180.75 L 513.5 179.92 Q 513.5 179.1 523.5 179.1 L 693.63 179.08" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 698.88 179.08 L 691.88 182.58 L 693.63 179.08 L 691.88 175.58 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1151px" height="711px" viewBox="-0.5 -0.5 1151 711" content="<mxfile host="app.diagrams.net" modified="2024-06-06T13:06:20.290Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15" etag="_bzW2ujPehIvRME25DFx" version="24.4.13" type="device"> <diagram name="Page-1" id="jhOoJUkd9Zucpb-pnEYY"> <mxGraphModel dx="2074" dy="816" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="#ffffff" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="3kIInuhs_YmBb0iZ2eFI-7" value="&lt;b&gt;Execution layer&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=south;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="1" vertex="1"> <mxGeometry x="490" y="30" width="450" height="440" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-63" value="" style="rounded=1;whiteSpace=wrap;html=1;hachureGap=4;fontFamily=Helvetica;strokeColor=default;fillColor=default;glass=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="10.003376623376589" y="39.550041395623865" width="393.8931818181818" height="227.41573033707866" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-8" value="&lt;b&gt;Adapter&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry width="170" height="190" relative="1" as="geometry"> <mxPoint x="30" y="55" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-43" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="38.86363636363636" y="304.0449438202247" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-9" value="&lt;b&gt;MoveVM&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-41" value="move-execution/move-vm-runtime" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-43" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-44" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="296.59090909090907" y="306.51685393258424" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-13" value="&lt;b&gt;Framework&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-42" value="iota-framework crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-44" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-56" value="Init" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=45;entryPerimeter=0;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-39" target="3kIInuhs_YmBb0iZ2eFI-9" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-58" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="46.16883116883113" y="88.98824364281488" width="378.4090909090909" height="168.0904080425784" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-40" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1" connectable="0"> <mxGeometry width="128.57142857142858" height="138.42696629213484" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-10" value="&lt;b&gt;Execution-engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-40" vertex="1"> <mxGeometry width="128.57142857142856" height="92.63157894736842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-39" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-adapter/execution-engine" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-58" vertex="1"> <mxGeometry x="5.849999999999999" y="92.6274157303371" width="109.575" height="45.79955056179776" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-61" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=45;exitDy=100;exitPerimeter=0;entryX=1;entryY=0.206;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;curved=0;" parent="3kIInuhs_YmBb0iZ2eFI-7" source="3kIInuhs_YmBb0iZ2eFI-13" target="3kIInuhs_YmBb0iZ2eFI-8" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-62" value="&lt;h4&gt;&lt;font face=&quot;Helvetica&quot;&gt;native fns etc&lt;/font&gt;&lt;/h4&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];sketch=1;hachureGap=4;jiggle=2;curveFitting=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-61" vertex="1" connectable="0"> <mxGeometry x="-0.1741" y="-1" relative="1" as="geometry"> <mxPoint as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-45" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1" connectable="0"> <mxGeometry x="276.1363636363636" y="98.87640449438204" width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-48" value="" style="group;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-45" vertex="1" connectable="0"> <mxGeometry width="102.27272727272727" height="118.65168539325842" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-46" value="&lt;b&gt;Object runtime&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry width="102.27272727272727" height="79.10112359550561" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-47" value="...execution/&lt;span style=&quot;font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;&quot;&gt;iota&lt;/span&gt;-move-natives" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-48" vertex="1"> <mxGeometry y="79.10112359550561" width="92.04545454545455" height="39.550561797752806" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-64" value="&lt;span style=&quot;caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration: none; float: none; display: inline !important;&quot;&gt;&lt;b&gt;iota-execution crate&lt;/b&gt;&lt;/span&gt;" style="text;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-7" vertex="1"> <mxGeometry x="268.5363636363636" y="49.43820224719101" width="117.47045454545454" height="29.662921348314605" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-15" value="&lt;b&gt;Nodes network(Iota node)&lt;/b&gt;" style="verticalAlign=top;align=left;shape=cube;size=10;direction=south;fontStyle=0;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="1" vertex="1"> <mxGeometry x="-210" y="30" width="380" height="430" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-16" value="&lt;b&gt;Node&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry width="330" height="370" relative="1" as="geometry"> <mxPoint x="20" y="40" as="offset" /> </mxGeometry> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-17" value="&lt;b&gt;API&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="42.2239247311828" y="92.97297297297297" width="85.80645161290323" height="267.2972972972973" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-18" value="&lt;b&gt;Ledger storage&lt;/b&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="253.33129032258068" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-65" value="&lt;b&gt;Indexer&lt;/b&gt;&lt;div&gt;&lt;b&gt;(read/sync db)&lt;/b&gt;&lt;/div&gt;" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="153.22580645161293" y="296.9044851994852" width="73.5483870967742" height="92.97297297297297" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-19" value="&lt;b&gt;Consensus engine&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="142.5" y="81.91" width="97.5" height="78.09" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-20" value="&lt;b&gt;Network&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-15" vertex="1"> <mxGeometry x="141.25" y="185.91" width="97.5" height="81.43" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-1" value="&lt;b&gt;Core&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" vertex="1" parent="3kIInuhs_YmBb0iZ2eFI-15"> <mxGeometry x="251.77" y="150" width="75.11" height="60" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-34" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="500" width="120" height="74.55" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-31" value="&lt;b&gt;iota-json-rpc&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry width="120" height="50" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-32" value="&lt;div&gt;iota-json-rpc crate&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="3kIInuhs_YmBb0iZ2eFI-34" vertex="1"> <mxGeometry x="20" y="50.00454545454545" width="80" height="24.545454545454547" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-36" value="" style="group;rounded=1;" parent="1" vertex="1" connectable="0"> <mxGeometry x="-150" y="670" width="100" height="70" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-28" value="&lt;b&gt;iota-cli&lt;/b&gt;" style="verticalAlign=top;align=center;shape=cube;size=10;direction=south;html=1;boundedLbl=1;spacingLeft=5;whiteSpace=wrap;rounded=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry width="100" height="40" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-35" value="iota crate" style="rounded=1;whiteSpace=wrap;html=1;" parent="3kIInuhs_YmBb0iZ2eFI-36" vertex="1"> <mxGeometry y="40" width="90" height="30" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-49" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=45;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;curved=0;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-28" target="3kIInuhs_YmBb0iZ2eFI-32" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="3kIInuhs_YmBb0iZ2eFI-50" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=55;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="3kIInuhs_YmBb0iZ2eFI-31" target="3kIInuhs_YmBb0iZ2eFI-65" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" edge="1" parent="1" source="3kIInuhs_YmBb0iZ2eFI-7" target="3kIInuhs_YmBb0iZ2eFI-18"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="C7P2nbfdh_euxFz_vEXo-11" style="edgeStyle=orthogonalEdgeStyle;rounded=1;hachureGap=4;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=32.385;exitDy=0;exitPerimeter=0;entryX=0.407;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" edge="1" parent="1" source="C7P2nbfdh_euxFz_vEXo-1" target="3kIInuhs_YmBb0iZ2eFI-7"> <mxGeometry relative="1" as="geometry" /> </mxCell> </root> </mxGraphModel> </diagram> </mxfile> " style="background-color: rgb(255, 255, 255);"><defs/><rect fill="#ffffff" width="100%" height="100%" x="0" y="0"/><g><g><path d="M 705 -5 L 1135 -5 L 1145 5 L 1145 445 L 715 445 L 705 435 L 705 -5 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,925,220)" pointer-events="all"/><path d="M 715 445 L 715 5 L 705 -5 M 715 5 L 1145 5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,925,220)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 433px; height: 1px; padding-top: 17px; margin-left: 707px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution layer</b></div></div></div></foreignObject><text x="707" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Execution layer</text></switch></g></g><g><rect x="710" y="39.55" width="393.89" height="227.42" rx="34.11" ry="34.11" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><path d="M 720 65 L 900 65 L 910 75 L 910 235 L 730 235 L 720 225 L 720 65 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,815,150)" pointer-events="all"/><path d="M 730 235 L 730 75 L 720 65 M 730 75 L 910 75" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,815,150)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 153px; height: 1px; padding-top: 72px; margin-left: 736px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Adapter</b></div></div></div></foreignObject><text x="813" y="84" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Adapter</text></switch></g></g><g/><g><path d="M 750.45 292.46 L 819.55 292.46 L 829.55 302.46 L 829.55 394.73 L 760.45 394.73 L 750.45 384.73 L 750.45 292.46 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,790,343.6)" pointer-events="all"/><path d="M 760.45 394.73 L 760.45 302.46 L 750.45 292.46 M 760.45 302.46 L 829.55 302.46" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,790,343.6)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 321px; margin-left: 745px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>MoveVM</b></div></div></div></foreignObject><text x="788" y="333" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">MoveVM</text></switch></g></g><g><rect x="738.86" y="383.15" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 403px; margin-left: 740px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">move-execution/move-vm-runtime</div></div></div></foreignObject><text x="785" y="407" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">move-execution/...</text></switch></g></g><g/><g><path d="M 1008.18 294.93 L 1077.28 294.93 L 1087.28 304.93 L 1087.28 397.2 L 1018.18 397.2 L 1008.18 387.2 L 1008.18 294.93 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1047.73,346.07)" pointer-events="all"/><path d="M 1018.18 397.2 L 1018.18 304.93 L 1008.18 294.93 M 1018.18 304.93 L 1087.28 304.93" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1047.73,346.07)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 324px; margin-left: 1003px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Framework</b></div></div></div></foreignObject><text x="1045" y="336" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Framework</text></switch></g></g><g><rect x="996.59" y="385.62" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 405px; margin-left: 998px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota-framework crate</div></div></div></foreignObject><text x="1043" y="409" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-framework...</text></switch></g></g><g><path d="M 806.81 227.42 L 806.8 255.7 Q 806.8 265.7 801.45 265.7 L 798.77 265.7 Q 796.1 265.7 796.11 275.7 L 796.13 297.68" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 796.14 302.93 L 792.63 295.93 L 796.13 297.68 L 799.63 295.92 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 266px; margin-left: 801px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">Init</div></div></div></foreignObject><text x="801" y="269" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">Init</text></switch></g></g><g/><g/><g><path d="M 764.14 71.02 L 846.77 71.02 L 856.77 81.02 L 856.77 199.59 L 774.14 199.59 L 764.14 189.59 L 764.14 71.02 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,810.45,135.3)" pointer-events="all"/><path d="M 774.14 199.59 L 774.14 81.02 L 764.14 71.02 M 774.14 81.02 L 856.77 81.02" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,810.45,135.3)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 112px; height: 1px; padding-top: 106px; margin-left: 752px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Execution-engine</b></div></div></div></foreignObject><text x="808" y="118" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Execution-engine</text></switch></g></g><g><rect x="752.02" y="181.62" width="109.58" height="45.8" rx="6.87" ry="6.87" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 205px; margin-left: 753px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-adapter/execution-engine</div></div></div></foreignObject><text x="807" y="208" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">...execution/iota-...</text></switch></g></g><g><path d="M 998.86 351.52 L 875 351.5 Q 865 351.5 865 341.5 L 864.98 251.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 864.98 246.12 L 868.48 253.12 L 864.98 251.37 L 861.48 253.12 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 351px; margin-left: 900px;"><div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 11px; font-family: "Architects Daughter"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"><h4><font face="Helvetica">native fns etc</font></h4></div></div></div></foreignObject><text x="900" y="354" fill="rgb(0, 0, 0)" font-family="Architects Daughter" font-size="11px" text-anchor="middle">native fns etc</text></switch></g></g><g/><g/><g><path d="M 987.72 87.29 L 1056.82 87.29 L 1066.82 97.29 L 1066.82 189.56 L 997.72 189.56 L 987.72 179.56 L 987.72 87.29 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1027.27,138.43)" pointer-events="all"/><path d="M 997.72 189.56 L 997.72 97.29 L 987.72 87.29 M 997.72 97.29 L 1066.82 97.29" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,1027.27,138.43)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 85px; height: 1px; padding-top: 116px; margin-left: 982px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Object runtime</b></div></div></div></foreignObject><text x="1025" y="128" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Object runtime</text></switch></g></g><g><rect x="976.14" y="177.98" width="92.05" height="39.55" rx="5.93" ry="5.93" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 90px; height: 1px; padding-top: 198px; margin-left: 977px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">...execution/<span style="font-family: "Helvetica Neue"; font-size: 13px;">iota</span>-move-natives</div></div></div></foreignObject><text x="1022" y="201" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">...execution/io...</text></switch></g></g><g><rect x="968.54" y="49.44" width="117.47" height="29.66" rx="4.45" ry="4.45" fill="none" stroke="none" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 115px; height: 1px; padding-top: 56px; margin-left: 971px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: "Architects Daughter"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; text-align: center; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration: none; float: none; display: inline !important;"><b>iota-execution crate</b></span></div></div></div></foreignObject><text x="971" y="68" fill="rgb(0, 0, 0)" font-family="Architects Daughter" font-size="12px">iota-execution crate</text></switch></g></g><g><path d="M -25 25 L 395 25 L 405 35 L 405 405 L -15 405 L -25 395 L -25 25 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,215)" pointer-events="all"/><path d="M -15 405 L -15 35 L -25 25 M -15 35 L 405 35" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,215)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 363px; height: 1px; padding-top: 17px; margin-left: 7px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Nodes network(Iota node)</b></div></div></div></foreignObject><text x="7" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px">Nodes network(Iota node)</text></switch></g></g><g><path d="M 0 60 L 360 60 L 370 70 L 370 390 L 10 390 L 0 380 L 0 60 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,185,225)" pointer-events="all"/><path d="M 10 390 L 10 70 L 0 60 M 10 70 L 370 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,185,225)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 313px; height: 1px; padding-top: 57px; margin-left: 26px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Node</b></div></div></div></foreignObject><text x="183" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Node</text></switch></g></g><g><path d="M -48.52 183.72 L 208.78 183.72 L 218.78 193.72 L 218.78 269.52 L -38.52 269.52 L -48.52 259.52 L -48.52 183.72 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,85.13,226.62)" pointer-events="all"/><path d="M -38.52 269.52 L -38.52 193.72 L -48.52 183.72 M -38.52 193.72 L 218.78 193.72" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,85.13,226.62)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 69px; height: 1px; padding-top: 110px; margin-left: 48px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>API</b></div></div></div></foreignObject><text x="83" y="122" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">API</text></switch></g></g><g><path d="M 253.33 311.9 C 253.33 303.62 269.8 296.9 290.11 296.9 C 299.86 296.9 309.21 298.48 316.11 301.3 C 323.01 304.11 326.88 307.93 326.88 311.9 L 326.88 374.88 C 326.88 383.16 310.42 389.88 290.11 389.88 C 269.8 389.88 253.33 383.16 253.33 374.88 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 326.88 311.9 C 326.88 320.19 310.42 326.9 290.11 326.9 C 269.8 326.9 253.33 320.19 253.33 311.9" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 356px; margin-left: 254px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Ledger storage</b></div></div></div></foreignObject><text x="290" y="359" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Ledger stora...</text></switch></g></g><g><path d="M 153.23 311.9 C 153.23 303.62 169.69 296.9 190 296.9 C 199.75 296.9 209.11 298.48 216 301.3 C 222.9 304.11 226.77 307.93 226.77 311.9 L 226.77 374.88 C 226.77 383.16 210.31 389.88 190 389.88 C 169.69 389.88 153.23 383.16 153.23 374.88 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 226.77 311.9 C 226.77 320.19 210.31 326.9 190 326.9 C 169.69 326.9 153.23 320.19 153.23 311.9" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 72px; height: 1px; padding-top: 356px; margin-left: 154px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Indexer</b><div><b>(read/sync db)</b></div></div></div></div></foreignObject><text x="190" y="359" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Indexer...</text></switch></g></g><g><path d="M 152.2 72.2 L 220.29 72.2 L 230.29 82.2 L 230.29 169.7 L 162.2 169.7 L 152.2 159.7 L 152.2 72.2 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,191.25,120.95)" pointer-events="all"/><path d="M 162.2 169.7 L 162.2 82.2 L 152.2 72.2 M 162.2 82.2 L 230.29 82.2" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,191.25,120.95)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 99px; margin-left: 149px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Consensus engine</b></div></div></div></foreignObject><text x="189" y="111" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Consensus engi...</text></switch></g></g><g><path d="M 149.29 177.88 L 220.72 177.88 L 230.72 187.88 L 230.72 275.38 L 159.29 275.38 L 149.29 265.38 L 149.29 177.88 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,226.63)" pointer-events="all"/><path d="M 159.29 275.38 L 159.29 187.88 L 149.29 177.88 M 159.29 187.88 L 230.72 187.88" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,190,226.63)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 81px; height: 1px; padding-top: 203px; margin-left: 147px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Network</b></div></div></div></foreignObject><text x="188" y="215" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Network</text></switch></g></g><g><path d="M 259.32 142.45 L 309.32 142.45 L 319.32 152.45 L 319.32 217.56 L 269.32 217.56 L 259.32 207.56 L 259.32 142.45 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,289.32,180)" pointer-events="all"/><path d="M 269.32 217.56 L 269.32 152.45 L 259.32 142.45 M 269.32 152.45 L 319.32 152.45" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,289.32,180)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 167px; margin-left: 258px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Core</b></div></div></div></foreignObject><text x="287" y="179" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Core</text></switch></g></g><g/><g><path d="M 95 435 L 135 435 L 145 445 L 145 555 L 105 555 L 95 545 L 95 435 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,120,495)" pointer-events="all"/><path d="M 105 555 L 105 445 L 95 435 M 105 445 L 145 445" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,120,495)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 487px; margin-left: 66px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-json-rpc</b></div></div></div></foreignObject><text x="118" y="499" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-json-rpc</text></switch></g></g><g><rect x="80" y="520" width="80" height="24.55" rx="3.68" ry="3.68" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 532px; margin-left: 81px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>iota-json-rpc crate<br /></div></div></div></div></foreignObject><text x="120" y="536" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-json-rpc...</text></switch></g></g><g/><g><path d="M 90 610 L 120 610 L 130 620 L 130 710 L 100 710 L 90 700 L 90 610 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,110,660)" pointer-events="all"/><path d="M 100 710 L 100 620 L 90 610 M 100 620 L 130 620" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,110,660)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 83px; height: 1px; padding-top: 657px; margin-left: 66px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>iota-cli</b></div></div></div></foreignObject><text x="108" y="669" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota-cli</text></switch></g></g><g><rect x="60" y="680" width="90" height="30" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/></g><g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 695px; margin-left: 61px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">iota crate</div></div></div></foreignObject><text x="105" y="699" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">iota crate</text></switch></g></g><g><path d="M 115 640 L 115 602.3 Q 115 592.3 117.5 592.3 L 118.75 592.3 Q 120 592.3 120 582.3 L 120 550.92" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 120 545.67 L 123.5 552.67 L 120 550.92 L 116.5 552.67 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><path d="M 125 470 L 125 440 Q 125 430 135 430 L 180 430 Q 190 430 190 420 L 190 396.25" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 190 391 L 193.5 398 L 190 396.25 L 186.5 398 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><path d="M 700 220 L 523.4 220 Q 513.4 220 513.4 230 L 513.4 333.4 Q 513.4 343.4 503.4 343.4 L 333.25 343.39" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 328 343.39 L 335 339.89 L 333.25 343.39 L 335 346.89 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g><g><path d="M 326.88 182.39 L 503.5 182.4 Q 513.5 182.4 513.5 180.75 L 513.5 179.92 Q 513.5 179.1 523.5 179.1 L 693.63 179.08" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 698.88 179.08 L 691.88 182.58 L 693.63 179.08 L 691.88 175.58 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/docs/site/static/json/developer/getting-started/connect.json b/docs/site/static/json/developer/getting-started/connect.json index 9f62706071a..7e155759c20 100644 --- a/docs/site/static/json/developer/getting-started/connect.json +++ b/docs/site/static/json/developer/getting-started/connect.json @@ -11,7 +11,7 @@ { "questionText": "When first running the 'iota client' command, what happens if you press Enter without specifying a node URL?", "answerOptions": [ - { "answerText": "It connects to the Devnet", "isCorrect": true }, + { "answerText": "It connects to the Testnet", "isCorrect": true }, { "answerText": "It exits the program", "isCorrect": false }, { "answerText": "It connects to the Mainnet", "isCorrect": false }, { "answerText": "It shows an error message", "isCorrect": false } diff --git a/docs/site/static/json/developer/getting-started/create-a-package.json b/docs/site/static/json/developer/getting-started/create-a-package.json index d2411c38815..23a5799d24d 100644 --- a/docs/site/static/json/developer/getting-started/create-a-package.json +++ b/docs/site/static/json/developer/getting-started/create-a-package.json @@ -1,29 +1,38 @@ [ { - "questionText": "What is the purpose of the `key` ability in a struct declaration in Move?", + "questionText": "What is the command to create a new IOTA Move package called 'my_first_package'?", "answerOptions": [ - { "answerText": "It allows a struct to be stored and transferred.", "isCorrect": true }, - { "answerText": "It allows a struct to be copied.", "isCorrect": false }, - { "answerText": "It allows a struct to be dropped or discarded.", "isCorrect": false }, - { "answerText": "It allows a struct to be stored within structs with the `key` ability.", "isCorrect": false } + { "answerText": "iota move new my_first_package", "isCorrect": true }, + { "answerText": "iota create package my_first_package", "isCorrect": false }, + { "answerText": "iota new package my_first_package", "isCorrect": false }, + { "answerText": "iota init my_first_package", "isCorrect": false } ] }, { - "questionText": "Which ability allows a struct to be discarded in Move?", + "questionText": "What is the purpose of the Move.toml file in an IOTA Move package?", "answerOptions": [ - { "answerText": "`copy`", "isCorrect": false }, - { "answerText": "`drop`", "isCorrect": true }, - { "answerText": "`key`", "isCorrect": false }, - { "answerText": "`store`", "isCorrect": false } + { "answerText": "It is the package's manifest that describes the package and its dependencies.", "isCorrect": true }, + { "answerText": "It contains the source code of the modules.", "isCorrect": false }, + { "answerText": "It stores compiled bytecode.", "isCorrect": false }, + { "answerText": "It is used to manage version control.", "isCorrect": false } ] }, { - "questionText": "How do you denote a comment in a `.move` file?", + "questionText": "In .toml files, which character is used to denote a comment?", "answerOptions": [ - { "answerText": "Using double slashes `//`", "isCorrect": true }, - { "answerText": "Using `/* ... */`", "isCorrect": false }, - { "answerText": "Using `#`", "isCorrect": false }, - { "answerText": "Using `--`", "isCorrect": false } + { "answerText": "Hash mark (#)", "isCorrect": true }, + { "answerText": "Double slash (//)", "isCorrect": false }, + { "answerText": "Semicolon (;)", "isCorrect": false }, + { "answerText": "Percent sign (%)", "isCorrect": false } + ] + }, + { + "questionText": "How can you resolve version conflicts in dependencies in the [dependencies] section?", + "answerOptions": [ + { "answerText": "By adding the 'override' field to the dependency", "isCorrect": true }, + { "answerText": "By removing the conflicting dependency", "isCorrect": false }, + { "answerText": "By renaming the dependency", "isCorrect": false }, + { "answerText": "By using a different package manager", "isCorrect": false } ] } ] diff --git a/docs/site/static/json/developer/getting-started/local-network.json b/docs/site/static/json/developer/getting-started/local-network.json index 23a5799d24d..4396fab85bb 100644 --- a/docs/site/static/json/developer/getting-started/local-network.json +++ b/docs/site/static/json/developer/getting-started/local-network.json @@ -1,38 +1,86 @@ [ - { - "questionText": "What is the command to create a new IOTA Move package called 'my_first_package'?", - "answerOptions": [ - { "answerText": "iota move new my_first_package", "isCorrect": true }, - { "answerText": "iota create package my_first_package", "isCorrect": false }, - { "answerText": "iota new package my_first_package", "isCorrect": false }, - { "answerText": "iota init my_first_package", "isCorrect": false } - ] - }, - { - "questionText": "What is the purpose of the Move.toml file in an IOTA Move package?", - "answerOptions": [ - { "answerText": "It is the package's manifest that describes the package and its dependencies.", "isCorrect": true }, - { "answerText": "It contains the source code of the modules.", "isCorrect": false }, - { "answerText": "It stores compiled bytecode.", "isCorrect": false }, - { "answerText": "It is used to manage version control.", "isCorrect": false } - ] - }, - { - "questionText": "In .toml files, which character is used to denote a comment?", - "answerOptions": [ - { "answerText": "Hash mark (#)", "isCorrect": true }, - { "answerText": "Double slash (//)", "isCorrect": false }, - { "answerText": "Semicolon (;)", "isCorrect": false }, - { "answerText": "Percent sign (%)", "isCorrect": false } - ] - }, - { - "questionText": "How can you resolve version conflicts in dependencies in the [dependencies] section?", - "answerOptions": [ - { "answerText": "By adding the 'override' field to the dependency", "isCorrect": true }, - { "answerText": "By removing the conflicting dependency", "isCorrect": false }, - { "answerText": "By renaming the dependency", "isCorrect": false }, - { "answerText": "By using a different package manager", "isCorrect": false } - ] - } -] + { + "questionText": "What command is used to start a local IOTA network with a test token faucet?", + "answerOptions": [ + { + "answerText": "RUST_LOG=\"off,iota_node=info\" cargo run --bin iota iota", + "isCorrect": false + }, + { + "answerText": "RUST_LOG=\"off,iota_node=info\" cargo run --bin iota start --force-regenesis --with-faucet", + "isCorrect": true + }, + { + "answerText": "RUST_LOG=\"off,iota_node=info\" cargo run --bin iota client", + "isCorrect": false + }, + { + "answerText": "cargo run --bin iota stop", + "isCorrect": false + } + ] + }, + { + "questionText": "Which flag should be used to persist data on the local network instead of using --force-regenesis?", + "answerOptions": [ + { + "answerText": "--config-dir", + "isCorrect": true + }, + { + "answerText": "--persist-data", + "isCorrect": false + }, + { + "answerText": "--save-state", + "isCorrect": false + }, + { + "answerText": "--data-dir", + "isCorrect": false + } + ] + }, + { + "questionText": "How can you retrieve the total transaction count from your local network using cURL?", + "answerOptions": [ + { + "answerText": "curl --location --request GET 'http://127.0.0.1:9000' --header 'Content-Type: application/json' --data-raw '{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"iota_getTotalTransactionBlocks\", \"params\": []}'", + "isCorrect": false + }, + { + "answerText": "curl --location --request POST 'http://127.0.0.1:9000' --header 'Content-Type: application/json' --data-raw '{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"iota_getTotalTransactionBlocks\", \"params\": []}'", + "isCorrect": true + }, + { + "answerText": "curl --location --request POST 'http://127.0.0.1:9000' --header 'Content-Type: application/json' --data-raw '{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"iota_getTransactionCount\", \"params\": []}'", + "isCorrect": false + }, + { + "answerText": "curl --location --request GET 'http://127.0.0.1:9000' --header 'Content-Type: application/json' --data-raw '{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"iota_getTransactionCount\", \"params\": []}'", + "isCorrect": false + } + ] + }, + { + "questionText": "What command is used to create a new environment for the IOTA Client CLI with an alias local and RPC URL http://127.0.0.1:9000?", + "answerOptions": [ + { + "answerText": "iota client new-env --alias local --rpc http://127.0.0.1:9000", + "isCorrect": true + }, + { + "answerText": "iota client create-env --alias local --rpc http://127.0.0.1:9000", + "isCorrect": false + }, + { + "answerText": "iota client setup-env --alias local --rpc http://127.0.0.1:9000", + "isCorrect": false + }, + { + "answerText": "iota client init-env --alias local --rpc http://127.0.0.1:9000", + "isCorrect": false + } + ] + } +] \ No newline at end of file diff --git a/examples/tic-tac-toe/cli/src/turn_cap.rs b/examples/tic-tac-toe/cli/src/turn_cap.rs index 73af874cc6e..9ee96ed8435 100644 --- a/examples/tic-tac-toe/cli/src/turn_cap.rs +++ b/examples/tic-tac-toe/cli/src/turn_cap.rs @@ -2,12 +2,11 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use serde::Deserialize; use iota_types::base_types::ObjectID; +use serde::Deserialize; -/// Rust representation of a Move `owned::TurnCap`, suitable for deserializing from their BCS -/// representation. -#[allow(dead_code)] +/// Rust representation of a Move `owned::TurnCap`, suitable for deserializing +/// from their BCS representation. #[derive(Deserialize)] pub(crate) struct TurnCap { pub id: ObjectID, diff --git a/iota-execution/latest/iota-adapter/src/gas_charger.rs b/iota-execution/latest/iota-adapter/src/gas_charger.rs index d83e38ba39b..be082050188 100644 --- a/iota-execution/latest/iota-adapter/src/gas_charger.rs +++ b/iota-execution/latest/iota-adapter/src/gas_charger.rs @@ -28,14 +28,14 @@ pub mod checked { /// All the information about gas is stored in this object. /// The objective here is two-fold: /// 1- Isolate al version info into a single entry point. This file and the - /// other gas related files are the only one that check for gas + /// other gas related files are the only one that check for gas /// version. 2- Isolate all gas accounting into a single implementation. - /// Gas objects are not passed around, and they are retrieved from + /// Gas objects are not passed around, and they are retrieved from /// this instance. - #[allow(dead_code)] #[derive(Debug)] pub struct GasCharger { tx_digest: TransactionDigest, + #[expect(unused)] gas_model_version: u64, gas_coins: Vec<ObjectRef>, // this is the first gas coin in `gas_coins` and the one that all others will diff --git a/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs b/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs index b382a447ffc..60dff0ae180 100644 --- a/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs +++ b/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs @@ -844,7 +844,7 @@ mod checked { /// instances using the protocol's binary configuration. The function /// ensures that the module list is not empty and converts any /// deserialization errors into an `ExecutionError`. - #[allow(clippy::extra_unused_type_parameters)] + #[expect(clippy::extra_unused_type_parameters)] fn deserialize_modules<Mode: ExecutionMode>( context: &mut ExecutionContext<'_, '_, '_>, module_bytes: &[Vec<u8>], diff --git a/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs b/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs index 50ef7d78cc3..fcada9506e3 100644 --- a/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs +++ b/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs @@ -229,7 +229,6 @@ impl Inner<'_> { Ok(obj_opt) } - #[allow(clippy::map_entry)] fn get_or_fetch_object_from_store( &mut self, parent: ObjectID, diff --git a/nre/docker/README.md b/nre/docker/README.md index 640e94dabd3..bc80d87cca3 100644 --- a/nre/docker/README.md +++ b/nre/docker/README.md @@ -9,7 +9,7 @@ Tested using: 1. Confirm you have either [Docker Engine](https://docs.docker.com/engine/install/) or [Docker Desktop](https://docs.docker.com/desktop/install/linux-install/) installed, as well as [Docker Compose](https://github.com/docker/compose#linux). -2. Update [validator.yaml](../config/validator.yaml) and place it in the same directory as `docker-compose.yaml`. +2. Update [validator.yaml](../config/validator.yaml) and place it in the same directory as [`docker-compose.yaml`](https://github.com/iotaledger/iota/blob/testnet/nre/docker/docker-compose.yaml). Add the paths to your private keys to validator.yaml. If you chose to put them in `/opt/iota/key-pairs`, you can use the following example: @@ -22,7 +22,7 @@ network-key-pair: path: /opt/iota/key-pairs/network.key ``` -3. Place `genesis.blob` in the same directory as `docker-compose.yaml`. (available post genesis ceremony) +3. Place `genesis.blob` in the same directory as [`docker-compose.yaml`](https://github.com/iotaledger/iota/blob/testnet/nre/docker/docker-compose.yaml). (available post genesis ceremony) ## Connectivity diff --git a/nre/validator_tasks.md b/nre/validator_tasks.md index 06d741d4131..ce7c4c1ee43 100644 --- a/nre/validator_tasks.md +++ b/nre/validator_tasks.md @@ -27,7 +27,6 @@ This document focuses on running the Iota Node software as a Validator. - [Chain Operations](#chain-operations) - [Updating On-chain Metadata](#updating-on-chain-metadata) - [Operation Cap](#operation-cap) - - [Updating the Gas Price Survey Quote](#updating-the-gas-price-survey-quote) - [Reporting/Un-reporting Validators](#reportingun-reporting-validators) - [Joining the Validator Set](#joining-the-validator-set) - [Leaving the Validator Set](#leaving-the-validator-set) @@ -364,16 +363,6 @@ setting the holder as the active address. <!-- Will be fixed by issue 1867. --> <!-- Or go to the [explorer](https://explorer.rebased.iota.org/object/0x0000000000000000000000000000000000000005) and look for `operation_cap_id` of that validator in the `validators` module. --> -### Updating the Gas Price Survey Quote - -To update the Gas Price Survey Quote of a validator, which is used to calculate the Reference Gas Price at the end of -the epoch, the sender needs to hold a valid [`UnverifiedValidatorOperationCap`](#operation-cap). The sender could be the -validator itself, or a trusted delegatee. To do so, call `iota_system::request_set_gas_price`: - -```shell -iota client call --package 0x3 --module iota_system --function request_set_gas_price --args 0x5 {cap_object_id} {new_gas_price} --gas-budget 10000 -``` - ### Reporting/Un-reporting Validators To report a validator or undo an existing report, the sender needs to hold a valid [`UnverifiedValidatorOperationCap`](#operation-cap). The sender could be the validator itself, or a trusted delegatee. To diff --git a/nre/validator_tool.md b/nre/validator_tool.md index 3c6c3291463..2dd32ef695c 100644 --- a/nre/validator_tool.md +++ b/nre/validator_tool.md @@ -106,22 +106,6 @@ Operation Cap allows a validator to authorizer another account to perform certai The Operation Cap holder (either the validator itself or the delegatee) updates its Gas Price and reports validator peers with the Operation Cap. -#### Update Gas Price - -To update Gas Price, run - -```bash -$IOTA_BINARY validator update-gas-price <gas-price> -``` - -if the account itself is a validator and holds the Operation Cap. Or - -```bash -$IOTA_BINARY validator update-gas-price --operation-cap-id <operation-cap-id> <gas-price> -``` - -if the account is a delegatee. - #### Report Validators To report validators peers, run diff --git a/package.json b/package.json index 653580fe2fc..9ceeb891cd9 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "semver@<7.5.2": ">=7.5.2", "postcss@<8.4.31": ">=8.4.31", "braces": ">=3.0.3", - "path-to-regexp@0.1.7": "0.1.10", + "path-to-regexp@<0.1.12": "0.1.12", "path-to-regexp@2.2.1": "3.3.0", "path-to-regexp@3.2.0": "3.3.0", "body-parser": ">=1.20.3", @@ -45,7 +45,13 @@ "eslint": "8.57.1", "mermaid@10.9.1": "10.9.3", "http-proxy-middleware": "2.0.7", - "cross-spawn": "7.0.5" + "cross-spawn": "7.0.5", + "axios@<0.28.0": "0.28.0", + "tough-cookie@<4.1.3": "4.1.3", + "ws": "8.18.0", + "@sentry/browser": "7.120.2", + "nanoid@3.3.7": "3.3.8", + "nanoid@5.0.7": "5.0.9" } }, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e655b68433..2d2bcfb5276 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ overrides: semver@<7.5.2: '>=7.5.2' postcss@<8.4.31: '>=8.4.31' braces: '>=3.0.3' - path-to-regexp@0.1.7: 0.1.10 + path-to-regexp@<0.1.12: 0.1.12 path-to-regexp@2.2.1: 3.3.0 path-to-regexp@3.2.0: 3.3.0 body-parser: '>=1.20.3' @@ -21,6 +21,12 @@ overrides: mermaid@10.9.1: 10.9.3 http-proxy-middleware: 2.0.7 cross-spawn: 7.0.5 + axios@<0.28.0: 0.28.0 + tough-cookie@<4.1.3: 4.1.3 + ws: 8.18.0 + '@sentry/browser': 7.120.2 + nanoid@3.3.7: 3.3.8 + nanoid@5.0.7: 5.0.9 importers: @@ -653,8 +659,8 @@ importers: specifier: ^5.3.3 version: 5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0) vite-plugin-dts: - specifier: ^3.9.1 - version: 3.9.1(@types/node@20.16.9)(rollup@4.22.4)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)) + specifier: ^4.3.0 + version: 4.3.0(@types/node@20.16.9)(rollup@4.22.4)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)) vite-tsconfig-paths: specifier: ^4.2.0 version: 4.3.2(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)) @@ -752,8 +758,8 @@ importers: specifier: ^1.3.0 version: 1.4.0 '@sentry/browser': - specifier: ^7.61.0 - version: 7.119.0 + specifier: 7.120.2 + version: 7.120.2 '@tanstack/react-query': specifier: ^5.50.1 version: 5.56.2(react@18.3.1) @@ -796,9 +802,6 @@ importers: idb-keyval: specifier: ^6.2.1 version: 6.2.1 - jose: - specifier: ^5.2.3 - version: 5.9.3 mitt: specifier: ^3.0.1 version: 3.0.1 @@ -990,8 +993,8 @@ importers: specifier: ^2.0.1 version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) web-ext: - specifier: ^7.6.2 - version: 7.12.0(body-parser@1.20.3) + specifier: ^8.3.0 + version: 8.3.0(body-parser@1.20.3) webpack: specifier: ^5.79.0 version: 5.95.0(@swc/core@1.7.28)(webpack-cli@5.1.4) @@ -1025,6 +1028,9 @@ importers: '@iota/ui-icons': specifier: workspace:* version: link:../ui-icons + '@iota/wallet-standard': + specifier: workspace:* + version: link:../../sdk/wallet-standard '@tanstack/react-query': specifier: ^5.50.1 version: 5.56.2(react@18.3.1) @@ -1038,8 +1044,8 @@ importers: specifier: ^2.4.2 version: 2.4.6(react@18.3.1) next: - specifier: 14.2.10 - version: 14.2.10(@babel/core@7.25.2)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.3) + specifier: 14.2.15 + version: 14.2.15(@babel/core@7.25.2)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.3) react: specifier: ^18.3.1 version: 18.3.1 @@ -1311,6 +1317,9 @@ importers: '@amplitude/analytics-browser': specifier: ^1.10.3 version: 1.13.6 + '@artsy/to-title-case': + specifier: ^1.1.0 + version: 1.1.0 '@docsearch/react': specifier: ^3.6.0 version: 3.6.1(@algolia/client-search@4.24.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1380,9 +1389,6 @@ importers: copy-text-to-clipboard: specifier: ^3.2.0 version: 3.2.0 - docusaurus-theme-search-typesense: - specifier: 0.20.0-0 - version: 0.20.0-0(@algolia/client-search@4.24.0)(@babel/runtime@7.25.6)(@docusaurus/core@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16))(@docusaurus/theme-common@3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16))(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.9)(algoliasearch@4.24.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16) dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -1398,6 +1404,9 @@ importers: hast-util-is-element: specifier: ^1.1.0 version: 1.1.0 + html-react-parser: + specifier: ^5.1.18 + version: 5.1.18(@types/react@18.3.9)(react@18.3.1) lodash: specifier: ^4.17.21 version: 4.17.21 @@ -1431,6 +1440,9 @@ importers: react-ui-scrollspy: specifier: ^2.3.0 version: 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rehype-jargon: + specifier: ^3.1.0 + version: 3.1.0 rehype-katex: specifier: ^7.0.0 version: 7.0.1 @@ -1553,128 +1565,6 @@ importers: specifier: ^5.5.3 version: 5.6.2 - sdk/create-dapp/templates/react-client-dapp: - dependencies: - '@iota/dapp-kit': - specifier: workspace:* - version: link:../../../dapp-kit - '@iota/iota-sdk': - specifier: workspace:* - version: link:../../../typescript - '@radix-ui/colors': - specifier: ^3.0.0 - version: 3.0.0 - '@radix-ui/react-icons': - specifier: ^1.3.0 - version: 1.3.0(react@18.3.1) - '@radix-ui/themes': - specifier: ^3.1.1 - version: 3.1.4(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-query': - specifier: ^5.50.1 - version: 5.56.2(react@18.3.1) - react: - specifier: ^18.3.1 - version: 18.3.1 - react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) - devDependencies: - '@types/react': - specifier: ^18.3.3 - version: 18.3.9 - '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 - '@typescript-eslint/eslint-plugin': - specifier: ^7.16.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) - '@typescript-eslint/parser': - specifier: ^7.16.0 - version: 7.18.0(eslint@8.57.1)(typescript@5.6.2) - '@vitejs/plugin-react-swc': - specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0)) - eslint: - specifier: 8.57.1 - version: 8.57.1 - eslint-plugin-react-hooks: - specifier: ^4.6.2 - version: 4.6.2(eslint@8.57.1) - eslint-plugin-react-refresh: - specifier: ^0.4.7 - version: 0.4.12(eslint@8.57.1) - prettier: - specifier: ^3.3.2 - version: 3.3.3 - typescript: - specifier: ^5.5.3 - version: 5.6.2 - vite: - specifier: ^5.3.3 - version: 5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0) - - sdk/create-dapp/templates/react-e2e-counter: - dependencies: - '@iota/dapp-kit': - specifier: workspace:* - version: link:../../../dapp-kit - '@iota/iota-sdk': - specifier: workspace:* - version: link:../../../typescript - '@radix-ui/colors': - specifier: ^3.0.0 - version: 3.0.0 - '@radix-ui/react-icons': - specifier: ^1.3.0 - version: 1.3.0(react@18.3.1) - '@radix-ui/themes': - specifier: ^3.1.1 - version: 3.1.4(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-query': - specifier: ^5.50.1 - version: 5.56.2(react@18.3.1) - react: - specifier: ^18.3.1 - version: 18.3.1 - react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) - devDependencies: - '@types/react': - specifier: ^18.3.3 - version: 18.3.9 - '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 - '@typescript-eslint/eslint-plugin': - specifier: ^6.1.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) - '@typescript-eslint/parser': - specifier: ^6.1.0 - version: 6.21.0(eslint@8.57.1)(typescript@5.6.2) - '@vitejs/plugin-react-swc': - specifier: ^3.7.0 - version: 3.7.0(@swc/helpers@0.5.5)(vite@5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0)) - eslint: - specifier: 8.57.1 - version: 8.57.1 - eslint-plugin-react-hooks: - specifier: ^4.6.2 - version: 4.6.2(eslint@8.57.1) - eslint-plugin-react-refresh: - specifier: ^0.4.7 - version: 0.4.12(eslint@8.57.1) - prettier: - specifier: ^3.3.2 - version: 3.3.3 - typescript: - specifier: ^5.5.3 - version: 5.6.2 - vite: - specifier: ^5.3.3 - version: 5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0) - sdk/dapp-kit: dependencies: '@iota/iota-sdk': @@ -2013,7 +1903,7 @@ importers: specifier: ^7.2.0 version: 7.2.0 ws: - specifier: ^8.18.0 + specifier: 8.18.0 version: 8.18.0 sdk/wallet-standard: @@ -2057,9 +1947,6 @@ packages: '@adraffy/ens-normalize@1.11.0': resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==} - '@algolia/autocomplete-core@1.8.2': - resolution: {integrity: sha512-mTeshsyFhAqw/ebqNsQpMtbnjr+qVOSKXArEj4K0d7sqc8It1XD0gkASwecm9mF/jlOQ4Z9RNg1HbdA8JPdRwQ==} - '@algolia/autocomplete-core@1.9.3': resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} @@ -2068,21 +1955,12 @@ packages: peerDependencies: search-insights: '>= 1 < 3' - '@algolia/autocomplete-preset-algolia@1.8.2': - resolution: {integrity: sha512-J0oTx4me6ZM9kIKPuL3lyU3aB8DEvpVvR6xWmHVROx5rOYJGQcZsdG4ozxwcOyiiu3qxMkIbzntnV1S1VWD8yA==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - '@algolia/autocomplete-preset-algolia@1.9.3': resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' - '@algolia/autocomplete-shared@1.8.2': - resolution: {integrity: sha512-b6Z/X4MczChMcfhk6kfRmBzPgjoPzuS9KGR4AFsiLulLNRAAqhP+xZTKtMnZGhLuc61I20d5WqlId02AZvcO6g==} - '@algolia/autocomplete-shared@1.9.3': resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==} peerDependencies: @@ -2227,6 +2105,9 @@ packages: resolution: {integrity: sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==} engines: {node: '>=14'} + '@artsy/to-title-case@1.1.0': + resolution: {integrity: sha512-n2GISILNv3X9xVjkUC1thoOl7rVKPeDskobQJsC5G8poFpF4HNxxSJD97NjvCK3Ai1RplSMsN2MCmMAy7plx9Q==} + '@aw-web-design/x-default-browser@1.4.126': resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==} hasBin: true @@ -2912,10 +2793,6 @@ packages: resolution: {integrity: sha512-Gz0Nrobx8szge6kQQ5Z5MX9L3ObqNwCQY1PSwSNzreFL7aHGxv8Fp2j3ETV6/wWdbiV+mW6OSm8oQhg3Tcsniw==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.21.0': - resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.25.6': resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} engines: {node: '>=6.9.0'} @@ -3271,8 +3148,8 @@ packages: resolution: {integrity: sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==} engines: {node: '>= 0.10.4'} - '@devicefarmer/adbkit@3.2.3': - resolution: {integrity: sha512-wK9rVrabs4QU0oK8Jnwi+HRBEm+s1x/o63kgthUe0y7K1bfcYmgLuQf41/adsj/5enddlSxzkJavl2EwOu+r1g==} + '@devicefarmer/adbkit@3.2.6': + resolution: {integrity: sha512-8lO1hSeTgtxcOHhp4tTWq/JaOysp5KNbbyFoxNEBnwkCDZu/Bji3ZfOaG++Riv9jN6c9bgdLBOZqJTC5VJPRKQ==} engines: {node: '>= 0.10.4'} hasBin: true @@ -3300,14 +3177,6 @@ packages: search-insights: optional: true - '@docusaurus/core@3.4.0': - resolution: {integrity: sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==} - engines: {node: '>=18.0'} - hasBin: true - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - '@docusaurus/core@3.5.2': resolution: {integrity: sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==} engines: {node: '>=18.0'} @@ -3317,29 +3186,14 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/cssnano-preset@3.4.0': - resolution: {integrity: sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==} - engines: {node: '>=18.0'} - '@docusaurus/cssnano-preset@3.5.2': resolution: {integrity: sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==} engines: {node: '>=18.0'} - '@docusaurus/logger@3.4.0': - resolution: {integrity: sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==} - engines: {node: '>=18.0'} - '@docusaurus/logger@3.5.2': resolution: {integrity: sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==} engines: {node: '>=18.0'} - '@docusaurus/mdx-loader@3.4.0': - resolution: {integrity: sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==} - engines: {node: '>=18.0'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - '@docusaurus/mdx-loader@3.5.2': resolution: {integrity: sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==} engines: {node: '>=18.0'} @@ -3347,12 +3201,6 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/module-type-aliases@3.4.0': - resolution: {integrity: sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==} - peerDependencies: - react: '*' - react-dom: '*' - '@docusaurus/module-type-aliases@3.5.2': resolution: {integrity: sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==} peerDependencies: @@ -3374,13 +3222,6 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-content-docs@3.4.0': - resolution: {integrity: sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==} - engines: {node: '>=18.0'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - '@docusaurus/plugin-content-docs@3.5.2': resolution: {integrity: sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==} engines: {node: '>=18.0'} @@ -3482,10 +3323,6 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/theme-translations@3.4.0': - resolution: {integrity: sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==} - engines: {node: '>=18.0'} - '@docusaurus/theme-translations@3.5.2': resolution: {integrity: sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==} engines: {node: '>=18.0'} @@ -3493,27 +3330,12 @@ packages: '@docusaurus/tsconfig@3.5.2': resolution: {integrity: sha512-rQ7toURCFnWAIn8ubcquDs0ewhPwviMzxh6WpRjBW7sJVCXb6yzwUaY3HMNa0VXCFw+qkIbFywrMTf+Pb4uHWQ==} - '@docusaurus/types@3.4.0': - resolution: {integrity: sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - '@docusaurus/types@3.5.2': resolution: {integrity: sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/utils-common@3.4.0': - resolution: {integrity: sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==} - engines: {node: '>=18.0'} - peerDependencies: - '@docusaurus/types': '*' - peerDependenciesMeta: - '@docusaurus/types': - optional: true - '@docusaurus/utils-common@3.5.2': resolution: {integrity: sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==} engines: {node: '>=18.0'} @@ -3523,23 +3345,10 @@ packages: '@docusaurus/types': optional: true - '@docusaurus/utils-validation@3.4.0': - resolution: {integrity: sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==} - engines: {node: '>=18.0'} - '@docusaurus/utils-validation@3.5.2': resolution: {integrity: sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==} engines: {node: '>=18.0'} - '@docusaurus/utils@3.4.0': - resolution: {integrity: sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==} - engines: {node: '>=18.0'} - peerDependencies: - '@docusaurus/types': '*' - peerDependenciesMeta: - '@docusaurus/types': - optional: true - '@docusaurus/utils@3.5.2': resolution: {integrity: sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==} engines: {node: '>=18.0'} @@ -4642,8 +4451,8 @@ packages: resolution: {integrity: sha512-3lBouSuF7CqlseLB+FKES0K4FQ02JrbEoRtJhxnsyB1s5v4AP03gsoohN8jp7DcOImhaR9scYdztq3/sLfk/qQ==} engines: {node: '>=14.18.0'} - '@mdn/browser-compat-data@5.5.29': - resolution: {integrity: sha512-NHdG3QOiAsxh8ygBSKMa/WaNJwpNt87uVqW+S2RlnSqgeRdk+L3foNWTX6qd0I3NHSlCFb47rgopeNCJtRDY5A==} + '@mdn/browser-compat-data@5.6.0': + resolution: {integrity: sha512-xArvLyzuk0r2m6hFVjTMYoLvhWwys3h7W8pO15tjSAea+U39cErWDNfoUs4g2C08HVg6bDHyDMBc0LC6FKRpVw==} '@mdx-js/mdx@3.0.1': resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==} @@ -4678,19 +4487,25 @@ packages: resolution: {integrity: sha512-ihb3B0T/wJm1eUuArYP4lCTSEoZsClHhuWyfo/kMX3m/odpqNcPfsz5O2A3NT7dXCAgWPGDQGPqygCpgeniKMw==} engines: {node: '>=12.0.0'} - '@microsoft/api-extractor-model@7.28.13': - resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} + '@microsoft/api-extractor-model@7.30.1': + resolution: {integrity: sha512-CTS2PlASJHxVY8hqHORVb1HdECWOEMcMnM6/kDkPr0RZapAFSIHhg9D4jxuE8g+OWYHtPc10LCpmde5pylTRlA==} - '@microsoft/api-extractor@7.43.0': - resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} + '@microsoft/api-extractor@7.48.1': + resolution: {integrity: sha512-HN9Osa1WxqLM66RaqB5nPAadx+nTIQmY/XtkFdaJvusjG8Tus++QqZtD7KPZDSkhEMGHsYeSyeU8qUzCDUXPjg==} hasBin: true '@microsoft/tsdoc-config@0.16.2': resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + '@microsoft/tsdoc-config@0.17.1': + resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==} + '@microsoft/tsdoc@0.14.2': resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + '@microsoft/tsdoc@0.15.1': + resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} + '@mswjs/interceptors@0.35.8': resolution: {integrity: sha512-PFfqpHplKa7KMdoQdj5td03uG05VK2Ng1dG0sP4pT9h0dGSX2v9txYt/AnrzPb/vAmfyBBC0NQV7VaBEX+efgQ==} engines: {node: '>=18'} @@ -4862,62 +4677,62 @@ packages: '@nestjs/platform-express': optional: true - '@next/env@14.2.10': - resolution: {integrity: sha512-dZIu93Bf5LUtluBXIv4woQw2cZVZ2DJTjax5/5DOs3lzEOeKLy7GxRSr4caK9/SCPdaW6bCgpye6+n4Dh9oJPw==} + '@next/env@14.2.15': + resolution: {integrity: sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==} '@next/eslint-plugin-next@14.2.3': resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} - '@next/swc-darwin-arm64@14.2.10': - resolution: {integrity: sha512-V3z10NV+cvMAfxQUMhKgfQnPbjw+Ew3cnr64b0lr8MDiBJs3eLnM6RpGC46nhfMZsiXgQngCJKWGTC/yDcgrDQ==} + '@next/swc-darwin-arm64@14.2.15': + resolution: {integrity: sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.10': - resolution: {integrity: sha512-Y0TC+FXbFUQ2MQgimJ/7Ina2mXIKhE7F+GUe1SgnzRmwFY3hX2z8nyVCxE82I2RicspdkZnSWMn4oTjIKz4uzA==} + '@next/swc-darwin-x64@14.2.15': + resolution: {integrity: sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.10': - resolution: {integrity: sha512-ZfQ7yOy5zyskSj9rFpa0Yd7gkrBnJTkYVSya95hX3zeBG9E55Z6OTNPn1j2BTFWvOVVj65C3T+qsjOyVI9DQpA==} + '@next/swc-linux-arm64-gnu@14.2.15': + resolution: {integrity: sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.10': - resolution: {integrity: sha512-n2i5o3y2jpBfXFRxDREr342BGIQCJbdAUi/K4q6Env3aSx8erM9VuKXHw5KNROK9ejFSPf0LhoSkU/ZiNdacpQ==} + '@next/swc-linux-arm64-musl@14.2.15': + resolution: {integrity: sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.10': - resolution: {integrity: sha512-GXvajAWh2woTT0GKEDlkVhFNxhJS/XdDmrVHrPOA83pLzlGPQnixqxD8u3bBB9oATBKB//5e4vpACnx5Vaxdqg==} + '@next/swc-linux-x64-gnu@14.2.15': + resolution: {integrity: sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.10': - resolution: {integrity: sha512-opFFN5B0SnO+HTz4Wq4HaylXGFV+iHrVxd3YvREUX9K+xfc4ePbRrxqOuPOFjtSuiVouwe6uLeDtabjEIbkmDA==} + '@next/swc-linux-x64-musl@14.2.15': + resolution: {integrity: sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.10': - resolution: {integrity: sha512-9NUzZuR8WiXTvv+EiU/MXdcQ1XUvFixbLIMNQiVHuzs7ZIFrJDLJDaOF1KaqttoTujpcxljM/RNAOmw1GhPPQQ==} + '@next/swc-win32-arm64-msvc@14.2.15': + resolution: {integrity: sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.10': - resolution: {integrity: sha512-fr3aEbSd1GeW3YUMBkWAu4hcdjZ6g4NBl1uku4gAn661tcxd1bHs1THWYzdsbTRLcCKLjrDZlNp6j2HTfrw+Bg==} + '@next/swc-win32-ia32-msvc@14.2.15': + resolution: {integrity: sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.10': - resolution: {integrity: sha512-UjeVoRGKNL2zfbcQ6fscmgjBAS/inHBh63mjIlfPg/NG8Yn2ztqylXt5qilYb6hoHIwaU2ogHknHWWmahJjgZQ==} + '@next/swc-win32-x64-msvc@14.2.15': + resolution: {integrity: sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -5163,15 +4978,9 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@radix-ui/colors@3.0.0': - resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} - '@radix-ui/number@1.0.1': resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} - '@radix-ui/number@1.1.0': - resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} - '@radix-ui/primitive@1.0.0': resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} @@ -5181,32 +4990,6 @@ packages: '@radix-ui/primitive@1.1.0': resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} - '@radix-ui/react-accessible-icon@1.1.0': - resolution: {integrity: sha512-i9Zg4NOSXlfUva0agzI2DjWrvFJm9uO4L6CMW7nmMa5CIOOX/Yin894W7WwjodFQWPwe5kmAJ4JF33R8slKI2g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-alert-dialog@1.1.1': - resolution: {integrity: sha512-wmCoJwj7byuVuiLKqDLlX7ClSUU0vd9sdCeM+2Ls+uf13+cpSJoMgwysHq1SGVVkJj5Xn0XWi1NoRCdkMpr6Mw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-arrow@1.0.3': resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -5233,45 +5016,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-aspect-ratio@1.1.0': - resolution: {integrity: sha512-dP87DM/Y7jFlPgUZTlhx6FF5CEzOiaxp2rBCKlaXlpH5Ip/9Fg5zZ9lDOQ5o/MOfUlf36eak14zoWYpgcgGoOg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-avatar@1.1.0': - resolution: {integrity: sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-checkbox@1.1.0': - resolution: {integrity: sha512-3+kSzVfMONtP3B6CvaOrXLVTyGYws7tGmG5kOY0AfyH9sexkLytIwciNwjZhY0RoGOEbxI7bMS21XYB8H5itWQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-checkbox@1.1.1': resolution: {integrity: sha512-0i/EKJ222Afa1FE0C6pNJxDq1itzcl3HChE9DwskA4th4KRse8ojx8a1nVcOjwJdbpDLcz7uol77yYnQNMHdKw==} peerDependencies: @@ -5347,19 +5091,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-context-menu@2.2.1': - resolution: {integrity: sha512-wvMKKIeb3eOrkJ96s722vcidZ+2ZNfcYZWBPRHIB1VWrF+fiF851Io6LX0kmK5wTDQFKdulCCKJk2c3SBaQHvA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-context@1.0.0': resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} peerDependencies: @@ -5520,37 +5251,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-form@0.1.0': - resolution: {integrity: sha512-1/oVYPDjbFILOLIarcGcMKo+y6SbTVT/iUKVEw59CF4offwZgBgC3ZOeSBewjqU0vdA6FWTPWTN63obj55S/tQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-hover-card@1.1.1': - resolution: {integrity: sha512-IwzAOP97hQpDADYVKrEEHUH/b2LA+9MgB0LgdmnbFO2u/3M5hmEofjjr2M6CyzUblaAqJdFm6B7oFtU72DPXrA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-icons@1.3.0': - resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} - peerDependencies: - react: ^16.x || ^17.x || ^18.x - '@radix-ui/react-id@1.0.0': resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} peerDependencies: @@ -5735,32 +5435,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.0': - resolution: {integrity: sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-radio-group@1.2.0': - resolution: {integrity: sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-roving-focus@1.1.0': resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==} peerDependencies: @@ -5774,19 +5448,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-scroll-area@1.1.0': - resolution: {integrity: sha512-9ArIZ9HWhsrfqS765h+GZuLoxaRHD/j0ZWOWilsCvYTpYJp8XwCqNG7Dt9Nu/TItKOdgLGkOPCodQvDc+UMwYg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-select@1.2.2': resolution: {integrity: sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==} peerDependencies: @@ -5800,19 +5461,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.1.1': - resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-separator@1.1.0': resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==} peerDependencies: @@ -5826,19 +5474,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slider@1.2.0': - resolution: {integrity: sha512-dAHCDA4/ySXROEPaRtaMV5WHL8+JB/DbtyTbJjYkY0RXmKMO2Ln8DFZhywG5/mVQ4WqHDBc8smc14yPXPqZHYA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-slot@1.0.0': resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==} peerDependencies: @@ -5927,19 +5562,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.1.1': - resolution: {integrity: sha512-LLE8nzNE4MzPMw3O2zlVlkLFid3y9hMUs7uCbSHyKSo+tCN4yMCf+ZCCcfrYgsOC0TiHBPQ1mtpJ2liY3ZT3SQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-use-callback-ref@1.0.0': resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} peerDependencies: @@ -6118,19 +5740,6 @@ packages: '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} - '@radix-ui/themes@3.1.4': - resolution: {integrity: sha512-HmyU8UpoYPmdfXSQIwbEnJS2Wv/5wbzxe/Niw/sMLMJTrlZiUKM/dEOM7N+bc7w2OMpUaQ8OH9czCGpcjHx98w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: 16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: 16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@reduxjs/toolkit@1.9.7': resolution: {integrity: sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==} peerDependencies: @@ -6244,27 +5853,27 @@ packages: '@rushstack/eslint-patch@1.10.4': resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} - '@rushstack/node-core-library@4.0.2': - resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} + '@rushstack/node-core-library@5.10.1': + resolution: {integrity: sha512-BSb/KcyBHmUQwINrgtzo6jiH0HlGFmrUy33vO6unmceuVKTEyL2q+P0fQq2oB5hvXVWOEUhxB2QvlkZluvUEmg==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/rig-package@0.5.2': - resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==} + '@rushstack/rig-package@0.5.3': + resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} - '@rushstack/terminal@0.10.0': - resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} + '@rushstack/terminal@0.14.4': + resolution: {integrity: sha512-NxACqERW0PHq8Rpq1V6v5iTHEwkRGxenjEW+VWqRYQ8T9puUzgmGHmEZUaUEDHAe9Qyvp0/Ew04sAiQw9XjhJg==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/ts-command-line@4.19.1': - resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + '@rushstack/ts-command-line@4.23.2': + resolution: {integrity: sha512-JJ7XZX5K3ThBBva38aomgsPv1L7FV6XmSOcR6HtM7HDFZJkepqT65imw26h9ggGqMjsY0R9jcl30tzKcVj9aOQ==} '@saucelabs/theme-github-codeblock@0.3.0': resolution: {integrity: sha512-+8xWxBfN+I8StJ0QXERMbGf+BHwRXHWV3mFl9uDayXERiZ/rR93d0nAS3s9s/rKjqh/YSm/4dThEkBNBLnGs4Q==} @@ -6284,20 +5893,20 @@ packages: '@scure/bip39@1.4.0': resolution: {integrity: sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==} - '@sentry-internal/feedback@7.119.0': - resolution: {integrity: sha512-om8TkAU5CQGO8nkmr7qsSBVkP+/vfeS4JgtW3sjoTK0fhj26+DljR6RlfCGWtYQdPSP6XV7atcPTjbSnsmG9FQ==} + '@sentry-internal/feedback@7.120.2': + resolution: {integrity: sha512-AAKW/Qii2qFA/jIZc9HK/CgQLfJ/9zBCqC9FF3bUTnAxzZAbJHO1TLnHqDl5k5wYtt449NrpObHORpopSk7TBg==} engines: {node: '>=12'} - '@sentry-internal/replay-canvas@7.119.0': - resolution: {integrity: sha512-NL02VQx6ekPxtVRcsdp1bp5Tb5w6vnfBKSIfMKuDRBy5A10Uc3GSoy/c3mPyHjOxB84452A+xZSx6bliEzAnuA==} + '@sentry-internal/replay-canvas@7.120.2': + resolution: {integrity: sha512-2Xm9rJxUA5/CCUYRHNH4VxG13eQpXvM05/cHQKn+pmjaPq/+3fbOHrZZM84gF1FSHjeN8Kg1igoMSekU2kavOQ==} engines: {node: '>=12'} - '@sentry-internal/tracing@7.119.0': - resolution: {integrity: sha512-oKdFJnn+56f0DHUADlL8o9l8jTib3VDLbWQBVkjD9EprxfaCwt2m8L5ACRBdQ8hmpxCEo4I8/6traZ7qAdBUqA==} + '@sentry-internal/tracing@7.120.2': + resolution: {integrity: sha512-eo2F8cP6X+vr54Mp6vu+NoQEDz0M5O24Tz8jPY0T1CpiWdwCmHb7Sln+oLXeQ3/LlWdVQihBfKDBZfBdUfsBTg==} engines: {node: '>=8'} - '@sentry/browser@7.119.0': - resolution: {integrity: sha512-WwmW1Y4D764kVGeKmdsNvQESZiAn9t8LmCWO0ucBksrjL2zw9gBPtOpRcO6l064sCLeSxxzCN+kIxhRm1gDFEA==} + '@sentry/browser@7.120.2': + resolution: {integrity: sha512-o5ll2Yv5MfnblbWxTvMlKK3RVXIbeJ+SPC+uw12b4j/pkrQg+/y7dyTLBXh6t0EgokSOsRUyYosQwSvxG/xs/Q==} engines: {node: '>=8'} '@sentry/cli@1.77.3': @@ -6313,12 +5922,16 @@ packages: resolution: {integrity: sha512-CS2kUv9rAJJEjiRat6wle3JATHypB0SyD7pt4cpX5y0dN5dZ1JrF57oLHRMnga9fxRivydHz7tMTuBhSSwhzjw==} engines: {node: '>=8'} + '@sentry/core@7.120.2': + resolution: {integrity: sha512-eurLBFQJC7WWWYoEna25Z9I/GJjqAmH339tv52XP8sqXV7B5hRcHDcfrsT/UGHpU316M24p3lWhj0eimtCZ0SQ==} + engines: {node: '>=8'} + '@sentry/hub@6.19.7': resolution: {integrity: sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==} engines: {node: '>=6'} - '@sentry/integrations@7.119.0': - resolution: {integrity: sha512-OHShvtsRW0A+ZL/ZbMnMqDEtJddPasndjq+1aQXw40mN+zeP7At/V1yPZyFaURy86iX7Ucxw5BtmzuNy7hLyTA==} + '@sentry/integrations@7.120.2': + resolution: {integrity: sha512-bMvL2fD3TGLM5YAUoQ2Qz6bYeVU8f7YRFNSjKNxK4EbvFgAU9j1FD6EKg0V0RNOJYnJjGIZYMmcWTXBbVTJL6w==} engines: {node: '>=8'} '@sentry/minimal@6.19.7': @@ -6335,8 +5948,8 @@ packages: peerDependencies: react: 15.x || 16.x || 17.x || 18.x - '@sentry/replay@7.119.0': - resolution: {integrity: sha512-BnNsYL+X5I4WCH6wOpY6HQtp4MgVt0NVlhLUsEyrvMUiTs0bPkDBrulsgZQBUKJsbOr3l9nHrFoNVB/0i6WNLA==} + '@sentry/replay@7.120.2': + resolution: {integrity: sha512-UAw0anHwXeej0Rfx+7qZVbRb3rj3mQ0DuHqdpPTSd9WpfRGOvCfthMIKD7rfHX7GjMnPMgdmsaU8K3lpe60h+w==} engines: {node: '>=12'} '@sentry/types@6.19.7': @@ -6347,6 +5960,10 @@ packages: resolution: {integrity: sha512-27qQbutDBPKGbuJHROxhIWc1i0HJaGLA90tjMu11wt0E4UNxXRX+UQl4Twu68v4EV3CPvQcEpQfgsViYcXmq+w==} engines: {node: '>=8'} + '@sentry/types@7.120.2': + resolution: {integrity: sha512-FWVoiblHQJ892GaOqdXx/5/n5XDLF28z81vJ0lCY49PMh8waz8LJ0b9RSmt9tasSDl0OQ7eUlPl1xu1jTrv1NA==} + engines: {node: '>=8'} + '@sentry/utils@6.19.7': resolution: {integrity: sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==} engines: {node: '>=6'} @@ -6355,6 +5972,10 @@ packages: resolution: {integrity: sha512-ZwyXexWn2ZIe2bBoYnXJVPc2esCSbKpdc6+0WJa8eutXfHq3FRKg4ohkfCBpfxljQGEfP1+kfin945lA21Ka+A==} engines: {node: '>=8'} + '@sentry/utils@7.120.2': + resolution: {integrity: sha512-jgnQlw11mRfQrQRAXbq4zEd+tbYwHel5eqeS/oU6EImXRjmHNtS79nB8MHvJeQu1FMCpFs1Ymrrs5FICwS6VeQ==} + engines: {node: '>=8'} + '@sentry/webpack-plugin@1.21.0': resolution: {integrity: sha512-x0PYIMWcsTauqxgl7vWUY6sANl+XGKtx7DCVnnY7aOIIlIna0jChTAPANTfA2QrK+VK+4I/4JxatCEZBnXh3Og==} engines: {node: '>= 8'} @@ -7723,14 +7344,14 @@ packages: '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} - '@volar/language-core@1.11.1': - resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + '@volar/language-core@2.4.11': + resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} - '@volar/source-map@1.11.1': - resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + '@volar/source-map@2.4.11': + resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} - '@volar/typescript@1.11.1': - resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + '@volar/typescript@2.4.11': + resolution: {integrity: sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==} '@vue/compiler-core@3.5.9': resolution: {integrity: sha512-KE1sCdwqSKq0CQ/ltg3XnlMTKeinjegIkuFsuq9DKvNPmqLGdmI51ChZdGBBRXIvEYTLm8X/JxOuBQ1HqF/+PA==} @@ -7738,8 +7359,11 @@ packages: '@vue/compiler-dom@3.5.9': resolution: {integrity: sha512-gEAURwPo902AsJF50vl59VaWR+Cx6cX9SoqLYHu1jq9hDbmQlXvpZyYNIIbxa2JTJ+FD/oBQweVUwuTQv79KTg==} - '@vue/language-core@1.8.27': - resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/language-core@2.1.6': + resolution: {integrity: sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -7938,19 +7562,24 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - addons-linter@6.28.0: - resolution: {integrity: sha512-fCTjXL/yG4hwq74JG8tQdrvEu0OvGrEN9yU+Df0020RDtHl3g/tTCyMeC4G1uyk8IuyMzp4myCBNnOGC7MWSQQ==} - engines: {node: '>=16.0.0'} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + addons-linter@7.1.0: + resolution: {integrity: sha512-UmkUB3dZSpf8bqhlulGDbpoxBwcfOk8JvaJTRvJ+AAXKHvTjqeNlFC+GRMqa0EjJJh/0yqpBaJzyaUIx+fjl+A==} + engines: {node: '>=18.0.0'} hasBin: true addons-moz-compare@1.3.0: resolution: {integrity: sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==} - addons-scanner-utils@9.10.1: - resolution: {integrity: sha512-Tz9OUQx9Ja0TyQ+H2GakB9KlJ50myI6ESBGRlA8N80nHBzMjjPRFGm0APADSaCd5NP74SrFtEvL4TRpDwZXETA==} + addons-scanner-utils@9.11.0: + resolution: {integrity: sha512-X95V8ymnue9EHmOLz3zJTGHvHDFlWKiavlH+kJKOlv2sJDWFvD3TWeJMHJgxS9GKOqT/545mOXvX3vuuGGum+g==} peerDependencies: body-parser: '>=1.20.3' - express: 4.18.3 + express: 4.19.2 node-fetch: 2.6.11 safe-compare: 1.1.4 peerDependenciesMeta: @@ -7987,9 +7616,20 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + ajv-keywords@3.5.2: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} peerDependencies: @@ -8166,17 +7806,10 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - asn1js@3.0.5: resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} engines: {node: '>=12.0.0'} - assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} @@ -8199,9 +7832,6 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true - async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - async@3.2.2: resolution: {integrity: sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==} @@ -8216,6 +7846,9 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + atomically@2.0.3: + resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} + auto-bind@4.0.0: resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} engines: {node: '>=8'} @@ -8231,18 +7864,12 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-sign2@0.7.0: - resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - - aws4@1.13.2: - resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - axe-core@4.10.0: resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} engines: {node: '>=4'} - axios@0.26.1: - resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + axios@0.28.0: + resolution: {integrity: sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==} axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} @@ -8336,9 +7963,6 @@ packages: batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - bech32@2.0.0: resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} @@ -8395,6 +8019,10 @@ packages: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + bplist-parser@0.2.0: resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} engines: {node: '>= 5.10.0'} @@ -8433,9 +8061,6 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -8448,10 +8073,9 @@ packages: builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} - bunyan@1.8.15: - resolution: {integrity: sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==} - engines: {'0': node >=0.10.0} - hasBin: true + bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} @@ -8519,6 +8143,10 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} @@ -8532,9 +8160,6 @@ packages: resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} hasBin: true - caseless@0.12.0: - resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -8634,8 +8259,8 @@ packages: '@chromatic-com/playwright': optional: true - chrome-launcher@0.15.1: - resolution: {integrity: sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg==} + chrome-launcher@1.1.2: + resolution: {integrity: sha512-YclTJey34KUm5jB1aEJCq807bSievi7Nb/TU4Gu504fUYi3jw3KCIaH6L7nFWQhdEgH3V+wCh+kKD1P5cXnfxw==} engines: {node: '>=12.13.0'} hasBin: true @@ -8656,9 +8281,6 @@ packages: class-variance-authority@0.7.0: resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} - classnames@2.3.2: - resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} - classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -8741,10 +8363,6 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - clsx@2.0.0: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} @@ -8863,6 +8481,9 @@ packages: commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} @@ -8896,6 +8517,9 @@ packages: confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -8903,6 +8527,10 @@ packages: resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} engines: {node: '>=12'} + configstore@7.0.0: + resolution: {integrity: sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==} + engines: {node: '>=18'} + connect-history-api-fallback@2.0.0: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} @@ -8969,15 +8597,9 @@ packages: core-js-pure@3.38.1: resolution: {integrity: sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==} - core-js@3.29.0: - resolution: {integrity: sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==} - core-js@3.38.1: resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} - core-util-is@1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -9348,18 +8970,10 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - dashdash@1.14.1: - resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} - engines: {node: '>=0.10'} - dashify@2.0.0: resolution: {integrity: sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A==} engines: {node: '>=4'} - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -9465,9 +9079,6 @@ packages: deep-object-diff@1.1.9: resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} - deepcopy@2.1.0: - resolution: {integrity: sha512-8cZeTb1ZKC3bdSCP6XOM1IsTczIO73fdqtwa2B0N15eAz7gmyhQo+mc5gnFuulsgN3vIQYmTgbmQVKalH1dKvQ==} - deepmerge@2.2.1: resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} engines: {node: '>=0.10.0'} @@ -9480,6 +9091,10 @@ packages: resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} engines: {node: '>=12'} + default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + default-gateway@6.0.3: resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} engines: {node: '>= 10'} @@ -9499,6 +9114,10 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -9627,15 +9246,6 @@ packages: peerDependencies: typedoc-plugin-markdown: '>=4.0.0' - docusaurus-theme-search-typesense@0.20.0-0: - resolution: {integrity: sha512-MW6fLJsZYfKKDRXC6pTe77OMhyQBishJO/L1M14hZymavKW2IyYFy+Mczp8TXL9QqvkLsW7Ja7YtnWXjmt5sCw==} - engines: {node: '>=18'} - peerDependencies: - '@docusaurus/core': 3.4.0 - '@docusaurus/theme-common': 3.4.0 - react: ^18.0.0 - react-dom: ^18.0.0 - dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -9689,6 +9299,10 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + dotenv-expand@10.0.0: resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} engines: {node: '>=12'} @@ -9705,10 +9319,6 @@ packages: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} - dtrace-provider@0.8.8: - resolution: {integrity: sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==} - engines: {node: '>=0.10'} - duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -9718,12 +9328,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ecc-jsbn@0.1.2: - resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -9846,10 +9450,6 @@ packages: es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - es6-promisify@7.0.0: - resolution: {integrity: sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q==} - engines: {node: '>=6'} - esbuild-plugin-alias@0.2.1: resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==} @@ -10003,11 +9603,6 @@ packages: peerDependencies: eslint: 8.57.1 - eslint-plugin-react-refresh@0.4.12: - resolution: {integrity: sha512-9neVjoGv20FwYtCP6CB1dzR1vr57ZDNOXst21wd2xJ/cTlM2xLq0GWVlSNTdMn/4BtP6cHYBMCSp1wFBJ9jBsg==} - peerDependencies: - eslint: 8.57.1 - eslint-plugin-react@7.37.0: resolution: {integrity: sha512-IHBePmfWH5lKhJnJ7WB1V+v/GolbB0rjS8XYVCSQCZKaQCAUhMoVoOEn1Ef8Z8Wf0a7l8KTJvuZg5/e4qrZ6nA==} engines: {node: '>=4'} @@ -10070,8 +9665,8 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true - espree@10.0.1: - resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==} + espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} espree@9.6.1: @@ -10170,6 +9765,10 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} + execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -10220,10 +9819,6 @@ packages: resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} hasBin: true - extsprintf@1.3.0: - resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} - engines: {'0': node >=0.6.0} - fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -10309,10 +9904,6 @@ packages: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - fetch-retry@5.0.6: resolution: {integrity: sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==} @@ -10390,8 +9981,9 @@ packages: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - firefox-profile@4.3.2: - resolution: {integrity: sha512-/C+Eqa0YgIsQT2p66p7Ygzqe7NlE/GNTbhw2SBCm5V3OsWDPITNdTPEcH2Q2fe7eMpYYNPKdUcuVioZBZiR6kA==} + firefox-profile@4.7.0: + resolution: {integrity: sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==} + engines: {node: '>=18'} hasBin: true first-chunk-stream@3.0.0: @@ -10429,9 +10021,6 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - forever-agent@0.6.1: - resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - fork-ts-checker-webpack-plugin@6.5.3: resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} @@ -10457,10 +10046,6 @@ packages: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} - form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - form-data@3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} @@ -10473,10 +10058,6 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - formidable@2.1.2: resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} @@ -10517,10 +10098,6 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - fs-extra@11.1.0: - resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} - engines: {node: '>=14.14'} - fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} @@ -10537,10 +10114,6 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} - fs-extra@9.0.1: - resolution: {integrity: sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==} - engines: {node: '>=10'} - fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -10636,9 +10209,6 @@ packages: get-tsconfig@4.8.1: resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - getpass@0.1.7: - resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - giget@1.2.3: resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==} hasBin: true @@ -10671,11 +10241,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - glob@10.4.1: - resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} - engines: {node: '>=16 || 14 >=14.18'} - hasBin: true - glob@10.4.2: resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==} engines: {node: '>=16 || 14 >=14.18'} @@ -10685,10 +10250,6 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@6.0.4: - resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} - deprecated: Glob versions prior to v9 are no longer supported - glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -10698,6 +10259,10 @@ packages: engines: {node: '>=12'} deprecated: Glob versions prior to v9 are no longer supported + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} @@ -10837,15 +10402,6 @@ packages: resolution: {integrity: sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==} engines: {node: '>=18.0.0'} - har-schema@2.0.0: - resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} - engines: {node: '>=4'} - - har-validator@5.1.5: - resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} - engines: {node: '>=6'} - deprecated: this library is no longer supported - has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -10890,6 +10446,9 @@ packages: hast-util-from-html-isomorphic@2.0.0: resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} + hast-util-from-html@2.0.1: + resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==} + hast-util-from-html@2.0.3: resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} @@ -10964,6 +10523,9 @@ packages: hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + html-dom-parser@5.0.10: + resolution: {integrity: sha512-GwArYL3V3V8yU/mLKoFF7HlLBv80BZ2Ey1BzfVNRpAci0cEKhFHI/Qh8o8oyt3qlAMLlK250wsxLdYX4viedvg==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -10984,6 +10546,15 @@ packages: engines: {node: ^14.13.1 || >=16.0.0} hasBin: true + html-react-parser@5.1.18: + resolution: {integrity: sha512-65BwC0zzrdeW96jB2FRr5f1ovBhRMpLPJNvwkY5kA8Ay5xdL9t/RH2/uUTM7p+cl5iM88i6dDk4LXtfMnRmaJQ==} + peerDependencies: + '@types/react': 0.14 || 15 || 16 || 17 || 18 + react: 0.14 || 15 || 16 || 17 || 18 + peerDependenciesMeta: + '@types/react': + optional: true + html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} @@ -11009,6 +10580,9 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -11047,10 +10621,6 @@ packages: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} - http-signature@1.2.0: - resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} - engines: {node: '>=0.8', npm: '>=1.3.7'} - http2-wrapper@2.2.1: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} @@ -11078,6 +10648,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -11186,6 +10760,14 @@ packages: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + inline-style-parser@0.1.1: resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} @@ -11331,6 +10913,11 @@ packages: engines: {node: '>=8'} hasBin: true + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -11376,10 +10963,24 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-installed-globally@0.4.0: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -11391,9 +10992,6 @@ packages: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} - is-mergeable-object@1.1.1: - resolution: {integrity: sha512-CPduJfuGg8h8vW74WOxHtHmtQutyQBzR+3MjQ6iDHIYdbOnm1YC7jv43SqCoU8OPGTJD4nibmiryA4kmogbGrA==} - is-nan@1.3.2: resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} engines: {node: '>= 0.4'} @@ -11433,6 +11031,10 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-plain-obj@3.0.0: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} @@ -11573,10 +11175,7 @@ packages: isomorphic-ws@5.0.0: resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: - ws: '*' - - isstream@0.1.2: - resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + ws: 8.18.0 istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} @@ -11774,8 +11373,8 @@ packages: joi@17.13.3: resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} - jose@4.13.1: - resolution: {integrity: sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==} + jose@5.9.2: + resolution: {integrity: sha512-ILI2xx/I57b20sd7rHZvgiiQrmp2mcotwsAH+5ajbpFQbrYVQdNHYlQhoA5cFb78CgtBOxtC05TeA+mcgkuCqQ==} jose@5.9.3: resolution: {integrity: sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==} @@ -11791,9 +11390,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsbn@0.1.1: - resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jscodeshift@0.15.2: resolution: {integrity: sha512-FquR7Okgmc4Sd0aEDwqho3rEiKR3BdvuG9jfdHjLJ6JQoWSMpavug3AoIfnfWhxFlf+5pzQh8qjqz0DWFrNQzA==} hasBin: true @@ -11833,6 +11429,10 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + json-rpc-engine@6.1.0: resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} engines: {node: '>=10.0.0'} @@ -11850,15 +11450,9 @@ packages: json-schema-typed@7.0.3: resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json-to-pretty-yaml@1.2.2: resolution: {integrity: sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==} engines: {node: '>= 0.2.0'} @@ -11884,14 +11478,6 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - jsonwebtoken@9.0.0: - resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==} - engines: {node: '>=12', npm: '>=6'} - - jsprim@1.4.2: - resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} - engines: {node: '>=0.6.0'} - jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -11899,12 +11485,6 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} - - jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - katex@0.16.11: resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} hasBin: true @@ -11930,6 +11510,10 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + ky@1.7.3: + resolution: {integrity: sha512-Sz49ZWR/BjNOq+2UK1k9ONZUVq8eyuCj30Zgc8VrRNtFlTBZduzuvehUd5kjQF6/Fms3Ir3EYqzJryw9tRvsSw==} + engines: {node: '>=18'} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -11941,6 +11525,10 @@ packages: resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} engines: {node: '>=14.16'} + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} + launch-editor@2.9.1: resolution: {integrity: sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==} @@ -11973,8 +11561,8 @@ packages: lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - lighthouse-logger@1.4.2: - resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} + lighthouse-logger@2.0.1: + resolution: {integrity: sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==} lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} @@ -12024,6 +11612,10 @@ packages: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + localforage@1.10.0: resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} @@ -12052,11 +11644,11 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + lodash.deburr@4.1.0: + resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} - lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -12073,6 +11665,9 @@ packages: lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -12096,10 +11691,6 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -12659,6 +12250,9 @@ packages: mlly@1.7.1: resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.3: + resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} + modern-ahocorasick@1.0.1: resolution: {integrity: sha512-yoe+JbhTClckZ67b2itRtistFKf8yPYelHLc7e5xAwtNAXxM6wJTUx2C7QeVSJFDzKT7bCIFyBVybPMKvmB9AA==} @@ -12689,8 +12283,8 @@ packages: typescript: optional: true - muggle-string@0.3.1: - resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} multer@1.4.4-lts.1: resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} @@ -12711,23 +12305,16 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - mv@2.1.1: - resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} - engines: {node: '>=0.8.0'} - mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nan@2.20.0: - resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.0.7: - resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==} + nanoid@5.0.9: + resolution: {integrity: sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==} engines: {node: ^18 || >=20} hasBin: true @@ -12740,10 +12327,6 @@ packages: natural-orderby@2.0.3: resolution: {integrity: sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==} - ncp@2.0.0: - resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} - hasBin: true - negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -12751,8 +12334,8 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next@14.2.10: - resolution: {integrity: sha512-sDDExXnh33cY3RkS9JuFEKaS4HmlWmDKP1VJioucCG6z5KuA008DPsDZOzi8UfqEk3Ii+2NCQSJrfbEWtZZfww==} + next@14.2.15: + resolution: {integrity: sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -12782,10 +12365,6 @@ packages: resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} engines: {node: '>= 0.10.5'} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} @@ -12805,10 +12384,6 @@ packages: encoding: optional: true - node-fetch@3.3.1: - resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} @@ -12873,9 +12448,6 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true - oauth-sign@0.9.0: - resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -12968,6 +12540,10 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -13069,6 +12645,10 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + package-json@8.1.1: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} @@ -13112,9 +12692,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse-json@6.0.2: - resolution: {integrity: sha512-SA5aMiaIjXkAiBrW/yPgLgQAQg42f7K3ACO+2l/zOvtQBwX58DMUsFJXelW2fx3yMBmWOVkR6j1MGsdSbCA4UA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + parse-json@7.1.1: + resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} + engines: {node: '>=16'} parse-numeric-range@1.3.0: resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} @@ -13189,8 +12769,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@0.1.10: - resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} path-to-regexp@1.9.0: resolution: {integrity: sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==} @@ -13221,9 +12801,6 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} @@ -13264,10 +12841,17 @@ packages: pino-std-serializers@6.2.2: resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + pino@8.20.0: resolution: {integrity: sha512-uhIfMj5TVp+WynVASaVEJFTncTUe4dHBq6CWplu/vBgvGHhvBvQfxz+vcOrnnBQdORH3izaGEurLfNlq3YxdFQ==} hasBin: true + pino@9.4.0: + resolution: {integrity: sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==} + hasBin: true + pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -13294,6 +12878,9 @@ packages: pkg-types@1.2.0: resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + pkg-types@1.2.1: + resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} + pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} @@ -13899,6 +13486,9 @@ packages: process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + process-warning@4.0.0: + resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -13999,10 +13589,6 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} - qs@6.5.3: - resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} - engines: {node: '>=0.6'} - querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -14166,6 +13752,9 @@ packages: react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-property@2.0.2: + resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} + react-redux@8.1.3: resolution: {integrity: sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==} peerDependencies: @@ -14191,16 +13780,6 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} - react-remove-scroll-bar@2.3.4: - resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - react-remove-scroll-bar@2.3.6: resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} engines: {node: '>=10'} @@ -14422,9 +14001,6 @@ packages: regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} - regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -14465,6 +14041,10 @@ packages: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true + rehype-jargon@3.1.0: + resolution: {integrity: sha512-DTvSVI0MPa9Gen7oiob71ROG+ztagQ2KIDbM/1vUsZvrh0f8G2YyxrNoS4AL0WQHbHwwNzg/wLz301B3u+Tq7Q==} + engines: {node: '>=18', npm: '>=9'} + rehype-katex@7.0.1: resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} @@ -14537,11 +14117,6 @@ packages: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} - request@2.88.2: - resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} - engines: {node: '>= 6'} - deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -14621,11 +14196,6 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@2.4.5: - resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -14669,6 +14239,10 @@ packages: engines: {node: '>=12.0.0'} hasBin: true + run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -14700,9 +14274,6 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-json-stringify@1.2.0: - resolution: {integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==} - safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} @@ -14786,11 +14357,6 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true - semver@7.6.3: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} @@ -14840,10 +14406,6 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -14883,10 +14445,6 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - sign-addon@5.3.0: - resolution: {integrity: sha512-7nHlCzhQgVMLBNiXVEgbG/raq48awOW0lYMN5uo1BaB3mp0+k8M8pvDwbfTlr3apcxZJsk9HQsAW1POwoJugpQ==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -14951,6 +14509,9 @@ packages: sonic-boom@3.8.1: resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + sort-css-media-queries@2.2.0: resolution: {integrity: sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==} engines: {node: '>= 6.3.0'} @@ -15035,11 +14596,6 @@ packages: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} - sshpk@1.18.0: - resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} - engines: {node: '>=0.10.0'} - hasBin: true - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -15090,13 +14646,6 @@ packages: stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - stream-to-array@2.3.0: - resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} - - stream-to-promise@3.0.0: - resolution: {integrity: sha512-h+7wLeFiYegOdgTfTxjRsrT7/Op7grnKEIHWgaO1RTHwcwk7xRreMr3S8XpDfDMesSxzgM2V4CxNCFAGo6ssnA==} - engines: {node: '>= 10'} - streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -15221,10 +14770,16 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-json-comments@5.0.0: - resolution: {integrity: sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==} + strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} + stubborn-fs@1.2.5: + resolution: {integrity: sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==} + + style-to-js@1.1.16: + resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} + style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} @@ -15413,6 +14968,9 @@ packages: thread-stream@2.7.0: resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + throttle-debounce@5.0.2: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} engines: {node: '>=12.22'} @@ -15463,14 +15021,14 @@ packages: title-case@3.0.3: resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} - tmp@0.2.1: - resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} - engines: {node: '>=8.17.0'} - tmp@0.2.3: resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} engines: {node: '>=14.14'} @@ -15502,18 +15060,10 @@ packages: toposort@2.0.2: resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} - tosource@1.0.0: - resolution: {integrity: sha512-N6g8eQ1eerw6Y1pBhdgkubWIiPFwXa2POSUrlL8jth5CyyEWNWzoGKRkO3CaO7Jx27hlJP54muB3btIAbx4MPg==} - engines: {node: '>=0.4.0'} - totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - tough-cookie@2.5.0: - resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} - engines: {node: '>=0.8'} - tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -15683,9 +15233,6 @@ packages: tween-functions@1.2.0: resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==} - tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} @@ -15697,10 +15244,6 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - type-fest@0.16.0: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} @@ -15729,6 +15272,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + type-fest@4.26.1: resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} engines: {node: '>=16'} @@ -15811,35 +15358,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - typesense-docsearch-css@0.4.1: - resolution: {integrity: sha512-mN8K18pfIpCrhzsMAJBzoS7l/YDcC4P3f9vsScenUceXmC8n3FCPldmF10dKDJmK3Lr7aAScQt70jCA5126y2w==} - - typesense-docsearch-react@3.4.1: - resolution: {integrity: sha512-d0PQym/B/p5oP+hfdFEOH6goiKa1JLR63bikZSDGq1+jT2FtuwNrdMGVBZZMNFUsXVsJRA8ULHJpsREmfSJmVw==} - peerDependencies: - '@types/react': '>= 16.8.0 < 19.0.0' - react: '>= 16.8.0 < 19.0.0' - react-dom: '>= 16.8.0 < 19.0.0' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - react-dom: - optional: true - - typesense-instantsearch-adapter@2.8.0: - resolution: {integrity: sha512-2q4QVpHoUV0ncf1XOqIC0dufOTkFRxQ0mHzg//H3WK02ZYqdNNPCAacZODhQlltl1cNJdTI8Y4uuGVd6fJuGzw==} - engines: {node: '>=16'} - peerDependencies: - '@babel/runtime': ^7.17.2 - - typesense@1.8.2: - resolution: {integrity: sha512-aBpePjA99Qvo+OP2pJwMpvga4Jrm1Y2oV5NsrWXBxlqUDNEUCPZBIksPv2Hq0jxQxHhLLyJVbjXjByXsvpCDVA==} - engines: {node: '>=18'} - peerDependencies: - '@babel/runtime': ^7.23.2 - ua-parser-js@1.0.39: resolution: {integrity: sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==} hasBin: true @@ -15959,10 +15477,6 @@ packages: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} - universalify@1.0.0: - resolution: {integrity: sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==} - engines: {node: '>= 10.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -16002,6 +15516,10 @@ packages: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} engines: {node: '>=14.16'} + update-notifier@7.3.1: + resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} + engines: {node: '>=18'} + upper-case-first@2.0.2: resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} @@ -16110,11 +15628,6 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true - uuid@3.4.0: - resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -16144,10 +15657,6 @@ packages: validate-npm-package-name@3.0.0: resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} - validator@13.12.0: - resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} - engines: {node: '>= 0.10'} - value-equal@1.0.1: resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==} @@ -16162,10 +15671,6 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - verror@1.10.0: - resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - engines: {'0': node >=0.6.0} - vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -16191,8 +15696,8 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite-plugin-dts@3.9.1: - resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} + vite-plugin-dts@4.3.0: + resolution: {integrity: sha512-LkBJh9IbLwL6/rxh0C1/bOurDrIEmRE7joC+jFdOEEciAFPbpEKOLSAr5nNh5R7CJ45cMbksTrFfy52szzC5eA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -16270,19 +15775,13 @@ packages: jsdom: optional: true + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + vue-parser@1.1.6: resolution: {integrity: sha512-v3/R7PLbaFVF/c8IIzWs1HgRpT2gN0dLRkaLIT5q+zJGVgmhN4VuZJF4Y9N4hFtFjS4B1EHxAOP6/tzqM4Ug2g==} engines: {node: '>= 4.0.0', npm: '>= 3.0.0'} - vue-template-compiler@2.7.16: - resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} - - vue-tsc@1.8.27: - resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} - hasBin: true - peerDependencies: - typescript: '*' - w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -16304,10 +15803,6 @@ packages: resolution: {integrity: sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==} hasBin: true - watchpack@2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} - engines: {node: '>=10.13.0'} - watchpack@2.4.2: resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} engines: {node: '>=10.13.0'} @@ -16318,9 +15813,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-ext@7.12.0: - resolution: {integrity: sha512-h+uWOYBlHlPKy5CqxuZKocgOdL8J7I4ctMw/rAGbQl7jq7tr+NmY/Lhh2FPMSlJ1Y0T2VeUqwBVighK0MM1+zA==} - engines: {node: '>=14.0.0', npm: '>=6.9.0'} + web-ext@8.3.0: + resolution: {integrity: sha512-mXSOiDtmm3n0KNpSuQ65fJpypAoNLAmZv3QkdlVbJ6etn0BK+hl/k+tjHefSIKdbzGUIeFbhn2oxWMe9Tdyrdg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true web-namespaces@2.0.1: @@ -16538,6 +16033,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + when-exit@2.1.3: + resolution: {integrity: sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw==} + when@3.7.7: resolution: {integrity: sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==} @@ -16585,6 +16083,10 @@ packages: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} @@ -16627,41 +16129,6 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@6.2.3: - resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.13.0: - resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -16686,8 +16153,8 @@ packages: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} - xml2js@0.5.0: - resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} xmlbuilder@11.0.1: @@ -16734,10 +16201,6 @@ packages: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} - yargs@17.7.1: - resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} - engines: {node: '>=12'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -16764,11 +16227,6 @@ packages: yup@1.4.0: resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} - z-schema@5.0.5: - resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} - engines: {node: '>=8.0.0'} - hasBin: true - zip-dir@2.0.0: resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==} @@ -16812,10 +16270,6 @@ snapshots: '@adraffy/ens-normalize@1.11.0': {} - '@algolia/autocomplete-core@1.8.2': - dependencies: - '@algolia/autocomplete-shared': 1.8.2 - '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': dependencies: '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) @@ -16832,20 +16286,12 @@ snapshots: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.8.2(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': - dependencies: - '@algolia/autocomplete-shared': 1.8.2 - '@algolia/client-search': 4.24.0 - algoliasearch: 4.24.0 - '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': dependencies: '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) '@algolia/client-search': 4.24.0 algoliasearch: 4.24.0 - '@algolia/autocomplete-shared@1.8.2': {} - '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': dependencies: '@algolia/client-search': 4.24.0 @@ -17130,6 +16576,12 @@ snapshots: transitivePeerDependencies: - encoding + '@artsy/to-title-case@1.1.0': + dependencies: + lodash.deburr: 4.1.0 + lodash.isnumber: 3.0.3 + lodash.upperfirst: 4.3.1 + '@aw-web-design/x-default-browser@1.4.126': dependencies: default-browser-id: 3.0.0 @@ -18021,10 +17473,6 @@ snapshots: core-js-pure: 3.38.1 regenerator-runtime: 0.14.1 - '@babel/runtime@7.21.0': - dependencies: - regenerator-runtime: 0.13.11 - '@babel/runtime@7.25.6': dependencies: regenerator-runtime: 0.14.1 @@ -18481,7 +17929,7 @@ snapshots: '@devicefarmer/adbkit-monkey@1.2.1': {} - '@devicefarmer/adbkit@3.2.3': + '@devicefarmer/adbkit@3.2.6': dependencies: '@devicefarmer/adbkit-logcat': 2.1.3 '@devicefarmer/adbkit-monkey': 1.2.1 @@ -18510,97 +17958,6 @@ snapshots: transitivePeerDependencies: - '@algolia/client-search' - '@docusaurus/core@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16)': - dependencies: - '@babel/core': 7.25.2 - '@babel/generator': 7.25.6 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-runtime': 7.25.4(@babel/core@7.25.2) - '@babel/preset-env': 7.25.4(@babel/core@7.25.2) - '@babel/preset-react': 7.24.7(@babel/core@7.25.2) - '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) - '@babel/runtime': 7.25.6 - '@babel/runtime-corejs3': 7.25.6 - '@babel/traverse': 7.25.6 - '@docusaurus/cssnano-preset': 3.4.0 - '@docusaurus/logger': 3.4.0 - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - autoprefixer: 10.4.20(postcss@8.4.47) - babel-loader: 9.2.1(@babel/core@7.25.2)(webpack@5.95.0) - babel-plugin-dynamic-import-node: 2.3.3 - boxen: 6.2.1 - chalk: 4.1.2 - chokidar: 3.6.0 - clean-css: 5.3.3 - cli-table3: 0.6.5 - combine-promises: 1.2.0 - commander: 5.1.0 - copy-webpack-plugin: 11.0.0(webpack@5.95.0) - core-js: 3.38.1 - css-loader: 6.11.0(webpack@5.95.0) - css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.95.0) - cssnano: 6.1.2(postcss@8.4.47) - del: 6.1.1 - detect-port: 1.6.1 - escape-html: 1.0.3 - eta: 2.2.0 - eval: 0.1.8 - file-loader: 6.2.0(webpack@5.95.0) - fs-extra: 11.2.0 - html-minifier-terser: 7.2.0 - html-tags: 3.3.1 - html-webpack-plugin: 5.6.0(webpack@5.95.0) - leven: 3.1.0 - lodash: 4.17.21 - mini-css-extract-plugin: 2.9.1(webpack@5.95.0) - p-map: 4.0.0 - postcss: 8.4.47 - postcss-loader: 7.3.4(postcss@8.4.47)(typescript@5.6.2)(webpack@5.95.0) - prompts: 2.4.2 - react: 18.3.1 - react-dev-utils: 12.0.1(eslint@8.57.1)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.95.0) - react-dom: 18.3.1(react@18.3.1) - react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.95.0) - react-router: 5.3.4(react@18.3.1) - react-router-config: 5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1) - react-router-dom: 5.3.4(react@18.3.1) - rtl-detect: 1.1.2 - semver: 7.6.3 - serve-handler: 6.1.5 - shelljs: 0.8.5 - terser-webpack-plugin: 5.3.10(webpack@5.95.0) - tslib: 2.7.0 - update-notifier: 6.0.2 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.95.0))(webpack@5.95.0) - webpack: 5.95.0 - webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 4.15.2(webpack@5.95.0) - webpack-merge: 5.10.0 - webpackbar: 5.0.2(webpack@5.95.0) - transitivePeerDependencies: - - '@docusaurus/types' - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@swc/css' - - bufferutil - - csso - - debug - - esbuild - - eslint - - lightningcss - - supports-color - - typescript - - uglify-js - - utf-8-validate - - vue-template-compiler - - webpack-cli - '@docusaurus/core@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16)': dependencies: '@babel/core': 7.25.2 @@ -18693,13 +18050,6 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/cssnano-preset@3.4.0': - dependencies: - cssnano-preset-advanced: 6.1.2(postcss@8.4.47) - postcss: 8.4.47 - postcss-sort-media-queries: 5.2.0(postcss@8.4.47) - tslib: 2.7.0 - '@docusaurus/cssnano-preset@3.5.2': dependencies: cssnano-preset-advanced: 6.1.2(postcss@8.4.47) @@ -18707,53 +18057,11 @@ snapshots: postcss-sort-media-queries: 5.2.0(postcss@8.4.47) tslib: 2.7.0 - '@docusaurus/logger@3.4.0': - dependencies: - chalk: 4.1.2 - tslib: 2.7.0 - '@docusaurus/logger@3.5.2': dependencies: chalk: 4.1.2 tslib: 2.7.0 - '@docusaurus/mdx-loader@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': - dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@mdx-js/mdx': 3.0.1 - '@slorber/remark-comment': 1.0.0 - escape-html: 1.0.3 - estree-util-value-to-estree: 3.1.2 - file-loader: 6.2.0(webpack@5.95.0) - fs-extra: 11.2.0 - image-size: 1.1.1 - mdast-util-mdx: 3.0.0 - mdast-util-to-string: 4.0.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - rehype-raw: 7.0.0 - remark-directive: 3.0.0 - remark-emoji: 4.0.1 - remark-frontmatter: 5.0.0 - remark-gfm: 4.0.0 - stringify-object: 3.3.0 - tslib: 2.7.0 - unified: 11.0.5 - unist-util-visit: 5.0.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.95.0))(webpack@5.95.0) - vfile: 6.0.3 - webpack: 5.95.0 - transitivePeerDependencies: - - '@docusaurus/types' - - '@swc/core' - - esbuild - - supports-color - - typescript - - uglify-js - - webpack-cli - '@docusaurus/mdx-loader@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': dependencies: '@docusaurus/logger': 3.5.2 @@ -18791,24 +18099,6 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/module-type-aliases@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/history': 4.7.11 - '@types/react': 18.3.9 - '@types/react-router-config': 5.0.11 - '@types/react-router-dom': 5.3.3 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-helmet-async: 2.0.5(react@18.3.1) - react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - transitivePeerDependencies: - - '@swc/core' - - esbuild - - supports-color - - uglify-js - - webpack-cli - '@docusaurus/module-type-aliases@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -18902,44 +18192,6 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-docs@3.4.0(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16)': - dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16) - '@docusaurus/logger': 3.4.0 - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@types/react-router-config': 5.0.11 - combine-promises: 1.2.0 - fs-extra: 11.2.0 - js-yaml: 4.1.0 - lodash: 4.17.21 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - tslib: 2.7.0 - utility-types: 3.11.0 - webpack: 5.95.0 - transitivePeerDependencies: - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@swc/css' - - bufferutil - - csso - - debug - - esbuild - - eslint - - lightningcss - - supports-color - - typescript - - uglify-js - - utf-8-validate - - vue-template-compiler - - webpack-cli - '@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16)': dependencies: '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16) @@ -19390,11 +18642,6 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-translations@3.4.0': - dependencies: - fs-extra: 11.2.0 - tslib: 2.7.0 - '@docusaurus/theme-translations@3.5.2': dependencies: fs-extra: 11.2.0 @@ -19402,26 +18649,6 @@ snapshots: '@docusaurus/tsconfig@3.5.2': {} - '@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@mdx-js/mdx': 3.0.1 - '@types/history': 4.7.11 - '@types/react': 18.3.9 - commander: 5.1.0 - joi: 17.13.3 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - utility-types: 3.11.0 - webpack: 5.95.0 - webpack-merge: 5.10.0 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - supports-color - - uglify-js - - webpack-cli - '@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@mdx-js/mdx': 3.0.1 @@ -19442,62 +18669,12 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/utils-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': - dependencies: - tslib: 2.7.0 - optionalDependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - - '@docusaurus/utils-common@3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': - dependencies: - tslib: 2.7.0 - optionalDependencies: - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-common@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: tslib: 2.7.0 optionalDependencies: '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': - dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - fs-extra: 11.2.0 - joi: 17.13.3 - js-yaml: 4.1.0 - lodash: 4.17.21 - tslib: 2.7.0 - transitivePeerDependencies: - - '@docusaurus/types' - - '@swc/core' - - esbuild - - supports-color - - typescript - - uglify-js - - webpack-cli - - '@docusaurus/utils-validation@3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': - dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - fs-extra: 11.2.0 - joi: 17.13.3 - js-yaml: 4.1.0 - lodash: 4.17.21 - tslib: 2.7.0 - transitivePeerDependencies: - - '@docusaurus/types' - - '@swc/core' - - esbuild - - supports-color - - typescript - - uglify-js - - webpack-cli - '@docusaurus/utils-validation@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': dependencies: '@docusaurus/logger': 3.5.2 @@ -19517,70 +18694,6 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/utils@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': - dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@svgr/webpack': 8.1.0(typescript@5.6.2) - escape-string-regexp: 4.0.0 - file-loader: 6.2.0(webpack@5.95.0) - fs-extra: 11.2.0 - github-slugger: 1.5.0 - globby: 11.1.0 - gray-matter: 4.0.3 - jiti: 1.21.6 - js-yaml: 4.1.0 - lodash: 4.17.21 - micromatch: 4.0.8 - prompts: 2.4.2 - resolve-pathname: 3.0.0 - shelljs: 0.8.5 - tslib: 2.7.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.95.0))(webpack@5.95.0) - utility-types: 3.11.0 - webpack: 5.95.0 - optionalDependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - transitivePeerDependencies: - - '@swc/core' - - esbuild - - supports-color - - typescript - - uglify-js - - webpack-cli - - '@docusaurus/utils@3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': - dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@svgr/webpack': 8.1.0(typescript@5.6.2) - escape-string-regexp: 4.0.0 - file-loader: 6.2.0(webpack@5.95.0) - fs-extra: 11.2.0 - github-slugger: 1.5.0 - globby: 11.1.0 - gray-matter: 4.0.3 - jiti: 1.21.6 - js-yaml: 4.1.0 - lodash: 4.17.21 - micromatch: 4.0.8 - prompts: 2.4.2 - resolve-pathname: 3.0.0 - shelljs: 0.8.5 - tslib: 2.7.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.95.0))(webpack@5.95.0) - utility-types: 3.11.0 - webpack: 5.95.0 - optionalDependencies: - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - transitivePeerDependencies: - - '@swc/core' - - esbuild - - supports-color - - typescript - - uglify-js - - webpack-cli - '@docusaurus/utils@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': dependencies: '@docusaurus/logger': 3.5.2 @@ -20956,7 +20069,7 @@ snapshots: jju: 1.4.0 js-yaml: 4.1.0 - '@mdn/browser-compat-data@5.5.29': {} + '@mdn/browser-compat-data@5.6.0': {} '@mdx-js/mdx@3.0.1': dependencies: @@ -21025,23 +20138,23 @@ snapshots: '@metamask/safe-event-emitter@3.1.1': {} - '@microsoft/api-extractor-model@7.28.13(@types/node@20.16.9)': + '@microsoft/api-extractor-model@7.30.1(@types/node@20.16.9)': dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.16.9) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.10.1(@types/node@20.16.9) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.43.0(@types/node@20.16.9)': + '@microsoft/api-extractor@7.48.1(@types/node@20.16.9)': dependencies: - '@microsoft/api-extractor-model': 7.28.13(@types/node@20.16.9) - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.16.9) - '@rushstack/rig-package': 0.5.2 - '@rushstack/terminal': 0.10.0(@types/node@20.16.9) - '@rushstack/ts-command-line': 4.19.1(@types/node@20.16.9) + '@microsoft/api-extractor-model': 7.30.1(@types/node@20.16.9) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.10.1(@types/node@20.16.9) + '@rushstack/rig-package': 0.5.3 + '@rushstack/terminal': 0.14.4(@types/node@20.16.9) + '@rushstack/ts-command-line': 4.23.2(@types/node@20.16.9) lodash: 4.17.21 minimatch: 3.0.8 resolve: 1.22.8 @@ -21058,8 +20171,17 @@ snapshots: jju: 1.4.0 resolve: 1.19.0 + '@microsoft/tsdoc-config@0.17.1': + dependencies: + '@microsoft/tsdoc': 0.15.1 + ajv: 8.12.0 + jju: 1.4.0 + resolve: 1.22.8 + '@microsoft/tsdoc@0.14.2': {} + '@microsoft/tsdoc@0.15.1': {} + '@mswjs/interceptors@0.35.8': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -21266,37 +20388,37 @@ snapshots: optionalDependencies: '@nestjs/platform-express': 10.4.4(@nestjs/common@10.4.4(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4) - '@next/env@14.2.10': {} + '@next/env@14.2.15': {} '@next/eslint-plugin-next@14.2.3': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.10': + '@next/swc-darwin-arm64@14.2.15': optional: true - '@next/swc-darwin-x64@14.2.10': + '@next/swc-darwin-x64@14.2.15': optional: true - '@next/swc-linux-arm64-gnu@14.2.10': + '@next/swc-linux-arm64-gnu@14.2.15': optional: true - '@next/swc-linux-arm64-musl@14.2.10': + '@next/swc-linux-arm64-musl@14.2.15': optional: true - '@next/swc-linux-x64-gnu@14.2.10': + '@next/swc-linux-x64-gnu@14.2.15': optional: true - '@next/swc-linux-x64-musl@14.2.10': + '@next/swc-linux-x64-musl@14.2.15': optional: true - '@next/swc-win32-arm64-msvc@14.2.10': + '@next/swc-win32-arm64-msvc@14.2.15': optional: true - '@next/swc-win32-ia32-msvc@14.2.10': + '@next/swc-win32-ia32-msvc@14.2.15': optional: true - '@next/swc-win32-x64-msvc@14.2.10': + '@next/swc-win32-x64-msvc@14.2.15': optional: true '@noble/curves@1.4.2': @@ -21613,14 +20735,10 @@ snapshots: '@popperjs/core@2.11.8': {} - '@radix-ui/colors@3.0.0': {} - '@radix-ui/number@1.0.1': dependencies: '@babel/runtime': 7.25.6 - '@radix-ui/number@1.1.0': {} - '@radix-ui/primitive@1.0.0': dependencies: '@babel/runtime': 7.25.6 @@ -21631,29 +20749,6 @@ snapshots: '@radix-ui/primitive@1.1.0': {} - '@radix-ui/react-accessible-icon@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - - '@radix-ui/react-alert-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -21673,43 +20768,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-aspect-ratio@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - - '@radix-ui/react-avatar@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - - '@radix-ui/react-checkbox@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-checkbox@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -21785,20 +20843,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.9 - '@radix-ui/react-context-menu@2.2.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-context@1.0.0(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -21977,41 +21021,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-form@0.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-label': 2.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - - '@radix-ui/react-hover-card@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - - '@radix-ui/react-icons@1.3.0(react@18.3.1)': - dependencies: - react: 18.3.1 - '@radix-ui/react-id@1.0.0(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -22221,34 +21230,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-progress@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - - '@radix-ui/react-radio-group@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -22266,23 +21247,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-scroll-area@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-select@1.2.2(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -22313,35 +21277,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-select@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - aria-hidden: 1.2.4 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.5.7(@types/react@18.3.9)(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-separator@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -22351,25 +21286,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-slider@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.9)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-slot@1.0.0(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -22463,26 +21379,6 @@ snapshots: '@types/react': 18.3.9 '@types/react-dom': 18.3.0 - '@radix-ui/react-tooltip@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@radix-ui/react-use-callback-ref@1.0.0(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -22629,49 +21525,6 @@ snapshots: '@radix-ui/rect@1.1.0': {} - '@radix-ui/themes@3.1.4(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@radix-ui/colors': 3.0.0 - '@radix-ui/primitive': 1.1.0 - '@radix-ui/react-accessible-icon': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-alert-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-aspect-ratio': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-avatar': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-checkbox': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-context-menu': 2.2.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-dropdown-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-form': 0.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-hover-card': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-navigation-menu': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-popover': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-progress': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-radio-group': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-scroll-area': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-select': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slider': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-slot': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-switch': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-tabs': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-toggle-group': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-tooltip': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.9)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - classnames: 2.3.2 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-remove-scroll-bar: 2.3.4(@types/react@18.3.9)(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.9 - '@types/react-dom': 18.3.0 - '@reduxjs/toolkit@1.9.7(react-redux@8.1.3(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1))(react@18.3.1)': dependencies: immer: 9.0.21 @@ -22746,32 +21599,34 @@ snapshots: '@rushstack/eslint-patch@1.10.4': {} - '@rushstack/node-core-library@4.0.2(@types/node@20.16.9)': + '@rushstack/node-core-library@5.10.1(@types/node@20.16.9)': dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1 fs-extra: 7.0.1 import-lazy: 4.0.0 jju: 1.4.0 resolve: 1.22.8 semver: 7.5.4 - z-schema: 5.0.5 optionalDependencies: '@types/node': 20.16.9 - '@rushstack/rig-package@0.5.2': + '@rushstack/rig-package@0.5.3': dependencies: resolve: 1.22.8 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.10.0(@types/node@20.16.9)': + '@rushstack/terminal@0.14.4(@types/node@20.16.9)': dependencies: - '@rushstack/node-core-library': 4.0.2(@types/node@20.16.9) + '@rushstack/node-core-library': 5.10.1(@types/node@20.16.9) supports-color: 8.1.1 optionalDependencies: '@types/node': 20.16.9 - '@rushstack/ts-command-line@4.19.1(@types/node@20.16.9)': + '@rushstack/ts-command-line@4.23.2(@types/node@20.16.9)': dependencies: - '@rushstack/terminal': 0.10.0(@types/node@20.16.9) + '@rushstack/terminal': 0.14.4(@types/node@20.16.9) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 @@ -22804,35 +21659,35 @@ snapshots: '@noble/hashes': 1.5.0 '@scure/base': 1.1.9 - '@sentry-internal/feedback@7.119.0': + '@sentry-internal/feedback@7.120.2': dependencies: - '@sentry/core': 7.119.0 - '@sentry/types': 7.119.0 - '@sentry/utils': 7.119.0 + '@sentry/core': 7.120.2 + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 - '@sentry-internal/replay-canvas@7.119.0': + '@sentry-internal/replay-canvas@7.120.2': dependencies: - '@sentry/core': 7.119.0 - '@sentry/replay': 7.119.0 - '@sentry/types': 7.119.0 - '@sentry/utils': 7.119.0 + '@sentry/core': 7.120.2 + '@sentry/replay': 7.120.2 + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 - '@sentry-internal/tracing@7.119.0': + '@sentry-internal/tracing@7.120.2': dependencies: - '@sentry/core': 7.119.0 - '@sentry/types': 7.119.0 - '@sentry/utils': 7.119.0 + '@sentry/core': 7.120.2 + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 - '@sentry/browser@7.119.0': + '@sentry/browser@7.120.2': dependencies: - '@sentry-internal/feedback': 7.119.0 - '@sentry-internal/replay-canvas': 7.119.0 - '@sentry-internal/tracing': 7.119.0 - '@sentry/core': 7.119.0 - '@sentry/integrations': 7.119.0 - '@sentry/replay': 7.119.0 - '@sentry/types': 7.119.0 - '@sentry/utils': 7.119.0 + '@sentry-internal/feedback': 7.120.2 + '@sentry-internal/replay-canvas': 7.120.2 + '@sentry-internal/tracing': 7.120.2 + '@sentry/core': 7.120.2 + '@sentry/integrations': 7.120.2 + '@sentry/replay': 7.120.2 + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 '@sentry/cli@1.77.3': dependencies: @@ -22859,17 +21714,22 @@ snapshots: '@sentry/types': 7.119.0 '@sentry/utils': 7.119.0 + '@sentry/core@7.120.2': + dependencies: + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 + '@sentry/hub@6.19.7': dependencies: '@sentry/types': 6.19.7 '@sentry/utils': 6.19.7 tslib: 1.14.1 - '@sentry/integrations@7.119.0': + '@sentry/integrations@7.120.2': dependencies: - '@sentry/core': 7.119.0 - '@sentry/types': 7.119.0 - '@sentry/utils': 7.119.0 + '@sentry/core': 7.120.2 + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 localforage: 1.10.0 '@sentry/minimal@6.19.7': @@ -22893,24 +21753,26 @@ snapshots: '@sentry/react@7.119.0(react@18.3.1)': dependencies: - '@sentry/browser': 7.119.0 + '@sentry/browser': 7.120.2 '@sentry/core': 7.119.0 '@sentry/types': 7.119.0 '@sentry/utils': 7.119.0 hoist-non-react-statics: 3.3.2 react: 18.3.1 - '@sentry/replay@7.119.0': + '@sentry/replay@7.120.2': dependencies: - '@sentry-internal/tracing': 7.119.0 - '@sentry/core': 7.119.0 - '@sentry/types': 7.119.0 - '@sentry/utils': 7.119.0 + '@sentry-internal/tracing': 7.120.2 + '@sentry/core': 7.120.2 + '@sentry/types': 7.120.2 + '@sentry/utils': 7.120.2 '@sentry/types@6.19.7': {} '@sentry/types@7.119.0': {} + '@sentry/types@7.120.2': {} + '@sentry/utils@6.19.7': dependencies: '@sentry/types': 6.19.7 @@ -22920,6 +21782,10 @@ snapshots: dependencies: '@sentry/types': 7.119.0 + '@sentry/utils@7.120.2': + dependencies: + '@sentry/types': 7.120.2 + '@sentry/webpack-plugin@1.21.0': dependencies: '@sentry/cli': 1.77.3 @@ -22982,7 +21848,7 @@ snapshots: '@size-limit/esbuild@11.1.5(size-limit@11.1.5)': dependencies: esbuild: 0.23.1 - nanoid: 5.0.7 + nanoid: 5.0.9 size-limit: 11.1.5 '@size-limit/file@11.1.5(size-limit@11.1.5)': @@ -24907,13 +23773,6 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.5)(vite@5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0))': - dependencies: - '@swc/core': 1.7.28(@swc/helpers@0.5.5) - vite: 5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0) - transitivePeerDependencies: - - '@swc/helpers' - '@vitejs/plugin-react@3.1.0(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0))': dependencies: '@babel/core': 7.25.2 @@ -25036,18 +23895,17 @@ snapshots: loupe: 3.1.1 tinyrainbow: 1.2.0 - '@volar/language-core@1.11.1': + '@volar/language-core@2.4.11': dependencies: - '@volar/source-map': 1.11.1 + '@volar/source-map': 2.4.11 - '@volar/source-map@1.11.1': - dependencies: - muggle-string: 0.3.1 + '@volar/source-map@2.4.11': {} - '@volar/typescript@1.11.1': + '@volar/typescript@2.4.11': dependencies: - '@volar/language-core': 1.11.1 + '@volar/language-core': 2.4.11 path-browserify: 1.0.1 + vscode-uri: 3.0.8 '@vue/compiler-core@3.5.9': dependencies: @@ -25062,17 +23920,21 @@ snapshots: '@vue/compiler-core': 3.5.9 '@vue/shared': 3.5.9 - '@vue/language-core@1.8.27(typescript@5.6.2)': + '@vue/compiler-vue2@2.7.16': dependencies: - '@volar/language-core': 1.11.1 - '@volar/source-map': 1.11.1 + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/language-core@2.1.6(typescript@5.6.2)': + dependencies: + '@volar/language-core': 2.4.11 '@vue/compiler-dom': 3.5.9 + '@vue/compiler-vue2': 2.7.16 '@vue/shared': 3.5.9 computeds: 0.0.1 minimatch: 9.0.5 - muggle-string: 0.3.1 + muggle-string: 0.4.1 path-browserify: 1.0.1 - vue-template-compiler: 2.7.16 optionalDependencies: typescript: 5.6.2 @@ -25290,13 +24152,15 @@ snapshots: acorn@8.12.1: {} - addons-linter@6.28.0(body-parser@1.20.3)(node-fetch@3.3.1): + acorn@8.14.0: {} + + addons-linter@7.1.0(body-parser@1.20.3): dependencies: '@fluent/syntax': 0.19.0 - '@mdn/browser-compat-data': 5.5.29 + '@mdn/browser-compat-data': 5.6.0 addons-moz-compare: 1.3.0 - addons-scanner-utils: 9.10.1(body-parser@1.20.3)(node-fetch@3.3.1) - ajv: 8.13.0 + addons-scanner-utils: 9.11.0(body-parser@1.20.3) + ajv: 8.17.1 chalk: 4.1.2 cheerio: 1.0.0-rc.12 columnify: 1.6.0 @@ -25305,21 +24169,17 @@ snapshots: eslint: 8.57.1 eslint-plugin-no-unsanitized: 4.0.2(eslint@8.57.1) eslint-visitor-keys: 4.0.0 - espree: 10.0.1 + espree: 10.1.0 esprima: 4.0.1 fast-json-patch: 3.1.1 - glob: 10.4.1 image-size: 1.1.1 - is-mergeable-object: 1.1.1 jed: 1.1.1 json-merge-patch: 1.0.2 os-locale: 5.0.0 pino: 8.20.0 relaxed-json: 1.0.3 - semver: 7.6.2 - sha.js: 2.4.11 + semver: 7.6.3 source-map-support: 0.5.21 - tosource: 1.0.0 upath: 2.0.1 yargs: 17.7.2 yauzl: 2.10.0 @@ -25332,7 +24192,7 @@ snapshots: addons-moz-compare@1.3.0: {} - addons-scanner-utils@9.10.1(body-parser@1.20.3)(node-fetch@3.3.1): + addons-scanner-utils@9.11.0(body-parser@1.20.3): dependencies: '@types/yauzl': 2.10.3 common-tags: 1.8.2 @@ -25342,7 +24202,6 @@ snapshots: yauzl: 2.10.0 optionalDependencies: body-parser: 1.20.3 - node-fetch: 3.3.1 address@1.2.2: {} @@ -25367,10 +24226,18 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 + ajv-draft-04@1.0.0(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + ajv-formats@2.1.1: dependencies: ajv: 8.17.1 + ajv-formats@3.0.1: + dependencies: + ajv: 8.17.1 + ajv-keywords@3.5.2(ajv@6.12.6): dependencies: ajv: 6.12.6 @@ -25581,18 +24448,12 @@ snapshots: asap@2.0.6: {} - asn1@0.2.6: - dependencies: - safer-buffer: 2.1.2 - asn1js@3.0.5: dependencies: pvtsutils: 1.3.5 pvutils: 1.1.3 tslib: 2.7.0 - assert-plus@1.0.0: {} - assert@2.1.0: dependencies: call-bind: 1.0.7 @@ -25613,8 +24474,6 @@ snapshots: astring@1.9.0: {} - async-limiter@1.0.1: {} - async@3.2.2: {} asynckit@0.4.0: {} @@ -25623,6 +24482,11 @@ snapshots: atomic-sleep@1.0.0: {} + atomically@2.0.3: + dependencies: + stubborn-fs: 1.2.5 + when-exit: 2.1.3 + auto-bind@4.0.0: {} autoprefixer@10.4.20(postcss@8.4.47): @@ -25639,15 +24503,13 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - aws-sign2@0.7.0: {} - - aws4@1.13.2: {} - axe-core@4.10.0: {} - axios@0.26.1: + axios@0.28.0: dependencies: follow-redirects: 1.15.9(debug@4.3.7) + form-data: 4.0.0 + proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -25808,10 +24670,6 @@ snapshots: batch@0.6.1: {} - bcrypt-pbkdf@1.0.2: - dependencies: - tweetnacl: 0.14.5 - bech32@2.0.0: {} better-opn@3.0.2: @@ -25832,7 +24690,7 @@ snapshots: binary-install@1.1.0: dependencies: - axios: 0.26.1 + axios: 0.28.0 rimraf: 3.0.2 tar: 6.2.1 transitivePeerDependencies: @@ -25894,6 +24752,17 @@ snapshots: widest-line: 4.0.1 wrap-ansi: 8.1.0 + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.26.1 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + bplist-parser@0.2.0: dependencies: big-integer: 1.6.52 @@ -25938,8 +24807,6 @@ snapshots: buffer-crc32@0.2.13: {} - buffer-equal-constant-time@1.0.1: {} - buffer-from@1.1.2: {} buffer@5.7.1: @@ -25954,12 +24821,9 @@ snapshots: builtins@1.0.3: {} - bunyan@1.8.15: - optionalDependencies: - dtrace-provider: 0.8.8 - moment: 2.30.1 - mv: 2.1.1 - safe-json-stringify: 1.2.0 + bundle-name@3.0.0: + dependencies: + run-applescript: 5.0.0 busboy@1.6.0: dependencies: @@ -26019,6 +24883,8 @@ snapshots: camelcase@7.0.1: {} + camelcase@8.0.0: {} + caniuse-api@3.0.0: dependencies: browserslist: 4.24.0 @@ -26039,8 +24905,6 @@ snapshots: ansicolors: 0.3.2 redeyed: 2.1.1 - caseless@0.12.0: {} - ccount@2.0.1: {} chai@5.1.1: @@ -26160,12 +25024,12 @@ snapshots: chromatic@11.10.4: {} - chrome-launcher@0.15.1: + chrome-launcher@1.1.2: dependencies: '@types/node': 20.16.9 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 2.0.1 transitivePeerDependencies: - supports-color @@ -26183,8 +25047,6 @@ snapshots: dependencies: clsx: 2.0.0 - classnames@2.3.2: {} - classnames@2.5.1: {} clean-css@5.3.3: @@ -26289,8 +25151,6 @@ snapshots: clone@1.0.4: {} - clsx@1.2.1: {} - clsx@2.0.0: {} clsx@2.1.1: {} @@ -26378,6 +25238,8 @@ snapshots: commondir@1.0.1: {} + compare-versions@6.1.1: {} + component-emitter@1.3.1: {} compressible@2.0.18: @@ -26434,6 +25296,8 @@ snapshots: confbox@0.1.7: {} + confbox@0.1.8: {} + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -26447,6 +25311,13 @@ snapshots: write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 + configstore@7.0.0: + dependencies: + atomically: 2.0.3 + dot-prop: 9.0.0 + graceful-fs: 4.2.11 + xdg-basedir: 5.1.0 + connect-history-api-fallback@2.0.0: {} consola@2.15.3: {} @@ -26509,12 +25380,8 @@ snapshots: core-js-pure@3.38.1: {} - core-js@3.29.0: {} - core-js@3.38.1: {} - core-util-is@1.0.2: {} - core-util-is@1.0.3: {} cors@2.8.5: @@ -26975,14 +25842,8 @@ snapshots: damerau-levenshtein@1.0.8: {} - dashdash@1.14.1: - dependencies: - assert-plus: 1.0.0 - dashify@2.0.0: {} - data-uri-to-buffer@4.0.1: {} - data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -27083,10 +25944,6 @@ snapshots: deep-object-diff@1.1.9: {} - deepcopy@2.1.0: - dependencies: - type-detect: 4.1.0 - deepmerge@2.2.1: {} deepmerge@4.3.1: {} @@ -27096,6 +25953,13 @@ snapshots: bplist-parser: 0.2.0 untildify: 4.0.0 + default-browser@4.0.0: + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.2.0 + titleize: 3.0.0 + default-gateway@6.0.3: dependencies: execa: 5.1.1 @@ -27114,6 +25978,8 @@ snapshots: define-lazy-prop@2.0.0: {} + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -27224,49 +26090,6 @@ snapshots: dependencies: typedoc-plugin-markdown: 4.2.10(typedoc@0.26.11(typescript@5.6.2)) - docusaurus-theme-search-typesense@0.20.0-0(@algolia/client-search@4.24.0)(@babel/runtime@7.25.6)(@docusaurus/core@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16))(@docusaurus/theme-common@3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16))(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.9)(algoliasearch@4.24.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16): - dependencies: - '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16) - '@docusaurus/logger': 3.4.0 - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16) - '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.9)(react@18.3.1))(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(vue-template-compiler@2.7.16))(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/theme-translations': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - algoliasearch-helper: 3.22.5(algoliasearch@4.24.0) - clsx: 1.2.1 - eta: 2.2.0 - fs-extra: 10.1.0 - lodash: 4.17.21 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - tslib: 2.7.0 - typesense-docsearch-react: 3.4.1(@algolia/client-search@4.24.0)(@babel/runtime@7.25.6)(@types/react@18.3.9)(algoliasearch@4.24.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - typesense-instantsearch-adapter: 2.8.0(@babel/runtime@7.25.6) - utility-types: 3.11.0 - transitivePeerDependencies: - - '@algolia/client-search' - - '@babel/runtime' - - '@docusaurus/types' - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@swc/css' - - '@types/react' - - algoliasearch - - bufferutil - - csso - - debug - - esbuild - - eslint - - lightningcss - - supports-color - - typescript - - uglify-js - - utf-8-validate - - vue-template-compiler - - webpack-cli - dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -27331,6 +26154,10 @@ snapshots: dependencies: is-obj: 2.0.0 + dot-prop@9.0.0: + dependencies: + type-fest: 4.26.1 + dotenv-expand@10.0.0: {} dotenv@16.4.5: {} @@ -27339,11 +26166,6 @@ snapshots: dset@3.1.4: {} - dtrace-provider@0.8.8: - dependencies: - nan: 2.20.0 - optional: true - duplexer@0.1.2: {} duplexify@3.7.1: @@ -27355,15 +26177,6 @@ snapshots: eastasianwidth@0.2.0: {} - ecc-jsbn@0.1.2: - dependencies: - jsbn: 0.1.1 - safer-buffer: 2.1.2 - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - ee-first@1.1.1: {} ejs@3.1.10: @@ -27530,8 +26343,6 @@ snapshots: es6-error@4.1.1: {} - es6-promisify@7.0.0: {} - esbuild-plugin-alias@0.2.1: {} esbuild-register@3.6.0(esbuild@0.18.20): @@ -27802,10 +26613,6 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-react-refresh@0.4.12(eslint@8.57.1): - dependencies: - eslint: 8.57.1 - eslint-plugin-react@7.37.0(eslint@8.57.1): dependencies: array-includes: 3.1.8 @@ -27915,7 +26722,7 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.0.1: + espree@10.1.0: dependencies: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) @@ -28039,6 +26846,18 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + execa@7.2.0: + dependencies: + cross-spawn: 7.0.5 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + execa@8.0.1: dependencies: cross-spawn: 7.0.5 @@ -28082,7 +26901,7 @@ snapshots: methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.10 + path-to-regexp: 0.1.12 proxy-addr: 2.0.7 qs: 6.13.0 range-parser: 1.2.1 @@ -28130,8 +26949,6 @@ snapshots: transitivePeerDependencies: - supports-color - extsprintf@1.3.0: {} - fast-decode-uri-component@1.0.1: {} fast-deep-equal@2.0.1: {} @@ -28218,11 +27035,6 @@ snapshots: dependencies: xml-js: 1.6.11 - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - fetch-retry@5.0.6: {} fflate@0.8.2: {} @@ -28310,13 +27122,13 @@ snapshots: locate-path: 7.2.0 path-exists: 5.0.0 - firefox-profile@4.3.2: + firefox-profile@4.7.0: dependencies: adm-zip: 0.5.16 - fs-extra: 9.0.1 - ini: 2.0.0 + fs-extra: 11.2.0 + ini: 4.1.3 minimist: 1.2.8 - xml2js: 0.5.0 + xml2js: 0.6.2 first-chunk-stream@3.0.0: {} @@ -28366,7 +27178,6 @@ snapshots: webpack: 5.95.0 optionalDependencies: eslint: 8.57.1 - vue-template-compiler: 2.7.16 fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.94.0(@swc/core@1.7.28)): dependencies: @@ -28387,12 +27198,6 @@ snapshots: form-data-encoder@2.1.4: {} - form-data@2.3.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - form-data@3.0.1: dependencies: asynckit: 0.4.0 @@ -28407,10 +27212,6 @@ snapshots: format@0.2.2: {} - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - formidable@2.1.2: dependencies: dezalgo: 1.0.4 @@ -28454,12 +27255,6 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 - fs-extra@11.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs-extra@11.1.1: dependencies: graceful-fs: 4.2.11 @@ -28484,13 +27279,6 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 - fs-extra@9.0.1: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 1.0.0 - fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -28576,10 +27364,6 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - getpass@0.1.7: - dependencies: - assert-plus: 1.0.0 - giget@1.2.3: dependencies: citty: 0.1.6 @@ -28622,14 +27406,6 @@ snapshots: minipass: 7.1.2 path-scurry: 1.11.1 - glob@10.4.1: - dependencies: - foreground-child: 3.3.0 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - path-scurry: 1.11.1 - glob@10.4.2: dependencies: foreground-child: 3.3.0 @@ -28648,15 +27424,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@6.0.4: - dependencies: - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - optional: true - glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -28674,6 +27441,10 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + global-dirs@3.0.1: dependencies: ini: 2.0.0 @@ -28889,13 +27660,6 @@ snapshots: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 - har-schema@2.0.0: {} - - har-validator@5.1.5: - dependencies: - ajv: 6.12.6 - har-schema: 2.0.0 - has-bigints@1.0.2: {} has-flag@3.0.0: {} @@ -28935,6 +27699,15 @@ snapshots: hast-util-from-html: 2.0.3 unist-util-remove-position: 5.0.0 + hast-util-from-html@2.0.1: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.1 + parse5: 7.1.2 + vfile: 6.0.3 + vfile-message: 4.0.2 + hast-util-from-html@2.0.3: dependencies: '@types/hast': 3.0.4 @@ -29110,6 +27883,11 @@ snapshots: readable-stream: 2.3.8 wbuf: 1.7.3 + html-dom-parser@5.0.10: + dependencies: + domhandler: 5.0.3 + htmlparser2: 9.1.0 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -29138,6 +27916,16 @@ snapshots: relateurl: 0.2.7 terser: 5.34.0 + html-react-parser@5.1.18(@types/react@18.3.9)(react@18.3.1): + dependencies: + domhandler: 5.0.3 + html-dom-parser: 5.0.10 + react: 18.3.1 + react-property: 2.0.2 + style-to-js: 1.1.16 + optionalDependencies: + '@types/react': 18.3.9 + html-tags@3.3.1: {} html-void-elements@3.0.0: {} @@ -29176,6 +27964,13 @@ snapshots: domutils: 3.1.0 entities: 4.5.0 + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + http-cache-semantics@4.1.1: {} http-call@5.3.0: @@ -29235,12 +28030,6 @@ snapshots: transitivePeerDependencies: - debug - http-signature@1.2.0: - dependencies: - assert-plus: 1.0.0 - jsprim: 1.4.2 - sshpk: 1.18.0 - http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 @@ -29273,6 +28062,8 @@ snapshots: human-signals@2.1.0: {} + human-signals@4.3.1: {} + human-signals@5.0.0: {} husky@9.1.6: {} @@ -29348,6 +28139,10 @@ snapshots: ini@2.0.0: {} + ini@4.1.1: {} + + ini@4.1.3: {} + inline-style-parser@0.1.1: {} inline-style-parser@0.2.4: {} @@ -29507,6 +28302,8 @@ snapshots: is-docker@2.2.1: {} + is-docker@3.0.0: {} + is-extendable@0.1.1: {} is-extglob@2.1.1: {} @@ -29539,11 +28336,22 @@ snapshots: is-hexadecimal@2.0.1: {} + is-in-ci@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 is-path-inside: 3.0.3 + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + is-interactive@1.0.0: {} is-lower-case@2.0.2: @@ -29552,8 +28360,6 @@ snapshots: is-map@2.0.3: {} - is-mergeable-object@1.1.1: {} - is-nan@1.3.2: dependencies: call-bind: 1.0.7 @@ -29579,6 +28385,8 @@ snapshots: is-path-inside@3.0.3: {} + is-path-inside@4.0.0: {} + is-plain-obj@3.0.0: {} is-plain-obj@4.1.0: {} @@ -29687,8 +28495,6 @@ snapshots: dependencies: ws: 8.18.0 - isstream@0.1.2: {} - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: @@ -30099,7 +28905,7 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 - jose@4.13.1: {} + jose@5.9.2: {} jose@5.9.3: {} @@ -30114,8 +28920,6 @@ snapshots: dependencies: argparse: 2.0.1 - jsbn@0.1.1: {} - jscodeshift@0.15.2(@babel/preset-env@7.25.4(@babel/core@7.25.2)): dependencies: '@babel/core': 7.25.2 @@ -30185,6 +28989,8 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-parse-even-better-errors@3.0.2: {} + json-rpc-engine@6.1.0: dependencies: '@metamask/safe-event-emitter': 2.0.0 @@ -30202,12 +29008,8 @@ snapshots: json-schema-typed@7.0.3: {} - json-schema@0.4.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - json-stringify-safe@5.0.1: {} - json-to-pretty-yaml@1.2.2: dependencies: remedial: 1.0.8 @@ -30233,20 +29035,6 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonwebtoken@9.0.0: - dependencies: - jws: 3.2.2 - lodash: 4.17.21 - ms: 2.1.3 - semver: 7.6.3 - - jsprim@1.4.2: - dependencies: - assert-plus: 1.0.0 - extsprintf: 1.3.0 - json-schema: 0.4.0 - verror: 1.10.0 - jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -30261,17 +29049,6 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - jwa@1.4.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@3.2.2: - dependencies: - jwa: 1.4.1 - safe-buffer: 5.2.1 - katex@0.16.11: dependencies: commander: 8.3.0 @@ -30290,6 +29067,8 @@ snapshots: kolorist@1.8.0: {} + ky@1.7.3: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -30300,6 +29079,10 @@ snapshots: dependencies: package-json: 8.1.1 + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + launch-editor@2.9.1: dependencies: picocolors: 1.1.0 @@ -30334,7 +29117,7 @@ snapshots: dependencies: immediate: 3.0.6 - lighthouse-logger@1.4.2: + lighthouse-logger@2.0.1: dependencies: debug: 2.6.9 marky: 1.2.5 @@ -30400,6 +29183,11 @@ snapshots: loader-utils@3.3.1: {} + local-pkg@0.5.1: + dependencies: + mlly: 1.7.3 + pkg-types: 1.2.1 + localforage@1.10.0: dependencies: lie: 3.1.1 @@ -30427,9 +29215,9 @@ snapshots: lodash.debounce@4.0.8: {} - lodash.get@4.4.2: {} + lodash.deburr@4.1.0: {} - lodash.isequal@4.5.0: {} + lodash.isnumber@3.0.3: {} lodash.memoize@4.1.2: {} @@ -30441,6 +29229,8 @@ snapshots: lodash.uniq@4.5.0: {} + lodash.upperfirst@4.3.1: {} + lodash@4.17.21: {} log-chopper@1.0.2: @@ -30471,8 +29261,6 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - loglevel@1.9.2: {} - longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -31424,6 +30212,13 @@ snapshots: pkg-types: 1.2.0 ufo: 1.5.4 + mlly@1.7.3: + dependencies: + acorn: 8.14.0 + pathe: 1.1.2 + pkg-types: 1.2.1 + ufo: 1.5.4 + modern-ahocorasick@1.0.1: {} moment@2.30.1: {} @@ -31458,7 +30253,7 @@ snapshots: optionalDependencies: typescript: 5.6.2 - muggle-string@0.3.1: {} + muggle-string@0.4.1: {} multer@1.4.4-lts.1: dependencies: @@ -31486,25 +30281,15 @@ snapshots: mute-stream@1.0.0: {} - mv@2.1.1: - dependencies: - mkdirp: 0.5.6 - ncp: 2.0.0 - rimraf: 2.4.5 - optional: true - mz@2.7.0: dependencies: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 - nan@2.20.0: - optional: true - - nanoid@3.3.7: {} + nanoid@3.3.8: {} - nanoid@5.0.7: {} + nanoid@5.0.9: {} nanospinner@1.1.0: dependencies: @@ -31514,16 +30299,13 @@ snapshots: natural-orderby@2.0.3: {} - ncp@2.0.0: - optional: true - negotiator@0.6.3: {} neo-async@2.6.2: {} - next@14.2.10(@babel/core@7.25.2)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.3): + next@14.2.15(@babel/core@7.25.2)(@playwright/test@1.47.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.3): dependencies: - '@next/env': 14.2.10 + '@next/env': 14.2.15 '@swc/helpers': 0.5.5 busboy: 1.6.0 caniuse-lite: 1.0.30001664 @@ -31533,15 +30315,15 @@ snapshots: react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.10 - '@next/swc-darwin-x64': 14.2.10 - '@next/swc-linux-arm64-gnu': 14.2.10 - '@next/swc-linux-arm64-musl': 14.2.10 - '@next/swc-linux-x64-gnu': 14.2.10 - '@next/swc-linux-x64-musl': 14.2.10 - '@next/swc-win32-arm64-msvc': 14.2.10 - '@next/swc-win32-ia32-msvc': 14.2.10 - '@next/swc-win32-x64-msvc': 14.2.10 + '@next/swc-darwin-arm64': 14.2.15 + '@next/swc-darwin-x64': 14.2.15 + '@next/swc-linux-arm64-gnu': 14.2.15 + '@next/swc-linux-arm64-musl': 14.2.15 + '@next/swc-linux-x64-gnu': 14.2.15 + '@next/swc-linux-x64-musl': 14.2.15 + '@next/swc-win32-arm64-msvc': 14.2.15 + '@next/swc-win32-ia32-msvc': 14.2.15 + '@next/swc-win32-x64-msvc': 14.2.15 '@playwright/test': 1.47.2 sass: 1.79.3 transitivePeerDependencies: @@ -31561,8 +30343,6 @@ snapshots: dependencies: minimatch: 3.1.2 - node-domexception@1.0.0: {} - node-emoji@1.11.0: dependencies: lodash: 4.17.21 @@ -31580,12 +30360,6 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-fetch@3.3.1: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - node-forge@1.3.1: {} node-int64@0.4.0: {} @@ -31649,8 +30423,6 @@ snapshots: pkg-types: 1.2.0 ufo: 1.5.4 - oauth-sign@0.9.0: {} - object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -31751,6 +30523,13 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + open@9.1.0: + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + opener@1.5.2: {} optionator@0.9.4: @@ -31854,6 +30633,13 @@ snapshots: package-json-from-dist@1.0.1: {} + package-json@10.0.1: + dependencies: + ky: 1.7.3 + registry-auth-token: 5.0.2 + registry-url: 6.0.1 + semver: 7.6.3 + package-json@8.1.1: dependencies: got: 12.6.1 @@ -31916,12 +30702,13 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse-json@6.0.2: + parse-json@7.1.1: dependencies: '@babel/code-frame': 7.24.7 error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 + json-parse-even-better-errors: 3.0.2 lines-and-columns: 2.0.4 + type-fest: 3.13.1 parse-numeric-range@1.3.0: {} @@ -31986,7 +30773,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@0.1.10: {} + path-to-regexp@0.1.12: {} path-to-regexp@1.9.0: dependencies: @@ -32014,8 +30801,6 @@ snapshots: pend@1.2.0: {} - performance-now@2.1.0: {} - periscopic@3.1.0: dependencies: '@types/estree': 1.0.6 @@ -32045,6 +30830,8 @@ snapshots: pino-std-serializers@6.2.2: {} + pino-std-serializers@7.0.0: {} + pino@8.20.0: dependencies: atomic-sleep: 1.0.0 @@ -32059,6 +30846,20 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 + pino@9.4.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.2.0 + pino-std-serializers: 7.0.0 + process-warning: 4.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + pirates@4.0.6: {} pkce-challenge@2.2.0: {} @@ -32085,6 +30886,12 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 + pkg-types@1.2.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.3 + pathe: 1.1.2 + pkg-up@3.1.0: dependencies: find-up: 3.0.0 @@ -32618,13 +31425,13 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.8 picocolors: 1.1.0 source-map-js: 1.2.1 postcss@8.4.47: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.8 picocolors: 1.1.0 source-map-js: 1.2.1 @@ -32679,6 +31486,8 @@ snapshots: process-warning@3.0.0: {} + process-warning@4.0.0: {} + process@0.11.10: {} progress@2.0.3: {} @@ -32764,7 +31573,7 @@ snapshots: progress: 2.0.3 proxy-from-env: 1.1.0 rimraf: 2.7.1 - ws: 6.2.3 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - supports-color @@ -32786,8 +31595,6 @@ snapshots: dependencies: side-channel: 1.0.6 - qs@6.5.3: {} - querystringify@2.2.0: {} queue-microtask@1.2.3: {} @@ -32978,6 +31785,8 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-property@2.0.2: {} + react-redux@8.1.3(@types/react-dom@18.3.0)(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(redux@4.2.1): dependencies: '@babel/runtime': 7.25.6 @@ -32995,14 +31804,6 @@ snapshots: react-refresh@0.14.2: {} - react-remove-scroll-bar@2.3.4(@types/react@18.3.9)(react@18.3.1): - dependencies: - react: 18.3.1 - react-style-singleton: 2.2.1(@types/react@18.3.9)(react@18.3.1) - tslib: 2.7.0 - optionalDependencies: - '@types/react': 18.3.9 - react-remove-scroll-bar@2.3.6(@types/react@18.3.9)(react@18.3.1): dependencies: react: 18.3.1 @@ -33277,8 +32078,6 @@ snapshots: regenerate@1.4.2: {} - regenerator-runtime@0.13.11: {} - regenerator-runtime@0.14.1: {} regenerator-transform@0.15.2: @@ -33330,6 +32129,11 @@ snapshots: dependencies: jsesc: 0.5.0 + rehype-jargon@3.1.0: + dependencies: + hast-util-from-html: 2.0.1 + unist-util-visit: 5.0.0 + rehype-katex@7.0.1: dependencies: '@types/hast': 3.0.4 @@ -33473,29 +32277,6 @@ snapshots: repeat-string@1.6.1: {} - request@2.88.2: - dependencies: - aws-sign2: 0.7.0 - aws4: 1.13.2 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 2.3.3 - har-validator: 5.1.5 - http-signature: 1.2.0 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - oauth-sign: 0.9.0 - performance-now: 2.1.0 - qs: 6.5.3 - safe-buffer: 5.2.1 - tough-cookie: 2.5.0 - tunnel-agent: 0.6.0 - uuid: 3.4.0 - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -33561,11 +32342,6 @@ snapshots: rfdc@1.4.1: {} - rimraf@2.4.5: - dependencies: - glob: 6.0.4 - optional: true - rimraf@2.6.3: dependencies: glob: 7.2.3 @@ -33621,6 +32397,10 @@ snapshots: postcss: 8.4.47 strip-json-comments: 3.1.1 + run-applescript@5.0.0: + dependencies: + execa: 5.1.1 + run-async@2.4.1: {} run-async@3.0.0: {} @@ -33650,9 +32430,6 @@ snapshots: safe-buffer@5.2.1: {} - safe-json-stringify@1.2.0: - optional: true - safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 @@ -33732,8 +32509,6 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.6.2: {} - semver@7.6.3: {} send@0.19.0: @@ -33826,11 +32601,6 @@ snapshots: setprototypeof@1.2.0: {} - sha.js@2.4.11: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 @@ -33873,19 +32643,6 @@ snapshots: siginfo@2.0.0: {} - sign-addon@5.3.0: - dependencies: - common-tags: 1.8.2 - core-js: 3.29.0 - deepcopy: 2.1.0 - es6-error: 4.1.1 - es6-promisify: 7.0.0 - jsonwebtoken: 9.0.0 - mz: 2.7.0 - request: 2.88.2 - source-map-support: 0.5.21 - stream-to-promise: 3.0.0 - signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -33962,6 +32719,10 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + sort-css-media-queries@2.2.0: {} source-map-js@1.2.1: {} @@ -34053,18 +32814,6 @@ snapshots: srcset@4.0.0: {} - sshpk@1.18.0: - dependencies: - asn1: 0.2.6 - assert-plus: 1.0.0 - bcrypt-pbkdf: 1.0.2 - dashdash: 1.14.1 - ecc-jsbn: 0.1.2 - getpass: 0.1.7 - jsbn: 0.1.1 - safer-buffer: 2.1.2 - tweetnacl: 0.14.5 - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -34133,16 +32882,6 @@ snapshots: stream-shift@1.0.3: {} - stream-to-array@2.3.0: - dependencies: - any-promise: 1.3.0 - - stream-to-promise@3.0.0: - dependencies: - any-promise: 1.3.0 - end-of-stream: 1.4.4 - stream-to-array: 2.3.0 - streamsearch@1.1.0: {} strict-event-emitter@0.5.1: {} @@ -34282,7 +33021,13 @@ snapshots: strip-json-comments@3.1.1: {} - strip-json-comments@5.0.0: {} + strip-json-comments@5.0.1: {} + + stubborn-fs@1.2.5: {} + + style-to-js@1.1.16: + dependencies: + style-to-object: 1.0.8 style-to-object@0.4.4: dependencies: @@ -34575,6 +33320,10 @@ snapshots: dependencies: real-require: 0.2.0 + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + throttle-debounce@5.0.2: {} through2@2.0.5: @@ -34613,14 +33362,12 @@ snapshots: dependencies: tslib: 2.7.0 + titleize@3.0.0: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - tmp@0.2.1: - dependencies: - rimraf: 3.0.2 - tmp@0.2.3: {} tmpl@1.0.5: {} @@ -34646,15 +33393,8 @@ snapshots: toposort@2.0.2: {} - tosource@1.0.0: {} - totalist@3.0.1: {} - tough-cookie@2.5.0: - dependencies: - psl: 1.9.0 - punycode: 2.3.1 - tough-cookie@4.1.4: dependencies: psl: 1.9.0 @@ -34860,8 +33600,6 @@ snapshots: tween-functions@1.2.0: {} - tweetnacl@0.14.5: {} - tweetnacl@1.0.3: {} type-check@0.4.0: @@ -34870,8 +33608,6 @@ snapshots: type-detect@4.0.8: {} - type-detect@4.1.0: {} - type-fest@0.16.0: {} type-fest@0.20.2: {} @@ -34886,6 +33622,8 @@ snapshots: type-fest@2.19.0: {} + type-fest@3.13.1: {} + type-fest@4.26.1: {} type-is@1.6.18: @@ -34979,40 +33717,6 @@ snapshots: typescript@5.6.2: {} - typesense-docsearch-css@0.4.1: {} - - typesense-docsearch-react@3.4.1(@algolia/client-search@4.24.0)(@babel/runtime@7.25.6)(@types/react@18.3.9)(algoliasearch@4.24.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@algolia/autocomplete-core': 1.8.2 - '@algolia/autocomplete-preset-algolia': 1.8.2(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) - typesense: 1.8.2(@babel/runtime@7.25.6) - typesense-docsearch-css: 0.4.1 - typesense-instantsearch-adapter: 2.8.0(@babel/runtime@7.25.6) - optionalDependencies: - '@types/react': 18.3.9 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - transitivePeerDependencies: - - '@algolia/client-search' - - '@babel/runtime' - - algoliasearch - - debug - - typesense-instantsearch-adapter@2.8.0(@babel/runtime@7.25.6): - dependencies: - '@babel/runtime': 7.25.6 - typesense: 1.8.2(@babel/runtime@7.25.6) - transitivePeerDependencies: - - debug - - typesense@1.8.2(@babel/runtime@7.25.6): - dependencies: - '@babel/runtime': 7.25.6 - axios: 1.7.7(debug@4.3.7) - loglevel: 1.9.2 - transitivePeerDependencies: - - debug - ua-parser-js@1.0.39: {} uc.micro@2.1.0: {} @@ -35147,8 +33851,6 @@ snapshots: universalify@0.2.0: {} - universalify@1.0.0: {} - universalify@2.0.1: {} unixify@1.0.0: @@ -35191,6 +33893,19 @@ snapshots: semver-diff: 4.0.0 xdg-basedir: 5.1.0 + update-notifier@7.3.1: + dependencies: + boxen: 8.0.1 + chalk: 5.3.0 + configstore: 7.0.0 + is-in-ci: 1.0.0 + is-installed-globally: 1.0.0 + is-npm: 6.0.0 + latest-version: 9.0.0 + pupa: 3.1.0 + semver: 7.6.3 + xdg-basedir: 5.1.0 + upper-case-first@2.0.2: dependencies: tslib: 2.7.0 @@ -35285,8 +34000,6 @@ snapshots: uuid@10.0.0: {} - uuid@3.4.0: {} - uuid@8.3.2: {} uuid@9.0.1: {} @@ -35317,8 +34030,6 @@ snapshots: dependencies: builtins: 1.0.3 - validator@13.12.0: {} - value-equal@1.0.1: {} value-or-promise@1.0.12: {} @@ -35327,12 +34038,6 @@ snapshots: vary@1.1.2: {} - verror@1.10.0: - dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.3.0 - vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -35430,16 +34135,18 @@ snapshots: - supports-color - terser - vite-plugin-dts@3.9.1(@types/node@20.16.9)(rollup@4.22.4)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)): + vite-plugin-dts@4.3.0(@types/node@20.16.9)(rollup@4.22.4)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)): dependencies: - '@microsoft/api-extractor': 7.43.0(@types/node@20.16.9) + '@microsoft/api-extractor': 7.48.1(@types/node@20.16.9) '@rollup/pluginutils': 5.1.2(rollup@4.22.4) - '@vue/language-core': 1.8.27(typescript@5.6.2) + '@volar/typescript': 2.4.11 + '@vue/language-core': 2.1.6(typescript@5.6.2) + compare-versions: 6.1.1 debug: 4.3.7(supports-color@8.1.1) kolorist: 1.8.0 + local-pkg: 0.5.1 magic-string: 0.30.11 typescript: 5.6.2 - vue-tsc: 1.8.27(typescript@5.6.2) optionalDependencies: vite: 5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0) transitivePeerDependencies: @@ -35575,22 +34282,12 @@ snapshots: - supports-color - terser + vscode-uri@3.0.8: {} + vue-parser@1.1.6: dependencies: parse5: 3.0.3 - vue-template-compiler@2.7.16: - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - vue-tsc@1.8.27(typescript@5.6.2): - dependencies: - '@volar/typescript': 1.11.1 - '@vue/language-core': 1.8.27(typescript@5.6.2) - semver: 7.6.3 - typescript: 5.6.2 - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -35625,11 +34322,6 @@ snapshots: transitivePeerDependencies: - debug - watchpack@2.4.0: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 @@ -35643,44 +34335,41 @@ snapshots: dependencies: defaults: 1.0.4 - web-ext@7.12.0(body-parser@1.20.3): + web-ext@8.3.0(body-parser@1.20.3): dependencies: - '@babel/runtime': 7.21.0 - '@devicefarmer/adbkit': 3.2.3 - addons-linter: 6.28.0(body-parser@1.20.3)(node-fetch@3.3.1) - bunyan: 1.8.15 - camelcase: 7.0.1 - chrome-launcher: 0.15.1 + '@babel/runtime': 7.25.6 + '@devicefarmer/adbkit': 3.2.6 + addons-linter: 7.1.0(body-parser@1.20.3) + camelcase: 8.0.0 + chrome-launcher: 1.1.2 debounce: 1.2.1 decamelize: 6.0.0 es6-error: 4.1.1 - firefox-profile: 4.3.2 - fs-extra: 11.1.0 + firefox-profile: 4.7.0 fx-runner: 1.4.0 - import-fresh: 3.3.0 - jose: 4.13.1 - mkdirp: 1.0.4 + https-proxy-agent: 7.0.5 + jose: 5.9.2 + jszip: 3.10.1 multimatch: 6.0.0 - mz: 2.7.0 - node-fetch: 3.3.1 node-notifier: 10.0.0 - open: 8.4.2 - parse-json: 6.0.2 + open: 9.1.0 + parse-json: 7.1.1 + pino: 9.4.0 promise-toolbox: 0.21.0 - sign-addon: 5.3.0 source-map-support: 0.5.21 strip-bom: 5.0.0 - strip-json-comments: 5.0.0 - tmp: 0.2.1 - update-notifier: 6.0.2 - watchpack: 2.4.0 - ws: 8.13.0 - yargs: 17.7.1 + strip-json-comments: 5.0.1 + tmp: 0.2.3 + update-notifier: 7.3.1 + watchpack: 2.4.2 + ws: 8.18.0 + yargs: 17.7.2 zip-dir: 2.0.0 transitivePeerDependencies: - body-parser - bufferutil - express + - node-fetch - safe-compare - supports-color - utf-8-validate @@ -35949,7 +34638,7 @@ snapshots: opener: 1.5.2 picocolors: 1.1.0 sirv: 2.0.4 - ws: 7.5.10 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -36188,6 +34877,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + when-exit@2.1.3: {} + when@3.7.7: {} which-boxed-primitive@1.0.2: @@ -36256,6 +34947,10 @@ snapshots: dependencies: string-width: 5.1.2 + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + wildcard@2.0.1: {} winreg@0.0.12: {} @@ -36308,14 +35003,6 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@6.2.3: - dependencies: - async-limiter: 1.0.1 - - ws@7.5.10: {} - - ws@8.13.0: {} - ws@8.18.0: {} xdg-basedir@5.1.0: {} @@ -36326,7 +35013,7 @@ snapshots: xml-name-validator@5.0.0: {} - xml2js@0.5.0: + xml2js@0.6.2: dependencies: sax: 1.4.1 xmlbuilder: 11.0.1 @@ -36370,16 +35057,6 @@ snapshots: y18n: 4.0.3 yargs-parser: 18.1.3 - yargs@17.7.1: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -36410,14 +35087,6 @@ snapshots: toposort: 2.0.2 type-fest: 2.19.0 - z-schema@5.0.5: - dependencies: - lodash.get: 4.4.2 - lodash.isequal: 4.5.0 - validator: 13.12.0 - optionalDependencies: - commander: 9.5.0 - zip-dir@2.0.0: dependencies: async: 3.2.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 58699ae570b..3ad2792cd72 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,7 +5,7 @@ packages: - "sdk/**" - "!**/dist/**" - "!**/.next/**" - - "!sdk/create-dapp/templates" + - "!sdk/create-dapp/templates/*" - "!sdk/typescript/bcs" - "!sdk/typescript/client" - "!sdk/typescript/cryptography" diff --git a/scripts/simtest/clippy.sh b/scripts/simtest/clippy.sh index 951e372c211..9b64233cc5e 100755 --- a/scripts/simtest/clippy.sh +++ b/scripts/simtest/clippy.sh @@ -5,6 +5,7 @@ # verify that git repo is clean if [[ -n $(git status -s) ]]; then echo "Working directory is not clean. Please commit all changes before running this script." + git status -s exit 1 fi diff --git a/scripts/simtest/simtest-cov.sh b/scripts/simtest/simtest-cov.sh index a8701b87c0c..acd415165eb 100755 --- a/scripts/simtest/simtest-cov.sh +++ b/scripts/simtest/simtest-cov.sh @@ -6,7 +6,7 @@ # verify that git repo is clean if [[ -n $(git status -s) ]]; then echo "Working directory is not clean. Please commit all changes before running this script." - echo $(git status -s) + git status -s exit 1 fi diff --git a/sdk/create-dapp/CHANGELOG.md b/sdk/create-dapp/CHANGELOG.md index d04a319a617..567b062afe9 100644 --- a/sdk/create-dapp/CHANGELOG.md +++ b/sdk/create-dapp/CHANGELOG.md @@ -1,5 +1,12 @@ # @iota/create-dapp +## 0.3.0 + +### Minor Changes + +- 8c3a220: Update some outdated eslint dependencies of the `@iota/create-dapp` templates, to their + eslint v9-compatible versions + ## 0.2.2 ### Patch Changes diff --git a/sdk/create-dapp/package.json b/sdk/create-dapp/package.json index af64537e99e..fa6117294d6 100644 --- a/sdk/create-dapp/package.json +++ b/sdk/create-dapp/package.json @@ -3,7 +3,7 @@ "author": "IOTA Foundation <info@iota.org>", "description": "A CLI for creating new IOTA dApps", "homepage": "https://docs.iota.org/ts-sdk/typescript/", - "version": "0.2.2", + "version": "0.3.0", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/create-dapp/templates/react-client-dapp/eslint.config.js b/sdk/create-dapp/templates/react-client-dapp/eslint.config.js new file mode 100644 index 00000000000..7c663262678 --- /dev/null +++ b/sdk/create-dapp/templates/react-client-dapp/eslint.config.js @@ -0,0 +1,28 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; + +export default tseslint.config( + { + files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"], + plugins: { + react, + reactHooks, + reactRefresh, + }, + settings: { + version: "18", + }, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + eslint.configs.recommended, + tseslint.configs.recommended, +); diff --git a/sdk/create-dapp/templates/react-client-dapp/package.json b/sdk/create-dapp/templates/react-client-dapp/package.json index 8e79c61bd6b..611f666329d 100644 --- a/sdk/create-dapp/templates/react-client-dapp/package.json +++ b/sdk/create-dapp/templates/react-client-dapp/package.json @@ -6,28 +6,30 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "lint": "eslint .", + "preview": "vite preview", + "format": "prettier -w ." }, "dependencies": { "@iota/dapp-kit": "workspace:*", "@iota/iota-sdk": "workspace:*", - "@radix-ui/colors": "^3.0.0", - "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.1", "@tanstack/react-query": "^5.50.1", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { + "@eslint/js": "^9.17.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^7.16.0", - "@typescript-eslint/parser": "^7.16.0", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "@vitejs/plugin-react-swc": "^3.7.0", - "eslint": "^9.6.0", - "eslint-plugin-react-hooks": "^4.6.2", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.7", + "eslint-plugin-react": "^7.37.2", + "typescript-eslint": "^8.18.0", "prettier": "^3.3.2", "typescript": "^5.5.3", "vite": "^5.3.3" diff --git a/sdk/create-dapp/templates/react-e2e-counter/eslint.config.js b/sdk/create-dapp/templates/react-e2e-counter/eslint.config.js new file mode 100644 index 00000000000..7c663262678 --- /dev/null +++ b/sdk/create-dapp/templates/react-e2e-counter/eslint.config.js @@ -0,0 +1,28 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; + +export default tseslint.config( + { + files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"], + plugins: { + react, + reactHooks, + reactRefresh, + }, + settings: { + version: "18", + }, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + }, + eslint.configs.recommended, + tseslint.configs.recommended, +); diff --git a/sdk/create-dapp/templates/react-e2e-counter/package.json b/sdk/create-dapp/templates/react-e2e-counter/package.json index 607acf6b606..0be7053cb16 100644 --- a/sdk/create-dapp/templates/react-e2e-counter/package.json +++ b/sdk/create-dapp/templates/react-e2e-counter/package.json @@ -6,28 +6,30 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "lint": "eslint .", + "preview": "vite preview", + "format": "prettier -w ." }, "dependencies": { "@iota/dapp-kit": "workspace:*", "@iota/iota-sdk": "workspace:*", - "@radix-ui/colors": "^3.0.0", - "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.1", "@tanstack/react-query": "^5.50.1", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { + "@eslint/js": "^9.17.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.1.0", - "@typescript-eslint/parser": "^6.1.0", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "@vitejs/plugin-react-swc": "^3.7.0", - "eslint": "^8.45.0", - "eslint-plugin-react-hooks": "^4.6.2", + "eslint": "^9.17.0", + "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.7", + "eslint-plugin-react": "^7.37.2", + "typescript-eslint": "^8.18.0", "prettier": "^3.3.2", "typescript": "^5.5.3", "vite": "^5.3.3" diff --git a/sdk/typescript/src/client/types/generated.ts b/sdk/typescript/src/client/types/generated.ts index 113119521fe..26c4f5a9d2f 100644 --- a/sdk/typescript/src/client/types/generated.ts +++ b/sdk/typescript/src/client/types/generated.ts @@ -1020,7 +1020,7 @@ export interface IotaObjectData { /** * The Display metadata for frontend UI rendering, default to be None unless * IotaObjectDataOptions.showContent is set to true This can also be None if the struct type does not - * have Display defined See more details in <https://forums.iota.io/t/nft-object-display-proposal/4872> + * have Display defined */ display?: DisplayFieldsResponse | null; objectId: string; diff --git a/sdk/typescript/src/client/types/params.ts b/sdk/typescript/src/client/types/params.ts index feb1c6ccf3c..6d6a88d5b48 100644 --- a/sdk/typescript/src/client/types/params.ts +++ b/sdk/typescript/src/client/types/params.ts @@ -326,7 +326,8 @@ export interface GetValidatorsApyParams {} /** Return list of events for a specified query criteria. */ export interface QueryEventsParams { /** - * The event query criteria. See [Event filter](https://docs.iota.org/developer/iota-101/using-events#applying-event-filters) + * The event query criteria. See + * [Event filter](https://docs.iota.org/developer/iota-101/using-events#applying-event-filters) * documentation for examples. */ query: RpcTypes.IotaEventFilter; @@ -353,7 +354,8 @@ export type QueryTransactionBlocksParams = { export interface SubscribeEventParams { /** * The filter criteria of the event stream. See - * [Event filter](https://docs.iota.org/developer/iota-101/using-events#applying-event-filters) documentation for examples. + * [Event filter](https://docs.iota.org/developer/iota-101/using-events#applying-event-filters) + * documentation for examples. */ filter: RpcTypes.IotaEventFilter; }