From 22a74b3f790e3f73ef23eb4c7892ba062d746547 Mon Sep 17 00:00:00 2001 From: Peter White Date: Wed, 18 Dec 2024 19:42:38 -0700 Subject: [PATCH 001/117] feat: wallet integration (#371) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(wallet integration): pop up contract (#365) * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * feat(up-contract): get call data for instantiate and pass to wallet integration -- PoC * feat(up-contract): run server and pass call_data for signing -- PoC * feat(up-contract): submit signed payload to node * fix(up-contract): update to FrontendFromDir type * refactor(pop-up): create functions for wallet integration, plus general cleanup * chore: use git branch for cargo contract * feat(pop-up-contract): better error handling and prompt displays * chore(up-contract): outdated field name and clippy allow * feat(up-contract): handle subxt events better, various improvements * feat(up-contract): custom errors in contracts crate * fix: minor fixes after rebase * fix(up-contract): test compilation after rebase * feat(up-contract): suri and use-wallet can't be used together * refactor: wait for finalization * feat(up-contract): serve HTML from string, fix deployment message on error, cors, and other misc. improvements (#380) * docs: add function comments * feat: wallet integration pop call chain (#379) * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * feat(up-contract): get call data for instantiate and pass to wallet integration -- PoC * feat(up-contract): run server and pass call_data for signing -- PoC * feat(up-contract): submit signed payload to node * fix(up-contract): update to FrontendFromDir type * refactor(pop-up): create functions for wallet integration, plus general cleanup * chore: use git branch for cargo contract * feat(pop-up-contract): better error handling and prompt displays * chore(up-contract): outdated field name and clippy allow * feat(up-contract): handle subxt events better, various improvements * feat(up-contract): custom errors in contracts crate * fix: minor fixes after rebase * fix(up-contract): test compilation after rebase * feat(up-contract): suri and use-wallet can't be used together * refactor: wait for finalization * feat(up-contract): serve HTML from string, fix deployment message on error, cors, and other misc. improvements (#380) * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * refactor(pop-up): create functions for wallet integration, plus general cleanup * chore: use git branch for cargo contract * feat(up-contract): handle subxt events better, various improvements * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: guide user to call a contract (#306) * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: parse user inputs for Option arguments (#332) * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test: fix unit test * feat: allow users to specify custom contract metadata files (#347) * chore: allow the user specify the metadata file to call a contract * test: unit test to parse metadata from a file * docs: fix docs * refactor: ensure_contract_built after user input path * fix: call contract when metadata file * fix: remove default_input in contract address * docs: rename metadata with artifact * fix: panic at has_contract_been_built * fix: clippy * refactor: keep ensure_contract_built as a CallContractCommand function * fix: ensure_contract_built * docs: improve comments * fix: feedback and include wasm file for testing * fix: clippy * chore: after build contract prompt the user if the contract is already deployed * refactor: ensure_contract_built * refactor: has_contract_been_built function * docs: fix comments and messages * refactor: get_messages and get_constructors * test: fix unit tests call ui --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * fix: support new substrate-contracts-node structure and stabilize integration tests (#360) * fix: parse new structure substrate-contracts-node * fix: paseo+coretime integration test * fix: sourcing latest version substrate-contracts-node * refactor: set_executable_permission function * fix: clippy * chore: CI configuration * test: specify port in run_contracts_node * fix: use random ports instead of hardcoded ones * feat: guide user to call a parachain (#316) * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README * feat: pop call parachain prototype * feat: dispaly arguments of extrinsic * refactor: structure similar to pop call contract * feat: parse all values for extrinsic/storage * refactor: signer in common * refactor: improve messages * feat: call parachain ui * fix: calls working * refactor: remove unused code * refactor: remove unused code * refactor: various fixes * refactor: various fixes * feat: add option to include params from command line * refactor: clean docs and refactor code * fix: tests * refactor: parse all the metadata again * refactor: reorganize and clean metadata functions * feat: display specific use cases to the user * refactor: predefined actions * fix: various fixes * fix: error message not supported for complex types * refactor: parse all metadata, including parameters at once * refactor: clean docs and move code * fix: format_type * fix: parse user inputs for Option arguments (#332) * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test: fix unit test * refactor: clean the way to parse and prompt parameters * feat: add Purchase on-demand coretime use cases * test: add skip_confirm, move when prompt for the signer and create the integration test * test: call parachain ui unit test * refactor: separate structs * fmt * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * fix: solve conflicts and unit tests (#359) * test: call parachain ui unit test * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * fix: conflicts and unit tests * test: remove test and improve test * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * feat: pop call parachain prototype * feat: dispaly arguments of extrinsic * refactor: structure similar to pop call contract * feat: parse all values for extrinsic/storage * refactor: signer in common * refactor: improve messages * feat: call parachain ui * fix: calls working * refactor: remove unused code * refactor: remove unused code * refactor: various fixes * refactor: various fixes * feat: add option to include params from command line * refactor: clean docs and refactor code * fix: tests * refactor: parse all the metadata again * refactor: reorganize and clean metadata functions * feat: display specific use cases to the user * refactor: predefined actions * fix: various fixes * fix: error message not supported for complex types * refactor: parse all metadata, including parameters at once * refactor: clean docs and move code * fix: format_type * test: fix unit test * refactor: clean the way to parse and prompt parameters * feat: add Purchase on-demand coretime use cases * test: add skip_confirm, move when prompt for the signer and create the integration test * test: call parachain ui unit test * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * feat: repeat call only if using guide UI * fix: clippy * refactor: various improvements * chore: parser for pallet and extrinsic input names * refactor: only move to pop_common the needed functions * refactor: improve test, docs and errors * test: fix unit tests * fix: reset_for_new_call when extrinisc is not supported * fix: build with parachain features * test: wait before call parachain in integration test * docs: minor improvements * test: migrate find_free_port to pop_common * test: fix increase waiting time * test: remove unnecesary test case * refactor: rename api with client * refactor: naming and docs * docs: improve docs and missing comments * test: remove unnecesary verbose * test: find_free_port * docs: improve parameter documentation * test: add missing test to sign_and_submit_extrinsic * fix: apply feedback from auxiliar PRs, remove unnecesary clones * docs: public modules * refactor: clean unused params * fix: mark all extrinsics that uses calls as parameter as unsupported * test: fix expect_select * docs: improve documentation * feat: submit extrinsic from call_data (#348) * feat: submit extrinsic from call_data * test: unit test for initialize_api_client * test: unit test for send_extrinsic_from_call_data * fix: CallData struct * fix: skip_confirm for send_extrinsic_from_call_data * chore: clippy * chore: fmt * refactor: minor doc and naming changes * refactor: remove unnecesary clones and return early when submit_extrinsic_from_call_data * chore: fmt * refactor: split decode_call_data logic outside sign_and_submit_extrinsic_with_call_data * feat: parse files when the argument values are very big (#363) * feat: parse files when the argument values are very big * test: unit test * chore: fmt * feat: file logic using the command line * fix: sequence arguments * test: fix unit test * refactor: remove prompting the user if input is file or value * refactor: parse_extrinsic_arguments * fix: CI deny * refactor: reorder Param derive macros * test: fix decode_call_data_works unit test * refactor: use Default derive macro and define constants for test values (#366) * feat: parse files when the argument values are very big * chore: fmt * feat: file logic using the command line * fix: sequence arguments * refactor: parse_extrinsic_arguments * refactor: use Default in pop_parachain structs * refactor: use Default in CallParachainCommand struct * refactor: use constant in tests * chore: fmt and small refactor * feat: flag sudo to wrap extrinsic (#349) * feat: submit extrinsic from call_data * test: unit test for initialize_api_client * feat: wrap call into a sudo call * test: add unit test to the new logic * fix: skip_confirm for send_extrinsic_from_call_data * chore: clippy * docs: renaming and improve docs * test: use force_transfer for testing * fix: check if sudo exist before prompt the user * chore: fmt * chore: fmt * test: fix wrong assert * docs: improve comments and output messages * refactor: split decode_call_data logic outside sign_and_submit_extrinsic_with_call_data * fix: test construct_sudo_extrinsic_works and formatting * refactor: various fixes and improvements (#367) * refactor: sort pallets/dispatchables * refactor: remove unnecessary async * fix: resolve issue after rebase * fix: more async issues after rebase * refactor: use single constant * refactor: terminology (#368) * refactor: terminology * refactor: simply pallet/function relationship * fix: amend call_data conflicts after refactor * refactor: improvements (#370) * fix: add missing short arg option * refactor: note that extrinsic wait includes finalization * refactor: remove clones * style: formatting * refactor: make file prompt more generic * refactor: add missing license headers * style: formatting * docs: comments * docs: comments * docs: comments * refactor: reuse existing metadata * refactor: minimise clones * docs: comments * refactor: naming * docs: fix parameter doc comments * refactor: address clippy warnings * refactor: rename parachain with chain as the primary command and retain parachain as an alias (#373) * refactor: rename parachain with chain in visible messages * refactor: rename parachain with chain internal code * chore: solve fmt after rebase * refactor: small fix, use alias instead aliases * refactor: rename CallParachain struct into Call --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Co-authored-by: Daanvdplas * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * chore: use git branch for cargo contract * feat(up-contract): handle subxt events better, various improvements * refactor: reuse pop up logic * chore: rebase fixes * refactor: clean after rebase * fix: cargo.lock after rebase * refactor: move terminate node into common::contracts * fix: sp-core not only for contracts * feat(up-contract): serve HTML from string, fix deployment message on error, cors, and other misc. improvements * fix: rebase * feat: secure signing logic for pop call chain * test: unit tests in pop-cli * feat: secure signing for call from call_data * refactor: docs and remove unneded functions * refactor: replace hex library * fix: rebase issues * fix: rebase * refactor: small changes * refactor: renaming * feat: wallet integration call contract (#378) * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * feat(up-contract): get call data for instantiate and pass to wallet integration -- PoC * feat(up-contract): run server and pass call_data for signing -- PoC * feat(up-contract): submit signed payload to node * fix(up-contract): update to FrontendFromDir type * refactor(pop-up): create functions for wallet integration, plus general cleanup * chore: use git branch for cargo contract * feat(pop-up-contract): better error handling and prompt displays * chore(up-contract): outdated field name and clippy allow * feat(up-contract): handle subxt events better, various improvements * feat(up-contract): custom errors in contracts crate * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * feat: guide user to call a contract (#306) * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: parse user inputs for Option arguments (#332) * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test: fix unit test * feat: allow users to specify custom contract metadata files (#347) * chore: allow the user specify the metadata file to call a contract * test: unit test to parse metadata from a file * docs: fix docs * refactor: ensure_contract_built after user input path * fix: call contract when metadata file * fix: remove default_input in contract address * docs: rename metadata with artifact * fix: panic at has_contract_been_built * fix: clippy * refactor: keep ensure_contract_built as a CallContractCommand function * fix: ensure_contract_built * docs: improve comments * fix: feedback and include wasm file for testing * fix: clippy * chore: after build contract prompt the user if the contract is already deployed * refactor: ensure_contract_built * refactor: has_contract_been_built function * docs: fix comments and messages * refactor: get_messages and get_constructors * test: fix unit tests call ui --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * fix: support new substrate-contracts-node structure and stabilize integration tests (#360) * fix: parse new structure substrate-contracts-node * fix: paseo+coretime integration test * fix: sourcing latest version substrate-contracts-node * refactor: set_executable_permission function * fix: clippy * chore: CI configuration * test: specify port in run_contracts_node * fix: use random ports instead of hardcoded ones * fix: build spec experience (#331) * fix: add chain to specify the chain specification * fix: default_bootnode by default to true * chore: fmt * chore: deprecate flag --release in build specs * fix: clean output (#334) * fix: undo deprecation of --release flag * refactor: small fix * style: remove extra space * fix(spec): better handling of spinner * style: use spinner instead of multispinner * docs: help message to include build * feat: reuse existing chain spec * refactor: remove clone * refactor: opt in to edit provided chain spec * docs: improve * refactor: flow flag input * fix: prepare_output_path * refactor: resolve small improvements * fix: protocol id prompt * fix: spinner * fix: docs * test: test cli * chore: refactor * chore: amend test * feat: production profile * refactor: improve profile experience * chore: feedback and rebase * chore: add profile tests * fix(test): parachain_lifecycle * style: fmt * fix: clippy * fix: cli required changes introduced by PR * fix: test * fix: clippy * docs: deprecation message --------- Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Co-authored-by: Daanvdplas * feat: guide user to call a parachain (#316) * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README * feat: pop call parachain prototype * feat: dispaly arguments of extrinsic * refactor: structure similar to pop call contract * feat: parse all values for extrinsic/storage * refactor: signer in common * refactor: improve messages * feat: call parachain ui * fix: calls working * refactor: remove unused code * refactor: remove unused code * refactor: various fixes * refactor: various fixes * feat: add option to include params from command line * refactor: clean docs and refactor code * fix: tests * refactor: parse all the metadata again * refactor: reorganize and clean metadata functions * feat: display specific use cases to the user * refactor: predefined actions * fix: various fixes * fix: error message not supported for complex types * refactor: parse all metadata, including parameters at once * refactor: clean docs and move code * fix: format_type * fix: parse user inputs for Option arguments (#332) * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test: fix unit test * refactor: clean the way to parse and prompt parameters * feat: add Purchase on-demand coretime use cases * test: add skip_confirm, move when prompt for the signer and create the integration test * test: call parachain ui unit test * refactor: separate structs * fmt * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * fix: solve conflicts and unit tests (#359) * test: call parachain ui unit test * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * fix: conflicts and unit tests * test: remove test and improve test * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * feat: pop call parachain prototype * feat: dispaly arguments of extrinsic * refactor: structure similar to pop call contract * feat: parse all values for extrinsic/storage * refactor: signer in common * refactor: improve messages * feat: call parachain ui * fix: calls working * refactor: remove unused code * refactor: remove unused code * refactor: various fixes * refactor: various fixes * feat: add option to include params from command line * refactor: clean docs and refactor code * fix: tests * refactor: parse all the metadata again * refactor: reorganize and clean metadata functions * feat: display specific use cases to the user * refactor: predefined actions * fix: various fixes * fix: error message not supported for complex types * refactor: parse all metadata, including parameters at once * refactor: clean docs and move code * fix: format_type * test: fix unit test * refactor: clean the way to parse and prompt parameters * feat: add Purchase on-demand coretime use cases * test: add skip_confirm, move when prompt for the signer and create the integration test * test: call parachain ui unit test * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * feat: repeat call only if using guide UI * fix: clippy * refactor: various improvements * chore: parser for pallet and extrinsic input names * refactor: only move to pop_common the needed functions * refactor: improve test, docs and errors * test: fix unit tests * fix: reset_for_new_call when extrinisc is not supported * fix: build with parachain features * test: wait before call parachain in integration test * docs: minor improvements * test: migrate find_free_port to pop_common * test: fix increase waiting time * test: remove unnecesary test case * refactor: rename api with client * refactor: naming and docs * docs: improve docs and missing comments * test: remove unnecesary verbose * test: find_free_port * docs: improve parameter documentation * test: add missing test to sign_and_submit_extrinsic * fix: apply feedback from auxiliar PRs, remove unnecesary clones * docs: public modules * refactor: clean unused params * fix: mark all extrinsics that uses calls as parameter as unsupported * test: fix expect_select * docs: improve documentation * feat: submit extrinsic from call_data (#348) * feat: submit extrinsic from call_data * test: unit test for initialize_api_client * test: unit test for send_extrinsic_from_call_data * fix: CallData struct * fix: skip_confirm for send_extrinsic_from_call_data * chore: clippy * chore: fmt * refactor: minor doc and naming changes * refactor: remove unnecesary clones and return early when submit_extrinsic_from_call_data * chore: fmt * refactor: split decode_call_data logic outside sign_and_submit_extrinsic_with_call_data * feat: parse files when the argument values are very big (#363) * feat: parse files when the argument values are very big * test: unit test * chore: fmt * feat: file logic using the command line * fix: sequence arguments * test: fix unit test * refactor: remove prompting the user if input is file or value * refactor: parse_extrinsic_arguments * fix: CI deny * refactor: reorder Param derive macros * test: fix decode_call_data_works unit test * refactor: use Default derive macro and define constants for test values (#366) * feat: parse files when the argument values are very big * chore: fmt * feat: file logic using the command line * fix: sequence arguments * refactor: parse_extrinsic_arguments * refactor: use Default in pop_parachain structs * refactor: use Default in CallParachainCommand struct * refactor: use constant in tests * chore: fmt and small refactor * feat: flag sudo to wrap extrinsic (#349) * feat: submit extrinsic from call_data * test: unit test for initialize_api_client * feat: wrap call into a sudo call * test: add unit test to the new logic * fix: skip_confirm for send_extrinsic_from_call_data * chore: clippy * docs: renaming and improve docs * test: use force_transfer for testing * fix: check if sudo exist before prompt the user * chore: fmt * chore: fmt * test: fix wrong assert * docs: improve comments and output messages * refactor: split decode_call_data logic outside sign_and_submit_extrinsic_with_call_data * fix: test construct_sudo_extrinsic_works and formatting * refactor: various fixes and improvements (#367) * refactor: sort pallets/dispatchables * refactor: remove unnecessary async * fix: resolve issue after rebase * fix: more async issues after rebase * refactor: use single constant * refactor: terminology (#368) * refactor: terminology * refactor: simply pallet/function relationship * fix: amend call_data conflicts after refactor * refactor: improvements (#370) * fix: add missing short arg option * refactor: note that extrinsic wait includes finalization * refactor: remove clones * style: formatting * refactor: make file prompt more generic * refactor: add missing license headers * style: formatting * docs: comments * docs: comments * docs: comments * refactor: reuse existing metadata * refactor: minimise clones * docs: comments * refactor: naming * docs: fix parameter doc comments * refactor: address clippy warnings * refactor: rename parachain with chain as the primary command and retain parachain as an alias (#373) * refactor: rename parachain with chain in visible messages * refactor: rename parachain with chain internal code * chore: solve fmt after rebase * refactor: small fix, use alias instead aliases * refactor: rename CallParachain struct into Call --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Co-authored-by: Daanvdplas * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * chore: use git branch for cargo contract * feat(up-contract): handle subxt events better, various improvements * refactor: reuse pop up logic * chore: rebase fixes * refactor: clean after rebase * fix: cargo.lock after rebase * refactor: move terminate node into common::contracts * fix: sp-core not only for contracts * feat(up-contract): serve HTML from string, fix deployment message on error, cors, and other misc. improvements * fix: rebase * chore: deny.toml license * feat: secure signing logic for pop call contract * feat: integrate secure signing with pop call contract * test: adapt pop-cli ui tests * refactor: clean code * docs: improve docs functions * test: ignore failing test (fixed in another PR), and remove println * fix: import removed by mistake * feat(up-contract): serve HTML from string, fix deployment message on error, cors, and other misc. improvements * feat(up-contract): serve HTML from string, fix deployment message on error, cors, and other misc. improvements * feat(wallet-integration): server and API (#362) * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: add TransactionData::new * refactor(wallet-integration: shutdown channel error handling, terminate helper, take_error and TranscationData::new tests * chore: clippy warning * feat(up-contract): get call data for upload-only and pass to wallet integration -- PoC * feat(up-contract): get call data for instantiate and pass to wallet integration -- PoC * refactor(pop-up): create functions for wallet integration, plus general cleanup * chore: use git branch for cargo contract * feat(up-contract): handle subxt events better, various improvements * feat(wallet-integration): implement server and API * feat(wallet-integration): add a few tests to server. Needs more * refactor(wallet-integration): just use call_data, remove data types * feat(wallet-integration): add frontend type for flexible serving * feat(wallet-integration): StateHandler in WalletIntegrationManager. Add terminate method * refactor(wallet-integration): move to module with pop-cli crate * feat(wallet-integration): server-side error handling, update port to 9090 * refactor(wallet-integration): restructure server for easier use in thread * fix(wallet-integration): remove invalid fn `finish` * docs(wallet-integration): inline comments * test(wallet-integration): unit tests for wallet-integration server. Add new frontend type * feat(wallet-integration): two new routes: error and terminate * refactor(wallet-integration): consistent comments, remove unnecessary code in tests * feat: guide user to call a contract (#306) * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: parse user inputs for Option arguments (#332) * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test: fix unit test * feat: allow users to specify custom contract metadata files (#347) * chore: allow the user specify the metadata file to call a contract * test: unit test to parse metadata from a file * docs: fix docs * refactor: ensure_contract_built after user input path * fix: call contract when metadata file * fix: remove default_input in contract address * docs: rename metadata with artifact * fix: panic at has_contract_been_built * fix: clippy * refactor: keep ensure_contract_built as a CallContractCommand function * fix: ensure_contract_built * docs: improve comments * fix: feedback and include wasm file for testing * fix: clippy * chore: after build contract prompt the user if the contract is already deployed * refactor: ensure_contract_built * refactor: has_contract_been_built function * docs: fix comments and messages * refactor: get_messages and get_constructors * test: fix unit tests call ui --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * fix: support new substrate-contracts-node structure and stabilize integration tests (#360) * fix: parse new structure substrate-contracts-node * fix: paseo+coretime integration test * fix: sourcing latest version substrate-contracts-node * refactor: set_executable_permission function * fix: clippy * chore: CI configuration * test: specify port in run_contracts_node * fix: use random ports instead of hardcoded ones * feat: guide user to call a parachain (#316) * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README * feat: pop call parachain prototype * feat: dispaly arguments of extrinsic * refactor: structure similar to pop call contract * feat: parse all values for extrinsic/storage * refactor: signer in common * refactor: improve messages * feat: call parachain ui * fix: calls working * refactor: remove unused code * refactor: remove unused code * refactor: various fixes * refactor: various fixes * feat: add option to include params from command line * refactor: clean docs and refactor code * fix: tests * refactor: parse all the metadata again * refactor: reorganize and clean metadata functions * feat: display specific use cases to the user * refactor: predefined actions * fix: various fixes * fix: error message not supported for complex types * refactor: parse all metadata, including parameters at once * refactor: clean docs and move code * fix: format_type * fix: parse user inputs for Option arguments (#332) * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * fix: logo doesn't show in README --------- Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test: fix unit test * refactor: clean the way to parse and prompt parameters * feat: add Purchase on-demand coretime use cases * test: add skip_confirm, move when prompt for the signer and create the integration test * test: call parachain ui unit test * refactor: separate structs * fmt * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * fix: solve conflicts and unit tests (#359) * test: call parachain ui unit test * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * fix: conflicts and unit tests * test: remove test and improve test * feat: guide user for calling a contract * feat: get metadata contract from the contract path * refactor: refactor test and validate address input * fix: apply feedback * feat: prompt to have another call and skip questions for queries * refactor: use Cli module instead of cliclack * test: unit test pop-cli crate * test: unit contracts crate * chore: format * test: refactor and improve test cases * fix: fix todos and refactor * test: fix unit test * feat: parse types of parameters and display it to the user in the placeholder * refactor: error handling for pop call * refactor: display call to be executed after guide and reorder * refactor: when repeat call use same contract values and dont clean screen * test: add dry-run test * test: refactor and add more test coverage * test: more coverage * fix: unit test * feat: dev mode to skip certain user prompts * refactor: test functions, renaming and fix clippy * refactor: improve devex of pop call contract * test: adjust tests to refactor * chore: reset_for_new_call fields * fix: build contract if has not been built * refactor: use command state (#338) Merged set_up_call_config and guide_user_to_call_contract into a single function. Also adds short symbols for arguments. * fix: automatically add some or none to Option argument * test: refactor and tests * refactor: improve code and comments * fix: renaming and clean code * chore: option params not mandatory * fix: parse user inputs for Option arguments in constructor (#335) * fix: automatically add some or none to Option argument * fix: tests * refactor: process_function_args * test: update tests accordingly last changes * fix: issue with delimiter * test: fix unit test * refactor: renaming and fix comments * refactor: format types (#339) Shows the full type representation, making it easier to see the entry format of parameter values. * feat: pop call parachain prototype * feat: dispaly arguments of extrinsic * refactor: structure similar to pop call contract * feat: parse all values for extrinsic/storage * refactor: signer in common * refactor: improve messages * feat: call parachain ui * fix: calls working * refactor: remove unused code * refactor: remove unused code * refactor: various fixes * refactor: various fixes * feat: add option to include params from command line * refactor: clean docs and refactor code * fix: tests * refactor: parse all the metadata again * refactor: reorganize and clean metadata functions * feat: display specific use cases to the user * refactor: predefined actions * fix: various fixes * fix: error message not supported for complex types * refactor: parse all metadata, including parameters at once * refactor: clean docs and move code * fix: format_type * test: fix unit test * refactor: clean the way to parse and prompt parameters * feat: add Purchase on-demand coretime use cases * test: add skip_confirm, move when prompt for the signer and create the integration test * test: call parachain ui unit test * test: pop-cli unit testing * test: pop-common unit tests * test: parse metadata unit tests * test: refactor and test processing parameters * test: comments and unit test in call functions * fix: clippy warnings * chore: fmt * feat: repeat call only if using guide UI * fix: clippy * refactor: various improvements * chore: parser for pallet and extrinsic input names * refactor: only move to pop_common the needed functions * refactor: improve test, docs and errors * test: fix unit tests * fix: reset_for_new_call when extrinisc is not supported * fix: build with parachain features * test: wait before call parachain in integration test * docs: minor improvements * test: migrate find_free_port to pop_common * test: fix increase waiting time * test: remove unnecesary test case * refactor: rename api with client * refactor: naming and docs * docs: improve docs and missing comments * test: remove unnecesary verbose * test: find_free_port * docs: improve parameter documentation * test: add missing test to sign_and_submit_extrinsic * fix: apply feedback from auxiliar PRs, remove unnecesary clones * docs: public modules * refactor: clean unused params * fix: mark all extrinsics that uses calls as parameter as unsupported * test: fix expect_select * docs: improve documentation * feat: submit extrinsic from call_data (#348) * feat: submit extrinsic from call_data * test: unit test for initialize_api_client * test: unit test for send_extrinsic_from_call_data * fix: CallData struct * fix: skip_confirm for send_extrinsic_from_call_data * chore: clippy * chore: fmt * refactor: minor doc and naming changes * refactor: remove unnecesary clones and return early when submit_extrinsic_from_call_data * chore: fmt * refactor: split decode_call_data logic outside sign_and_submit_extrinsic_with_call_data * feat: parse files when the argument values are very big (#363) * feat: parse files when the argument values are very big * test: unit test * chore: fmt * feat: file logic using the command line * fix: sequence arguments * test: fix unit test * refactor: remove prompting the user if input is file or value * refactor: parse_extrinsic_arguments * fix: CI deny * refactor: reorder Param derive macros * test: fix decode_call_data_works unit test * refactor: use Default derive macro and define constants for test values (#366) * feat: parse files when the argument values are very big * chore: fmt * feat: file logic using the command line * fix: sequence arguments * refactor: parse_extrinsic_arguments * refactor: use Default in pop_parachain structs * refactor: use Default in CallParachainCommand struct * refactor: use constant in tests * chore: fmt and small refactor * feat: flag sudo to wrap extrinsic (#349) * feat: submit extrinsic from call_data * test: unit test for initialize_api_client * feat: wrap call into a sudo call * test: add unit test to the new logic * fix: skip_confirm for send_extrinsic_from_call_data * chore: clippy * docs: rena… * Test wallet integration (#383) * chore: get_payload_works * test(wallet-integration): up contract get payload works * get paylaod from server works * test(wallet-integration): retrieve upload call data works * test(wallet-integration): retrieve instantiate call data works * style: better comments * test: try higher wait times in CI * test(wallet-integration): bump sleep time * test(wallet-integration): even more sleep time * test(wallet-integration): maybe a port problem ? * revert 0075e94 * test(wallet-integration): better unit tests * test(wallet-integration): wait for wallet signature * test(wallet-integration): assert on received payload * test(wallet-integration): use tokio spawn * test(wallet-integration): add some waiting time * test(wallet-integration): use cargo run * style: nightly fmt * test(wallet-integration): 500s sleep time * test(wallet-integration): ignore some tests * test(wallet-integration): get instantiate call data * test(wallet-integration): integration tests improvements * test(wallet-integration): bump sleep time * test(wallet-integration): merge integration tests * test(wallet-integration): remove sign_call_data test * test(wallet-integration): use random free port * test(wallet-integration): define gas_limit & proof_size * fix: merge issues --------- Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> * test(wallet-integration): remove sign_call_data test (#384) * chore: remove hex crate and use alternate means with test * chore: minor changes to comments, spinners, and deps * refactor: shared prompt, determine_signing_method * refactor: use extrinsic instead of transaction * chore: latest frontend * feat: send remark on contracts-node launch * chore: use cargo-contract crate release * feat(wallet): choose random port for server * feat(wallet): auto-open browser * refactor: find_free_port has preferred port * fix: conflicting tests merged to one * fix(wallet): port selection, update frontend * feat: update frontend with warning * [CI test] test: include instantiate_call_data_works (#389) * test: include instantiate_call_data_works * test: increase sleep time in run_contracts_node * fix: fmt --------- Co-authored-by: AlexD10S * refactor: review fixes and improvements (#390) * docs: align description across commands for consistency * refactor: use short = '' arg style recently adopted in main for consistency * test: remove unused variable * refactor: align function naming * refactor: remove unnecessary async * refactor: improve result handling Eliminates the expects and unwraps. * refactor: remove unnecessary clones * refactor: simplify code * style: formatting * docs: adds missing doc comments * refactor: remove unused function * refactor: use .expect() as server_url is parameter on public function * docs: add missing public function doc comments * refactor: use constant * refactor: module directory to file * refactor: use imports * refactor: eliminate duplication * docs: add missing doc comments * style: formatting * chore: use latest frontend * chore: latest frontend * chore: update frontend to remove trailing slash * chore: improve uploading / calling contract message to wait for finalization * test: improve `crates/pop-cli/src/common/contracts.rs` coverage (#391) * test: improve common/contracts coverage * style: fmt * refactor: include Cli in methods * style: typo * fix: improve tests * style: better comment * refactor: include cache path as param in check_contracts_node_and_prompt * docs: improve function docs * remove typo in comment --------- Co-authored-by: Alex Bean Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Co-authored-by: Daanvdplas --- Cargo.lock | 649 +++++++---- Cargo.toml | 12 +- crates/pop-cli/Cargo.toml | 14 +- crates/pop-cli/src/assets/index.html | 1024 +++++++++++++++++ crates/pop-cli/src/cli.rs | 1 - crates/pop-cli/src/commands/call/chain.rs | 134 ++- crates/pop-cli/src/commands/call/contract.rs | 181 ++- crates/pop-cli/src/commands/test/contract.rs | 8 +- crates/pop-cli/src/commands/up/contract.rs | 373 ++++-- crates/pop-cli/src/common/contracts.rs | 97 +- crates/pop-cli/src/common/mod.rs | 1 + crates/pop-cli/src/common/wallet.rs | 67 ++ crates/pop-cli/src/main.rs | 1 + crates/pop-cli/src/wallet_integration.rs | 579 ++++++++++ crates/pop-cli/tests/contract.rs | 115 +- crates/pop-cli/tests/parachain.rs | 2 +- crates/pop-common/src/lib.rs | 16 +- crates/pop-contracts/Cargo.toml | 6 +- crates/pop-contracts/src/call.rs | 56 +- crates/pop-contracts/src/lib.rs | 14 +- .../src/{node/mod.rs => node.rs} | 24 +- crates/pop-contracts/src/up.rs | 260 ++++- crates/pop-contracts/src/utils/metadata.rs | 2 +- crates/pop-contracts/tests/files/testing.wasm | Bin 3710 -> 0 bytes crates/pop-parachains/Cargo.toml | 2 +- .../pop-parachains/src/call/metadata/mod.rs | 7 +- crates/pop-parachains/src/call/mod.rs | 31 +- crates/pop-parachains/src/lib.rs | 7 +- 28 files changed, 3237 insertions(+), 446 deletions(-) create mode 100644 crates/pop-cli/src/assets/index.html create mode 100644 crates/pop-cli/src/common/wallet.rs create mode 100644 crates/pop-cli/src/wallet_integration.rs rename crates/pop-contracts/src/{node/mod.rs => node.rs} (91%) diff --git a/Cargo.lock b/Cargo.lock index bb211c82e..f581a91d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -724,7 +724,7 @@ dependencies = [ "parachains-runtimes-test-utils", "parity-scale-codec", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-parachain-info", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -749,7 +749,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", "staging-xcm-builder", "staging-xcm-executor", @@ -938,6 +938,61 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backoff" version = "0.4.0" @@ -1271,7 +1326,7 @@ dependencies = [ "serde", "sp-consensus-grandpa", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -1306,7 +1361,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -1339,7 +1394,7 @@ dependencies = [ "scale-info", "serde", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -1358,7 +1413,7 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -1379,7 +1434,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-state-machine 0.43.0", "sp-std", "sp-trie 37.0.0", @@ -1402,7 +1457,7 @@ dependencies = [ "sp-application-crypto 38.0.0", "sp-consensus-grandpa", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "sp-trie 37.0.0", ] @@ -1434,7 +1489,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", ] @@ -1451,7 +1506,7 @@ dependencies = [ "scale-info", "snowbridge-core", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", ] @@ -1493,7 +1548,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-keyring", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-tracing 17.0.1", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -1525,7 +1580,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "sp-trie 37.0.0", "staging-xcm 14.2.0", @@ -1552,9 +1607,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -1648,7 +1703,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "thiserror 1.0.69", @@ -1662,7 +1717,7 @@ checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "thiserror 2.0.6", @@ -1680,9 +1735,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" dependencies = [ "jobserver", "libc", @@ -1948,9 +2003,9 @@ checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" [[package]] name = "contract-build" -version = "5.0.1" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "857769855bf40d230e41baf6575cc44bd5e6869f69f88f45f6791da793f49a0c" +checksum = "b014fa89030235ecd8bdeb061ec97df326281b484f89ad3e17a79f08759c2f52" dependencies = [ "anyhow", "blake2", @@ -1967,7 +2022,7 @@ dependencies = [ "parity-scale-codec", "regex", "rustc_version 0.4.1", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "strum 0.26.3", @@ -1989,9 +2044,9 @@ dependencies = [ [[package]] name = "contract-extrinsics" -version = "5.0.1" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77ad38bef6454f97ca33481e960e9b105cb97f3794656071cbf50229445a89d" +checksum = "67d0c91349c31caec4d5e3c544b4bc4fcc7c0468dc49ee84a96a24f915464401" dependencies = [ "anyhow", "blake2", @@ -2022,13 +2077,13 @@ dependencies = [ [[package]] name = "contract-metadata" -version = "5.0.1" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733a6624ea05dd71050641c3cd9baff7a1445032a0082f0e55c800c078716424" +checksum = "83ae8bcb5f7c5ea033d05fa0bbffa4e762a5b69c0ce96e4188fb15385a01998b" dependencies = [ "anyhow", "impl-serde 0.5.0", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "url", @@ -2036,9 +2091,9 @@ dependencies = [ [[package]] name = "contract-transcode" -version = "5.0.1" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9131028be7b8eefdd9151a0a682ed428c1418d5d1ec142c35d2dbe6b9653c6" +checksum = "baca96dc859fd180eba5f15e468f59dc8c932a6b72f6b76f91b571b6743a9e7d" dependencies = [ "anyhow", "base58", @@ -2355,7 +2410,7 @@ dependencies = [ "scale-info", "sp-application-crypto 38.0.0", "sp-consensus-aura", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -2372,7 +2427,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", ] @@ -2403,7 +2458,7 @@ dependencies = [ "sp-externalities 0.29.0", "sp-inherents", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-state-machine 0.43.0", "sp-std", "sp-trie 37.0.0", @@ -2436,7 +2491,7 @@ dependencies = [ "frame-system", "pallet-session", "parity-scale-codec", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -2452,7 +2507,7 @@ dependencies = [ "parity-scale-codec", "polkadot-primitives 16.0.0", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -2467,7 +2522,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", ] @@ -2491,7 +2546,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", "staging-xcm-builder", "staging-xcm-executor", @@ -2509,7 +2564,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", ] @@ -2524,7 +2579,7 @@ dependencies = [ "polkadot-primitives 15.0.0", "sp-api", "sp-consensus-aura", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -2539,7 +2594,7 @@ dependencies = [ "polkadot-primitives 16.0.0", "scale-info", "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-trie 37.0.0", "staging-xcm 14.2.0", ] @@ -2584,7 +2639,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -2610,7 +2665,7 @@ dependencies = [ "pallet-asset-conversion", "parity-scale-codec", "polkadot-runtime-common", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", "staging-xcm-builder", "staging-xcm-executor", @@ -2625,7 +2680,7 @@ dependencies = [ "cumulus-primitives-core", "parity-scale-codec", "polkadot-primitives 16.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-state-machine 0.43.0", "sp-trie 37.0.0", ] @@ -2672,9 +2727,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e1ec88093d2abd9cf1b09ffd979136b8e922bf31cad966a8fe0d73233112ef" +checksum = "a5a32d755fe20281b46118ee4b507233311fb7a48a0cfd42f554b93640521a2f" dependencies = [ "cc", "cxxbridge-cmd", @@ -2686,9 +2741,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afa390d956ee7ccb41aeed7ed7856ab3ffb4fc587e7216be7e0f83e949b4e6c" +checksum = "11645536ada5d1c8804312cbffc9ab950f2216154de431de930da47ca6955199" dependencies = [ "cc", "codespan-reporting", @@ -2700,9 +2755,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c23bfff654d6227cbc83de8e059d2f8678ede5fc3a6c5a35d5c379983cc61e6" +checksum = "ebcc9c78e3c7289665aab921a2b394eaffe8bdb369aa18d81ffc0f534fd49385" dependencies = [ "clap", "codespan-reporting", @@ -2713,15 +2768,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c01b36e22051bc6928a78583f1621abaaf7621561c2ada1b00f7878fbe2caa" +checksum = "3a22a87bd9e78d7204d793261470a4c9d585154fddd251828d8aefbb5f74c3bf" [[package]] name = "cxxbridge-macro" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e14013136fac689345d17b9a6df55977251f11d333c0a571e8d963b55e1f95" +checksum = "1dfdb020ff8787c5daf6e0dca743005cc8782868faeadfbabb8824ede5cb1c72" dependencies = [ "proc-macro2", "quote", @@ -3560,7 +3615,7 @@ dependencies = [ "sp-application-crypto 38.0.0", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-runtime-interface 28.0.0", "sp-storage 21.0.0", "static_assertions", @@ -3578,7 +3633,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -3621,7 +3676,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-npos-elections", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -3639,7 +3694,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-tracing 17.0.1", ] @@ -3680,7 +3735,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -3715,7 +3770,7 @@ dependencies = [ "sp-inherents", "sp-io 38.0.0", "sp-metadata-ir", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", "sp-state-machine 0.43.0", "sp-std", @@ -3784,7 +3839,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "sp-version", "sp-weights", @@ -3802,7 +3857,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -3825,7 +3880,7 @@ dependencies = [ "frame-support", "parity-scale-codec", "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -4336,6 +4391,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.9.5" @@ -4449,7 +4510,7 @@ dependencies = [ "http 1.2.0", "hyper 1.5.1", "hyper-util", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pki-types", "tokio", "tokio-rustls 0.26.1", @@ -4975,6 +5036,15 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + [[package]] name = "is-terminal" version = "0.4.13" @@ -4986,6 +5056,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -5117,7 +5197,7 @@ dependencies = [ "http 1.2.0", "jsonrpsee-core", "pin-project", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pki-types", "rustls-platform-verifier", "soketto", @@ -5266,8 +5346,8 @@ dependencies = [ "tokio", "tokio-tungstenite", "tokio-util", - "tower", - "tower-http", + "tower 0.4.13", + "tower-http 0.4.4", "tracing", ] @@ -5716,6 +5796,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "matrixmultiply" version = "0.3.9" @@ -6126,6 +6212,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "open" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.68" @@ -6238,7 +6335,7 @@ dependencies = [ "sp-core 34.0.0", "sp-crypto-hashing", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6257,7 +6354,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6276,7 +6373,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6291,7 +6388,7 @@ dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6306,7 +6403,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6324,7 +6421,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6341,7 +6438,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6357,7 +6454,7 @@ dependencies = [ "pallet-assets", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6372,7 +6469,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6389,7 +6486,7 @@ dependencies = [ "scale-info", "sp-application-crypto 38.0.0", "sp-consensus-aura", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6405,7 +6502,7 @@ dependencies = [ "scale-info", "sp-application-crypto 38.0.0", "sp-authority-discovery", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6419,7 +6516,7 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6441,7 +6538,7 @@ dependencies = [ "sp-consensus-babe", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-staking 36.0.0", ] @@ -6464,7 +6561,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-tracing 17.0.1", ] @@ -6481,7 +6578,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6499,7 +6596,7 @@ dependencies = [ "scale-info", "serde", "sp-consensus-beefy", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-staking 36.0.0", ] @@ -6526,7 +6623,7 @@ dependencies = [ "sp-consensus-beefy", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-state-machine 0.43.0", ] @@ -6545,7 +6642,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6564,7 +6661,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-consensus-grandpa", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -6583,7 +6680,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "sp-trie 37.0.0", ] @@ -6605,7 +6702,7 @@ dependencies = [ "pallet-bridge-grandpa", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -6630,7 +6727,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -6650,7 +6747,7 @@ dependencies = [ "sp-api", "sp-arithmetic", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6669,7 +6766,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6688,7 +6785,7 @@ dependencies = [ "parity-scale-codec", "rand", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -6706,7 +6803,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6721,7 +6818,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6749,7 +6846,7 @@ dependencies = [ "sp-api", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -6785,7 +6882,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-tracing 17.0.1", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -6855,7 +6952,7 @@ dependencies = [ "scale-info", "serde", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6874,7 +6971,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6889,7 +6986,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -6908,7 +7005,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6924,7 +7021,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6946,7 +7043,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-npos-elections", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "strum 0.26.3", ] @@ -6961,7 +7058,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "sp-npos-elections", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -6979,7 +7076,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-npos-elections", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -6998,7 +7095,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -7018,7 +7115,7 @@ dependencies = [ "sp-core 34.0.0", "sp-inherents", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7039,7 +7136,7 @@ dependencies = [ "sp-consensus-grandpa", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-staking 36.0.0", ] @@ -7058,7 +7155,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7077,7 +7174,7 @@ dependencies = [ "sp-application-crypto 38.0.0", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -7095,7 +7192,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-keyring", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7109,7 +7206,7 @@ dependencies = [ "parity-scale-codec", "safe-mix", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7123,7 +7220,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7140,7 +7237,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7159,7 +7256,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", ] @@ -7178,7 +7275,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7198,7 +7295,7 @@ dependencies = [ "sp-arithmetic", "sp-io 38.0.0", "sp-mixnet", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7216,7 +7313,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-mmr-primitives", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7232,7 +7329,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7249,7 +7346,7 @@ dependencies = [ "pallet-nfts", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7267,7 +7364,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7294,7 +7391,7 @@ dependencies = [ "scale-info", "sp-arithmetic", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7310,7 +7407,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7327,7 +7424,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", "sp-tracing 17.0.1", ] @@ -7348,7 +7445,7 @@ dependencies = [ "pallet-staking", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-runtime-interface 28.0.0", "sp-staking 36.0.0", ] @@ -7377,7 +7474,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -7401,7 +7498,7 @@ dependencies = [ "pallet-staking", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -7420,7 +7517,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-metadata-ir", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7438,7 +7535,7 @@ dependencies = [ "scale-info", "serde", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7455,7 +7552,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7470,7 +7567,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7489,7 +7586,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7504,7 +7601,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7522,7 +7619,7 @@ dependencies = [ "serde", "sp-arithmetic", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7539,7 +7636,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7567,7 +7664,7 @@ dependencies = [ "sp-api", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -7583,7 +7680,7 @@ dependencies = [ "frame-system", "parity-wasm", "polkavm-linker 0.10.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "tempfile", "toml 0.8.19", ] @@ -7615,7 +7712,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-tracing 17.0.1", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -7659,7 +7756,7 @@ dependencies = [ "pallet-staking", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -7675,7 +7772,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7694,7 +7791,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7713,7 +7810,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7730,7 +7827,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", ] @@ -7745,7 +7842,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7763,7 +7860,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-staking 36.0.0", "sp-state-machine 0.43.0", @@ -7783,7 +7880,7 @@ dependencies = [ "pallet-staking", "parity-scale-codec", "rand", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", ] @@ -7797,7 +7894,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7815,7 +7912,7 @@ dependencies = [ "scale-info", "sp-arithmetic", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7836,7 +7933,7 @@ dependencies = [ "serde", "sp-application-crypto 38.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -7875,7 +7972,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7892,7 +7989,7 @@ dependencies = [ "sp-api", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-statement-store", ] @@ -7909,7 +8006,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7927,7 +8024,7 @@ dependencies = [ "scale-info", "sp-inherents", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-storage 21.0.0", "sp-timestamp", ] @@ -7948,7 +8045,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7964,7 +8061,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -7976,7 +8073,7 @@ dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", ] @@ -7996,7 +8093,7 @@ dependencies = [ "serde", "sp-inherents", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-transaction-storage-proof", ] @@ -8016,7 +8113,7 @@ dependencies = [ "scale-info", "serde", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8034,7 +8131,7 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8049,7 +8146,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8065,7 +8162,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8080,7 +8177,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8095,7 +8192,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8115,7 +8212,7 @@ dependencies = [ "serde", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", "staging-xcm-builder", "staging-xcm-executor", @@ -8136,7 +8233,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", "staging-xcm-builder", "staging-xcm-executor", @@ -8158,7 +8255,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -8179,7 +8276,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -8209,7 +8306,7 @@ dependencies = [ "sp-consensus-aura", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-parachain-info", "staging-xcm 14.2.0", "staging-xcm-executor", @@ -8239,7 +8336,7 @@ dependencies = [ "sp-consensus-aura", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-tracing 17.0.1", "staging-parachain-info", "staging-xcm 14.2.0", @@ -8374,6 +8471,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -8524,7 +8627,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -8540,7 +8643,7 @@ dependencies = [ "scale-info", "serde", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", ] @@ -8567,7 +8670,7 @@ dependencies = [ "sp-inherents", "sp-io 38.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 34.0.0", ] @@ -8594,7 +8697,7 @@ dependencies = [ "sp-inherents", "sp-io 38.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -8639,7 +8742,7 @@ dependencies = [ "sp-inherents", "sp-io 38.0.0", "sp-npos-elections", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-staking 36.0.0", "staging-xcm 14.2.0", @@ -8702,7 +8805,7 @@ dependencies = [ "sp-inherents", "sp-io 38.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-staking 36.0.0", "sp-std", @@ -8917,7 +9020,7 @@ dependencies = [ "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-runtime-interface 28.0.0", "sp-session", "sp-staking 36.0.0", @@ -8968,7 +9071,7 @@ dependencies = [ "sp-inherents", "sp-io 38.0.0", "sp-offchain", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-session", "sp-storage 21.0.0", "sp-transaction-pool", @@ -9251,13 +9354,16 @@ version = "0.5.0" dependencies = [ "anyhow", "assert_cmd", + "axum", "clap", "cliclack", "console", + "contract-extrinsics", "dirs", "duct", "env_logger 0.11.5", "git2", + "open", "os_info", "pop-common", "pop-contracts", @@ -9265,13 +9371,17 @@ dependencies = [ "pop-telemetry", "predicates", "reqwest 0.12.9", + "serde", "serde_json", "sp-core 32.0.0", "sp-weights", "strum 0.26.3", "strum_macros 0.26.4", + "subxt", + "subxt-signer", "tempfile", "tokio", + "tower-http 0.6.2", "url", ] @@ -9328,6 +9438,8 @@ dependencies = [ "sp-weights", "strum 0.26.3", "strum_macros 0.26.4", + "subxt", + "subxt-signer", "tar", "tempfile", "thiserror 1.0.69", @@ -9346,7 +9458,6 @@ dependencies = [ "duct", "flate2", "glob", - "hex", "indexmap 2.7.0", "mockito", "pop-common", @@ -9354,6 +9465,7 @@ dependencies = [ "scale-info", "scale-value", "serde_json", + "sp-core 32.0.0", "strum 0.26.3", "strum_macros 0.26.4", "subxt", @@ -9710,9 +9822,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -9990,7 +10102,7 @@ dependencies = [ "polkadot-runtime-common", "smallvec", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -10090,7 +10202,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.23", + "semver 1.0.24", ] [[package]] @@ -10134,9 +10246,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "log", "once_cell", @@ -10192,9 +10304,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" [[package]] name = "rustls-platform-verifier" @@ -10207,7 +10319,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", @@ -10842,9 +10954,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -10936,6 +11048,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.19" @@ -11185,7 +11307,7 @@ dependencies = [ "enumn", "parity-scale-codec", "paste", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -11334,7 +11456,7 @@ dependencies = [ "snowbridge-milagro-bls", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "ssz_rs", "ssz_rs_derive", @@ -11358,7 +11480,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -11381,7 +11503,7 @@ dependencies = [ "serde", "serde-big-array", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -11409,7 +11531,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -11446,7 +11568,7 @@ dependencies = [ "snowbridge-pallet-ethereum-client-fixtures", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "static_assertions", ] @@ -11486,7 +11608,7 @@ dependencies = [ "snowbridge-router-primitives", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-executor", @@ -11524,7 +11646,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", ] @@ -11543,7 +11665,7 @@ dependencies = [ "snowbridge-core", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-executor", @@ -11563,7 +11685,7 @@ dependencies = [ "snowbridge-core", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-executor", @@ -11612,7 +11734,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-keyring", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-parachain-info", "staging-xcm 14.2.0", "staging-xcm-executor", @@ -11671,7 +11793,7 @@ dependencies = [ "sp-core 34.0.0", "sp-externalities 0.29.0", "sp-metadata-ir", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-runtime-interface 28.0.0", "sp-state-machine 0.43.0", "sp-trie 37.0.0", @@ -11747,7 +11869,7 @@ dependencies = [ "scale-info", "sp-api", "sp-application-crypto 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -11758,7 +11880,7 @@ checksum = "74738809461e3d4bd707b5b94e0e0c064a623a74a6a8fe5c98514417a02858dd" dependencies = [ "sp-api", "sp-inherents", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -11774,7 +11896,7 @@ dependencies = [ "sp-application-crypto 38.0.0", "sp-consensus-slots", "sp-inherents", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-timestamp", ] @@ -11793,7 +11915,7 @@ dependencies = [ "sp-consensus-slots", "sp-core 34.0.0", "sp-inherents", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-timestamp", ] @@ -11814,7 +11936,7 @@ dependencies = [ "sp-io 38.0.0", "sp-keystore 0.40.0", "sp-mmr-primitives", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", "strum 0.26.3", ] @@ -11834,7 +11956,7 @@ dependencies = [ "sp-application-crypto 38.0.0", "sp-core 34.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -11846,7 +11968,7 @@ dependencies = [ "parity-scale-codec", "sp-api", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12112,7 +12234,7 @@ dependencies = [ "scale-info", "serde_json", "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12125,7 +12247,7 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "thiserror 1.0.69", ] @@ -12190,7 +12312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c0e20624277f578b27f44ecfbe2ebc2e908488511ee2c900c5281599f700ab3" dependencies = [ "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "strum 0.26.3", ] @@ -12265,7 +12387,7 @@ dependencies = [ "sp-api", "sp-core 34.0.0", "sp-debug-derive", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "thiserror 1.0.69", ] @@ -12280,7 +12402,7 @@ dependencies = [ "serde", "sp-arithmetic", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12291,7 +12413,7 @@ checksum = "2d9de237d72ecffd07f90826eef18360208b16d8de939d54e61591fac0fcbf99" dependencies = [ "sp-api", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12332,9 +12454,9 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "39.0.2" +version = "39.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f23be7c79a85581029676a73265c107c5469157e3444c8c640fdbaa8bfed0" +checksum = "ef567865c042b9002dfa44b8fc850fe611038acdf1e382e539495015f60f692f" dependencies = [ "docify", "either", @@ -12442,7 +12564,7 @@ dependencies = [ "sp-api", "sp-core 34.0.0", "sp-keystore 0.40.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-staking 36.0.0", ] @@ -12457,7 +12579,7 @@ dependencies = [ "scale-info", "serde", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12471,7 +12593,7 @@ dependencies = [ "scale-info", "serde", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12535,7 +12657,7 @@ dependencies = [ "sp-core 34.0.0", "sp-crypto-hashing", "sp-externalities 0.29.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-runtime-interface 28.0.0", "thiserror 1.0.69", "x25519-dalek", @@ -12583,7 +12705,7 @@ dependencies = [ "async-trait", "parity-scale-codec", "sp-inherents", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "thiserror 1.0.69", ] @@ -12619,7 +12741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4bf251059485a7dd38fe4afeda8792983511cc47f342ff4695e2dcae6b5247" dependencies = [ "sp-api", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12633,7 +12755,7 @@ dependencies = [ "scale-info", "sp-core 34.0.0", "sp-inherents", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-trie 37.0.0", ] @@ -12697,7 +12819,7 @@ dependencies = [ "scale-info", "serde", "sp-crypto-hashing-proc-macro", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "sp-version-proc-macro", "thiserror 1.0.69", @@ -12828,7 +12950,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", ] [[package]] @@ -12865,7 +12987,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", "xcm-procedural 10.1.0", ] @@ -12887,7 +13009,7 @@ dependencies = [ "scale-info", "sp-arithmetic", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", "staging-xcm 14.2.0", "staging-xcm-executor", @@ -12908,7 +13030,7 @@ dependencies = [ "sp-arithmetic", "sp-core 34.0.0", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", "staging-xcm 14.2.0", "tracing", @@ -13397,7 +13519,7 @@ dependencies = [ "polkadot-core-primitives", "rococo-runtime-constants", "smallvec", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "staging-xcm 14.2.0", "westend-runtime-constants", ] @@ -13593,7 +13715,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.19", + "rustls 0.23.20", "tokio", ] @@ -13733,6 +13855,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-http" version = "0.4.4" @@ -13746,9 +13884,34 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "http-range-header", + "http-range-header 0.3.1", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "http-range-header 0.4.2", + "httpdate", "mime", + "mime_guess", + "percent-encoding", "pin-project-lite", + "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -14399,7 +14562,7 @@ dependencies = [ "bitflags 2.6.0", "hashbrown 0.14.5", "indexmap 2.7.0", - "semver 1.0.23", + "semver 1.0.24", "serde", ] @@ -14647,7 +14810,7 @@ dependencies = [ "polkadot-runtime-common", "smallvec", "sp-core 34.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-weights", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -15092,7 +15255,7 @@ dependencies = [ "polkadot-runtime-parachains", "scale-info", "sp-io 38.0.0", - "sp-runtime 39.0.2", + "sp-runtime 39.0.3", "sp-std", "staging-xcm 14.2.0", "staging-xcm-builder", @@ -15236,9 +15399,9 @@ dependencies = [ [[package]] name = "zombienet-configuration" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22860eef7e651d6e0aa7e37fc3bcba3c2c8b7bd1c140d7ea929caacf2b7fc726" +checksum = "d716b3ff8112d98ced15f53b0c72454f8cde533fe2b68bb04379228961efbd80" dependencies = [ "anyhow", "lazy_static", @@ -15256,9 +15419,9 @@ dependencies = [ [[package]] name = "zombienet-orchestrator" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b1b2fd2db3153155f21cb84cdd8e5d6faefc3043353b8c90661c44f4660da" +checksum = "4098a7d33b729b59e32c41a87aa4d484bd1b8771a059bbd4edfb4d430b3b2d74" dependencies = [ "anyhow", "async-trait", @@ -15289,9 +15452,9 @@ dependencies = [ [[package]] name = "zombienet-prom-metrics-parser" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61ce9c6b2d43be864ad34328d05794079381807f5d77c737a062486966347f" +checksum = "961e30be45b34f6ebeabf29ee2f47b0cd191ea62e40c064752572207509a6f5c" dependencies = [ "pest", "pest_derive", @@ -15300,9 +15463,9 @@ dependencies = [ [[package]] name = "zombienet-provider" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99cc7c143f1145bda2b2f5a945b8040a898a85fc029dba51f220395a7188d9d" +checksum = "ab0f7f01780b7c99a6c40539d195d979f234305f32808d547438b50829d44262" dependencies = [ "anyhow", "async-trait", @@ -15331,9 +15494,9 @@ dependencies = [ [[package]] name = "zombienet-sdk" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e5abdad4ad32c1c06cb8fdc4507db026f65987cb5c46ae4224118cc496097b" +checksum = "99a3c5f2d657235b3ab7dc384677e63cde21983029e99106766ecd49e9f8d7f3" dependencies = [ "async-trait", "futures", @@ -15349,9 +15512,9 @@ dependencies = [ [[package]] name = "zombienet-support" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e3310631948f8bb4d394c160c4b063889a4e0c6beadd6b95f9b62d1616ab93c" +checksum = "296f887ea88e07edd771f8e1d0dec5297a58b422f4b884a6292a21ebe03277cb" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 86a7a928d..44dd1fbd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,13 +50,12 @@ subxt = "0.38.0" ink_env = "5.0.0" sp-core = "32.0.0" sp-weights = "31.0.0" -contract-build = "5.0.0" -contract-extrinsics = "5.0.0" -contract-transcode = "5.0.0" scale-info = { version = "2.11.4", default-features = false, features = ["derive"] } scale-value = { version = "0.17.0", default-features = false, features = ["from-string", "parser-ss58"] } +contract-build = "5.0.2" +contract-extrinsics = "5.0.2" +contract-transcode = "5.0.2" heck = "0.5.0" -hex = { version = "0.4.3", default-features = false } # parachains askama = "0.12" @@ -77,3 +76,8 @@ console = "0.15" os_info = { version = "3", default-features = false } strum = "0.26" strum_macros = "0.26" + +# wallet-integration +axum = "0.7.9" +open = "5.3.1" +tower-http = "0.6.2" diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index c9761b642..34e51111d 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -18,6 +18,7 @@ duct.workspace = true env_logger.workspace = true os_info.workspace = true reqwest.workspace = true +serde = { workspace = true, features = ["derive"] } serde_json.workspace = true tempfile.workspace = true tokio.workspace = true @@ -27,12 +28,12 @@ url.workspace = true clap.workspace = true cliclack.workspace = true console.workspace = true +sp-core.workspace = true strum.workspace = true strum_macros.workspace = true # contracts pop-contracts = { path = "../pop-contracts", version = "0.5.0", optional = true } -sp-core = { workspace = true, optional = true } sp-weights = { workspace = true, optional = true } # parachains @@ -46,12 +47,21 @@ pop-telemetry = { path = "../pop-telemetry", version = "0.5.0", optional = true # common pop-common = { path = "../pop-common", version = "0.5.0" } +# wallet-integration +axum.workspace = true +open.workspace = true +tower-http = { workspace = true, features = ["fs", "cors"] } + [dev-dependencies] assert_cmd.workspace = true +contract-extrinsics.workspace = true predicates.workspace = true +subxt.workspace = true +subxt-signer.workspace = true +sp-weights.workspace = true [features] default = ["contract", "parachain", "telemetry"] -contract = ["dep:pop-contracts", "dep:sp-core", "dep:sp-weights", "dep:dirs"] +contract = ["dep:pop-contracts", "dep:sp-weights", "dep:dirs"] parachain = ["dep:pop-parachains", "dep:dirs"] telemetry = ["dep:pop-telemetry"] diff --git a/crates/pop-cli/src/assets/index.html b/crates/pop-cli/src/assets/index.html new file mode 100644 index 000000000..d1d5a8ef7 --- /dev/null +++ b/crates/pop-cli/src/assets/index.html @@ -0,0 +1,1024 @@ + + + + + + + + Pop CLI Signing Portal + + + + + +
+ + + diff --git a/crates/pop-cli/src/cli.rs b/crates/pop-cli/src/cli.rs index 6c6bb3bce..f98c934e9 100644 --- a/crates/pop-cli/src/cli.rs +++ b/crates/pop-cli/src/cli.rs @@ -150,7 +150,6 @@ impl traits::Confirm for Confirm { /// A input prompt using cliclack. struct Input(cliclack::Input); - impl traits::Input for Input { /// Sets the default value for the input. fn default_input(mut self, value: &str) -> Self { diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 36b4dbd1e..41348826f 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -2,14 +2,17 @@ use std::path::Path; -use crate::cli::{self, traits::*}; +use crate::{ + cli::{self, traits::*}, + common::wallet::{prompt_to_use_wallet, request_signature}, +}; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, - sign_and_submit_extrinsic, supported_actions, Action, CallData, DynamicPayload, Function, - OnlineClient, Pallet, Param, SubstrateConfig, + sign_and_submit_extrinsic, submit_signed_extrinsic, supported_actions, Action, CallData, + DynamicPayload, Function, OnlineClient, Pallet, Param, Payload, SubstrateConfig, }; use url::Url; @@ -40,6 +43,15 @@ pub struct CallChainCommand { /// - with a password "//Alice///SECRET_PASSWORD" #[arg(short, long)] suri: Option, + /// Use a browser extension wallet to sign the extrinsic. + #[arg( + name = "use-wallet", + short = 'w', + long, + default_value = "false", + conflicts_with = "suri" + )] + use_wallet: bool, /// SCALE encoded bytes representing the call data of the extrinsic. #[arg(name = "call", short, long, conflicts_with_all = ["pallet", "function", "args"])] call_data: Option, @@ -95,7 +107,14 @@ impl CallChainCommand { }; // Sign and submit the extrinsic. - if let Err(e) = call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await { + let result = if self.use_wallet { + let call_data = xt.encode_call_data(&chain.client.metadata())?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli).await + } else { + call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await + }; + + if let Err(e) = result { display_message(&e.to_string(), false, &mut cli)?; break; } @@ -197,12 +216,8 @@ impl CallChainCommand { // sudo. self.configure_sudo(chain, cli)?; - // Resolve who is signing the extrinsic. - let suri = match self.suri.as_ref() { - Some(suri) => suri.clone(), - None => - cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()?, - }; + let (use_wallet, suri) = self.determine_signing_method(cli)?; + self.use_wallet = use_wallet; return Ok(Call { function: function.clone(), @@ -210,6 +225,7 @@ impl CallChainCommand { suri, skip_confirm: self.skip_confirm, sudo: self.sudo, + use_wallet: self.use_wallet, }); } } @@ -222,11 +238,18 @@ impl CallChainCommand { call_data: &str, cli: &mut impl Cli, ) -> Result<()> { - // Resolve who is signing the extrinsic. - let suri = match self.suri.as_ref() { - Some(suri) => suri, - None => &cli.input("Signer of the extrinsic:").default_input(DEFAULT_URI).interact()?, - }; + let (use_wallet, suri) = self.determine_signing_method(cli)?; + + // Perform signing steps with wallet integration and return early. + if use_wallet { + let call_data_bytes = + decode_call_data(call_data).map_err(|err| anyhow!("{}", format!("{err:?}")))?; + submit_extrinsic_with_wallet(client, url, call_data_bytes, cli) + .await + .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + display_message("Call complete.", true, cli)?; + return Ok(()); + } cli.info(format!("Encoded call data: {}", call_data))?; if !self.skip_confirm && !cli.confirm("Do you want to submit the extrinsic?") @@ -244,7 +267,7 @@ impl CallChainCommand { spinner.start("Signing and submitting the extrinsic and then waiting for finalization, please be patient..."); let call_data_bytes = decode_call_data(call_data).map_err(|err| anyhow!("{}", format!("{err:?}")))?; - let result = sign_and_submit_extrinsic(client, url, CallData::new(call_data_bytes), suri) + let result = sign_and_submit_extrinsic(client, url, CallData::new(call_data_bytes), &suri) .await .map_err(|err| anyhow!("{}", format!("{err:?}")))?; @@ -253,6 +276,29 @@ impl CallChainCommand { Ok(()) } + // Resolve who is signing the extrinsic. If a `suri` was provided via the command line, + // skip the prompt. + fn determine_signing_method(&self, cli: &mut impl Cli) -> Result<(bool, String)> { + let mut use_wallet = self.use_wallet; + let suri = match self.suri.as_ref() { + Some(suri) => suri.clone(), + None => + if !self.use_wallet { + if prompt_to_use_wallet(cli)? { + use_wallet = true; + DEFAULT_URI.to_string() + } else { + cli.input("Signer of the extrinsic:") + .default_input(DEFAULT_URI) + .interact()? + } + } else { + DEFAULT_URI.to_string() + }, + }; + Ok((use_wallet, suri)) + } + // Checks if the chain has the Sudo pallet and prompts the user to confirm if they want to // execute the call via `sudo`. fn configure_sudo(&mut self, chain: &Chain, cli: &mut impl Cli) -> Result<()> { @@ -283,6 +329,7 @@ impl CallChainCommand { self.function = None; self.args.clear(); self.sudo = false; + self.use_wallet = false; } // Function to check if all required fields are specified. @@ -334,6 +381,8 @@ struct Call { /// - for a dev account "//Alice" /// - with a password "//Alice///SECRET_PASSWORD" suri: String, + /// Whether to use your browser wallet to sign the extrinsic. + use_wallet: bool, /// Whether to automatically sign and submit the extrinsic without prompting for confirmation. skip_confirm: bool, /// Whether to dispatch the function call with `Root` origin. @@ -411,7 +460,12 @@ impl Call { .collect(); full_message.push_str(&format!(" --args {}", args.join(" "))); } - full_message.push_str(&format!(" --url {} --suri {}", chain.url, self.suri)); + full_message.push_str(&format!(" --url {}", chain.url)); + if self.use_wallet { + full_message.push_str(" --use-wallet"); + } else { + full_message.push_str(&format!(" --suri {}", self.suri)); + } if self.sudo { full_message.push_str(" --sudo"); } @@ -419,6 +473,32 @@ impl Call { } } +// Sign and submit an extrinsic using wallet integration. +async fn submit_extrinsic_with_wallet( + client: &OnlineClient, + url: &Url, + call_data: Vec, + cli: &mut impl Cli, +) -> Result<()> { + let maybe_payload = request_signature(call_data, url.to_string()).await?; + if let Some(payload) = maybe_payload { + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner.start( + "Submitting the extrinsic and then waiting for finalization, please be patient...", + ); + + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); + } else { + display_message("No signed payload received.", false, cli)?; + } + Ok(()) +} + // Displays a message to the user, with formatting based on the success status. fn display_message(message: &str, success: bool, cli: &mut impl Cli) -> Result<()> { if success { @@ -589,7 +669,7 @@ fn parse_function_name(name: &str) -> Result { #[cfg(test)] mod tests { use super::*; - use crate::cli::MockCli; + use crate::{cli::MockCli, common::wallet::USE_WALLET_PROMPT}; use tempfile::tempdir; use url::Url; @@ -642,7 +722,7 @@ mod tests { ) .expect_input("The value for `remark` might be too large to enter. You may enter the path to a file instead.", "0x11".into()) .expect_confirm("Would you like to dispatch this function call with `Root` origin?", true) - .expect_input("Signer of the extrinsic:", "//Bob".into()); + .expect_confirm(USE_WALLET_PROMPT, true); let chain = call_config.configure_chain(&mut cli).await?; assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); @@ -651,9 +731,10 @@ mod tests { assert_eq!(call_chain.function.pallet, "System"); assert_eq!(call_chain.function.name, "remark"); assert_eq!(call_chain.args, ["0x11".to_string()].to_vec()); - assert_eq!(call_chain.suri, "//Bob"); + assert_eq!(call_chain.suri, "//Alice"); // Default value + assert!(call_chain.use_wallet); assert!(call_chain.sudo); - assert_eq!(call_chain.display(&chain), "pop call chain --pallet System --function remark --args \"0x11\" --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Bob --sudo"); + assert_eq!(call_chain.display(&chain), "pop call chain --pallet System --function remark --args \"0x11\" --url wss://rpc1.paseo.popnetwork.xyz/ --use-wallet --sudo"); cli.verify() } @@ -714,6 +795,7 @@ mod tests { }, args: vec!["0x11".to_string()].to_vec(), suri: DEFAULT_URI.to_string(), + use_wallet: false, skip_confirm: false, sudo: false, }; @@ -753,6 +835,7 @@ mod tests { function: find_dispatchable_by_name(&pallets, "System", "remark")?.clone(), args: vec!["0x11".to_string()].to_vec(), suri: DEFAULT_URI.to_string(), + use_wallet: false, skip_confirm: false, sudo: false, }; @@ -776,11 +859,13 @@ mod tests { args: vec![].to_vec(), url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), suri: None, + use_wallet: false, skip_confirm: false, call_data: Some("0x00000411".to_string()), sudo: false, }; let mut cli = MockCli::new() + .expect_confirm(USE_WALLET_PROMPT, false) .expect_input("Signer of the extrinsic:", "//Bob".into()) .expect_confirm("Do you want to submit the extrinsic?", false) .expect_outro_cancel("Extrinsic with call data 0x00000411 was not submitted."); @@ -803,8 +888,9 @@ mod tests { pallet: None, function: None, args: vec![].to_vec(), - url: Some(Url::parse("wss://polkadot-rpc.publicnode.com")?), + url: Some(Url::parse(POLKADOT_NETWORK_URL)?), suri: Some("//Alice".to_string()), + use_wallet: false, skip_confirm: false, call_data: Some("0x00000411".to_string()), sudo: true, @@ -836,6 +922,7 @@ mod tests { function: Some("remark".to_string()), args: vec!["0x11".to_string()].to_vec(), url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + use_wallet: true, suri: Some(DEFAULT_URI.to_string()), skip_confirm: false, call_data: None, @@ -846,6 +933,7 @@ mod tests { assert_eq!(call_config.function, None); assert_eq!(call_config.args.len(), 0); assert!(!call_config.sudo); + assert!(!call_config.use_wallet); Ok(()) } @@ -857,6 +945,7 @@ mod tests { args: vec!["0x11".to_string()].to_vec(), url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), suri: Some(DEFAULT_URI.to_string()), + use_wallet: false, skip_confirm: false, call_data: None, sudo: false, @@ -875,6 +964,7 @@ mod tests { args: vec!["2000".to_string(), "0x1".to_string(), "0x12".to_string()].to_vec(), url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), suri: Some(DEFAULT_URI.to_string()), + use_wallet: false, call_data: None, skip_confirm: false, sudo: false, diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index e83a871d2..ab7a826fd 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -2,17 +2,22 @@ use crate::{ cli::{self, traits::*}, - common::contracts::has_contract_been_built, + common::{ + contracts::has_contract_been_built, + wallet::{prompt_to_use_wallet, request_signature}, + }, }; use anyhow::{anyhow, Result}; use clap::Args; use cliclack::spinner; +use pop_common::{DefaultConfig, Keypair}; use pop_contracts::{ - build_smart_contract, call_smart_contract, dry_run_call, dry_run_gas_estimate_call, - get_messages, parse_account, set_up_call, CallOpts, Verbosity, + build_smart_contract, call_smart_contract, call_smart_contract_from_signed_payload, + dry_run_call, dry_run_gas_estimate_call, get_call_payload, get_message, get_messages, + parse_account, set_up_call, CallExec, CallOpts, DefaultEnvironment, Verbosity, }; use sp_weights::Weight; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; const DEFAULT_URL: &str = "ws://localhost:9944/"; const DEFAULT_URI: &str = "//Alice"; @@ -54,6 +59,15 @@ pub struct CallContractCommand { /// - with a password "//Alice///SECRET_PASSWORD" #[arg(short, long, default_value = DEFAULT_URI)] suri: String, + /// Use a browser extension wallet to sign the extrinsic. + #[arg( + name = "use-wallet", + long, + short = 'w', + default_value = "false", + conflicts_with = "suri" + )] + use_wallet: bool, /// Submit an extrinsic for on-chain execution. #[arg(short = 'x', long)] execute: bool, @@ -115,14 +129,19 @@ impl CallContractCommand { full_message.push_str(&format!(" --gas {}", gas_limit)); } if let Some(proof_size) = self.proof_size { - full_message.push_str(&format!(" --proof_size {}", proof_size)); + full_message.push_str(&format!(" --proof-size {}", proof_size)); + } + full_message.push_str(&format!(" --url {}", self.url)); + if self.use_wallet { + full_message.push_str(" --use-wallet"); + } else { + full_message.push_str(&format!(" --suri {}", self.suri)); } - full_message.push_str(&format!(" --url {} --suri {}", self.url, self.suri)); if self.execute { full_message.push_str(" --execute"); } if self.dry_run { - full_message.push_str(" --dry_run"); + full_message.push_str(" --dry-run"); } full_message } @@ -165,7 +184,7 @@ impl CallContractCommand { fn is_contract_build_required(&self) -> bool { self.path .as_ref() - .map(|p| p.is_dir() && !has_contract_been_built(Some(&p))) + .map(|p| p.is_dir() && !has_contract_been_built(Some(p))) .unwrap_or_default() } @@ -303,18 +322,22 @@ impl CallContractCommand { self.proof_size = proof_size_input.parse::().ok(); // If blank or bad input, estimate it. } - // Resolve who is calling the contract. - if self.suri == DEFAULT_URI { - // Prompt for uri. - self.suri = cli - .input("Signer calling the contract:") - .placeholder("//Alice") - .default_input("//Alice") - .interact()?; - }; + // Resolve who is calling the contract. If a `suri` was provided via the command line, skip + // the prompt. + if self.suri == DEFAULT_URI && !self.use_wallet && message.mutates { + if prompt_to_use_wallet(cli)? { + self.use_wallet = true; + } else { + self.suri = cli + .input("Signer calling the contract:") + .placeholder("//Alice") + .default_input("//Alice") + .interact()?; + }; + } // Finally prompt for confirmation. - let is_call_confirmed = if message.mutates && !self.dev_mode { + let is_call_confirmed = if message.mutates && !self.dev_mode && !self.use_wallet { cli.confirm("Do you want to execute the call? (Selecting 'No' will perform a dry run)") .initial_value(true) .interact()? @@ -340,6 +363,14 @@ impl CallContractCommand { return Err(anyhow!("Please specify the message to call.")); }, }; + // Disable wallet signing and display warning if the call is read-only. + let message_metadata = + get_message(self.path.as_deref().unwrap_or_else(|| Path::new("./")), &message)?; + if !message_metadata.mutates && self.use_wallet { + cli.warning("NOTE: Signing is not required for this read-only call. The '--use-wallet' flag will be ignored.")?; + self.use_wallet = false; + } + let contract = match &self.contract { Some(contract) => contract.to_string(), None => { @@ -366,6 +397,12 @@ impl CallContractCommand { }, }; + // Perform signing steps with wallet integration, skipping secure signing for query-only + // operations. + if self.use_wallet { + self.execute_with_wallet(call_exec, cli).await?; + return self.finalize_execute_call(cli, prompt_to_repeat_call).await; + } if self.dry_run { let spinner = spinner(); spinner.start("Doing a dry run to estimate the gas..."); @@ -386,6 +423,7 @@ impl CallContractCommand { let spinner = spinner(); spinner.start("Calling the contract..."); let call_dry_run_result = dry_run_call(&call_exec).await?; + spinner.stop(""); cli.info(format!("Result: {}", call_dry_run_result))?; cli.warning("Your call has not been executed.")?; } else { @@ -414,7 +452,15 @@ impl CallContractCommand { cli.info(call_result)?; } + self.finalize_execute_call(cli, prompt_to_repeat_call).await + } + /// Finalize the current call, prompting the user to repeat or conclude the process. + async fn finalize_execute_call( + &mut self, + cli: &mut impl Cli, + prompt_to_repeat_call: bool, + ) -> Result<()> { // Prompt for any additional calls. if !prompt_to_repeat_call { display_message("Call completed successfully!", true, cli)?; @@ -435,12 +481,56 @@ impl CallContractCommand { } } + /// Execute the smart contract call using wallet integration. + async fn execute_with_wallet( + &self, + call_exec: CallExec, + cli: &mut impl Cli, + ) -> Result<()> { + let call_data = self.get_contract_data(&call_exec).map_err(|err| { + anyhow!("An error occurred getting the call data: {}", err.to_string()) + })?; + + let maybe_payload = request_signature(call_data, self.url.to_string()).await?; + if let Some(payload) = maybe_payload { + cli.success("Signed payload received.")?; + let spinner = spinner(); + spinner + .start("Calling the contract and waiting for finalization, please be patient..."); + + let call_result = + call_smart_contract_from_signed_payload(call_exec, payload, &self.url) + .await + .map_err(|err| anyhow!("{} {}", "ERROR:", format!("{err:?}")))?; + + cli.info(call_result)?; + } else { + display_message("No signed payload received.", false, cli)?; + } + Ok(()) + } + + // Get the call data. + fn get_contract_data( + &self, + call_exec: &CallExec, + ) -> anyhow::Result> { + let weight_limit = if self.gas_limit.is_some() && self.proof_size.is_some() { + Weight::from_parts(self.gas_limit.unwrap(), self.proof_size.unwrap()) + } else { + Weight::zero() + }; + let call_data = get_call_payload(call_exec, weight_limit)?; + Ok(call_data) + } + /// Resets message specific fields to default values for a new call. fn reset_for_new_call(&mut self) { self.message = None; self.value = DEFAULT_PAYABLE_VALUE.to_string(); self.gas_limit = None; self.proof_size = None; + self.use_wallet = false; } } @@ -456,7 +546,7 @@ fn display_message(message: &str, success: bool, cli: &mut impl Cli) -> Result<( #[cfg(test)] mod tests { use super::*; - use crate::cli::MockCli; + use crate::{cli::MockCli, common::wallet::USE_WALLET_PROMPT}; use pop_contracts::{mock_build_process, new_environment}; use std::{env, fs::write}; use url::Url; @@ -482,6 +572,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -517,13 +608,14 @@ mod tests { proof_size: Some(10), url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: true, execute: false, dev_mode: false, }; call_config.configure(&mut cli, false).await?; assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof_size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry_run", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof-size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry-run", temp_dir.path().join("testing").display().to_string(), )); // Contract deployed on Pop Network testnet, test dry-run @@ -553,13 +645,14 @@ mod tests { proof_size: Some(10), url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: true, execute: false, dev_mode: false, }; call_config.configure(&mut cli, false).await?; assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof_size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry_run", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof-size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry-run", current_dir.join("pop-contracts/tests/files/testing.contract").display().to_string(), )); // Contract deployed on Pop Network testnet, test dry-run @@ -569,7 +662,7 @@ mod tests { call_config.path = Some(current_dir.join("pop-contracts/tests/files/testing.json")); call_config.configure(&mut cli, false).await?; assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof_size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry_run", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof-size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry-run", current_dir.join("pop-contracts/tests/files/testing.json").display().to_string(), )); @@ -577,7 +670,7 @@ mod tests { call_config.path = Some(current_dir.join("pop-contracts/tests/files/testing.wasm")); call_config.configure(&mut cli, false).await?; assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof_size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry_run", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message flip --gas 100 --proof-size 10 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --dry-run", current_dir.join("pop-contracts/tests/files/testing.wasm").display().to_string(), )); // Contract deployed on Pop Network testnet, test dry-run @@ -608,10 +701,6 @@ mod tests { "Do you want to perform another call using the existing smart contract?", true, ) - .expect_confirm( - "Do you want to perform another call using the existing smart contract?", - false, - ) .expect_select( "Select the message to call:", Some(false), @@ -619,12 +708,16 @@ mod tests { Some(items), 1, // "get" message ) - .expect_input("Signer calling the contract:", "//Alice".into()) .expect_info(format!( "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message get --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice", temp_dir.path().join("testing").display().to_string(), )) + .expect_warning("NOTE: Signing is not required for this read-only call. The '--use-wallet' flag will be ignored.") .expect_warning("Your call has not been executed.") + .expect_confirm( + "Do you want to perform another call using the existing smart contract?", + false, + ) .expect_outro("Contract calling complete."); // Contract deployed on Pop Network testnet, test get @@ -638,6 +731,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: true, dry_run: false, execute: false, dev_mode: false, @@ -688,7 +782,6 @@ mod tests { "Provide the on-chain contract address:", "15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".into(), ) - .expect_input("Signer calling the contract:", "//Alice".into()) .expect_info(format!( "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message get --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice", temp_dir.path().join("testing").display().to_string(), @@ -704,6 +797,7 @@ mod tests { proof_size: None, url: Url::parse(DEFAULT_URL)?, suri: DEFAULT_URI.to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -750,14 +844,6 @@ mod tests { ]; // The inputs are processed in reverse order. let mut cli = MockCli::new() - .expect_confirm("Do you want to execute the call? (Selecting 'No' will perform a dry run)", true) - .expect_select( - "Select the message to call:", - Some(false), - true, - Some(items), - 2, // "specific_flip" message - ) .expect_input( "Where is your project or contract artifact located?", temp_dir.path().join("testing").display().to_string(), @@ -770,14 +856,21 @@ mod tests { "Provide the on-chain contract address:", "15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".into(), ) + .expect_select( + "Select the message to call:", + Some(false), + true, + Some(items), + 2, // "specific_flip" message + ) .expect_input("Enter the value for the parameter: new_value", "true".into()) // Args for specific_flip .expect_input("Enter the value for the parameter: number", "2".into()) // Args for specific_flip .expect_input("Value to transfer to the call:", "50".into()) // Only if payable .expect_input("Enter the gas limit:", "".into()) // Only if call .expect_input("Enter the proof size limit:", "".into()) // Only if call - .expect_input("Signer calling the contract:", "//Alice".into()) + .expect_confirm(USE_WALLET_PROMPT, true) .expect_info(format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --use-wallet --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -791,6 +884,7 @@ mod tests { proof_size: None, url: Url::parse(DEFAULT_URL)?, suri: DEFAULT_URI.to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -809,10 +903,11 @@ mod tests { assert_eq!(call_config.proof_size, None); assert_eq!(call_config.url.to_string(), "wss://rpc1.paseo.popnetwork.xyz/"); assert_eq!(call_config.suri, "//Alice"); + assert!(call_config.use_wallet); assert!(call_config.execute); assert!(!call_config.dry_run); assert_eq!(call_config.display(), format!( - "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", + "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --use-wallet --execute", temp_dir.path().join("testing").display().to_string(), )); @@ -877,6 +972,7 @@ mod tests { proof_size: None, url: Url::parse(DEFAULT_URL)?, suri: DEFAULT_URI.to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: true, @@ -935,6 +1031,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -984,6 +1081,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -1002,6 +1100,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -1025,6 +1124,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, @@ -1055,6 +1155,7 @@ mod tests { proof_size: None, url: Url::parse("wss://rpc1.paseo.popnetwork.xyz")?, suri: "//Alice".to_string(), + use_wallet: false, dry_run: false, execute: false, dev_mode: false, diff --git a/crates/pop-cli/src/commands/test/contract.rs b/crates/pop-cli/src/commands/test/contract.rs index 190d9ac64..05e842eca 100644 --- a/crates/pop-cli/src/commands/test/contract.rs +++ b/crates/pop-cli/src/commands/test/contract.rs @@ -48,7 +48,13 @@ impl TestContractCommand { sleep(Duration::from_secs(3)).await; } - self.node = match check_contracts_node_and_prompt(self.skip_confirm).await { + self.node = match check_contracts_node_and_prompt( + &mut Cli, + &crate::cache()?, + self.skip_confirm, + ) + .await + { Ok(binary_path) => Some(binary_path), Err(_) => { warning("🚫 substrate-contracts-node is necessary to run e2e tests. Will try to run tests anyway...")?; diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 40e158032..f46c0776f 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -2,23 +2,25 @@ use crate::{ cli::{traits::Cli as _, Cli}, - common::contracts::{check_contracts_node_and_prompt, has_contract_been_built}, + common::{ + contracts::{check_contracts_node_and_prompt, has_contract_been_built, terminate_node}, + wallet::request_signature, + }, style::style, }; use clap::Args; -use cliclack::{confirm, log, log::error, spinner}; +use cliclack::{confirm, log, log::error, spinner, ProgressBar}; use console::{Emoji, Style}; use pop_contracts::{ build_smart_contract, dry_run_gas_estimate_instantiate, dry_run_upload, - instantiate_smart_contract, is_chain_alive, parse_hex_bytes, run_contracts_node, - set_up_deployment, set_up_upload, upload_smart_contract, UpOpts, Verbosity, + get_code_hash_from_event, get_contract_code, get_instantiate_payload, get_upload_payload, + instantiate_contract_signed, instantiate_smart_contract, is_chain_alive, parse_hex_bytes, + run_contracts_node, set_up_deployment, set_up_upload, upload_contract_signed, + upload_smart_contract, UpOpts, Verbosity, }; use sp_core::Bytes; use sp_weights::Weight; -use std::{ - path::PathBuf, - process::{Child, Command}, -}; +use std::path::PathBuf; use tempfile::NamedTempFile; use url::Url; @@ -64,6 +66,15 @@ pub struct UpContractCommand { /// - with a password "//Alice///SECRET_PASSWORD" #[clap(short, long, default_value = "//Alice")] suri: String, + /// Use a browser extension wallet to sign the extrinsic. + #[clap( + name = "use-wallet", + long, + default_value = "false", + short('w'), + conflicts_with = "suri" + )] + use_wallet: bool, /// Perform a dry-run via RPC to estimate the gas usage. This does not submit a transaction. #[clap(short = 'D', long)] dry_run: bool, @@ -128,7 +139,13 @@ impl UpContractCommand { let log = NamedTempFile::new()?; // uses the cache location - let binary_path = match check_contracts_node_and_prompt(self.skip_confirm).await { + let binary_path = match check_contracts_node_and_prompt( + &mut Cli, + &crate::cache()?, + self.skip_confirm, + ) + .await + { Ok(binary_path) => binary_path, Err(_) => { Cli.outro_cancel( @@ -164,10 +181,89 @@ impl UpContractCommand { None }; + // Run steps for signing with wallet integration. Returns early. + if self.use_wallet { + let (call_data, hash) = match self.get_contract_data().await { + Ok(data) => data, + Err(e) => { + error(format!("An error occurred getting the call data: {e}"))?; + terminate_node(&mut Cli, process)?; + Cli.outro_cancel(FAILED)?; + return Ok(()); + }, + }; + + let maybe_payload = request_signature(call_data, self.url.to_string()).await?; + if let Some(payload) = maybe_payload { + log::success("Signed payload received.")?; + let spinner = spinner(); + spinner.start( + "Uploading the contract and waiting for finalization, please be patient...", + ); + + if self.upload_only { + let upload_result = match upload_contract_signed(self.url.as_str(), payload) + .await + { + Err(e) => { + spinner + .error(format!("An error occurred uploading your contract: {e}")); + terminate_node(&mut Cli, process)?; + Cli.outro_cancel(FAILED)?; + return Ok(()); + }, + Ok(result) => result, + }; + + match get_code_hash_from_event(&upload_result, hash) { + Ok(r) => { + spinner.stop(format!("Contract uploaded: The code hash is {:?}", r)); + }, + Err(e) => { + spinner + .error(format!("An error occurred uploading your contract: {e}")); + }, + }; + } else { + let contract_info = + match instantiate_contract_signed(self.url.as_str(), payload).await { + Err(e) => { + spinner.error(format!( + "An error occurred uploading your contract: {e}" + )); + terminate_node(&mut Cli, process)?; + Cli.outro_cancel(FAILED)?; + return Ok(()); + }, + Ok(result) => result, + }; + + let hash = contract_info.code_hash.map(|code_hash| format!("{:?}", code_hash)); + display_contract_info( + &spinner, + contract_info.contract_address.to_string(), + hash, + ); + }; + + if self.upload_only { + log::warning("NOTE: The contract has not been instantiated.")?; + } + } else { + Cli.outro_cancel("Signed payload doesn't exist.")?; + terminate_node(&mut Cli, process)?; + return Ok(()); + } + + terminate_node(&mut Cli, process)?; + Cli.outro(COMPLETE)?; + return Ok(()); + } + // Check for upload only. if self.upload_only { let result = self.upload_contract().await; - Self::terminate_node(process)?; + terminate_node(&mut Cli, process)?; match result { Ok(_) => { Cli.outro(COMPLETE)?; @@ -180,23 +276,11 @@ impl UpContractCommand { } // Otherwise instantiate. - let instantiate_exec = match set_up_deployment(UpOpts { - path: self.path.clone(), - constructor: self.constructor.clone(), - args: self.args.clone(), - value: self.value.clone(), - gas_limit: self.gas_limit, - proof_size: self.proof_size, - salt: self.salt.clone(), - url: self.url.clone(), - suri: self.suri.clone(), - }) - .await - { + let instantiate_exec = match set_up_deployment(self.clone().into()).await { Ok(i) => i, Err(e) => { error(format!("An error occurred instantiating the contract: {e}"))?; - Self::terminate_node(process)?; + terminate_node(&mut Cli, process)?; Cli.outro_cancel(FAILED)?; return Ok(()); }, @@ -214,7 +298,7 @@ impl UpContractCommand { }, Err(e) => { spinner.error(format!("{e}")); - Self::terminate_node(process)?; + terminate_node(&mut Cli, process)?; Cli.outro_cancel(FAILED)?; return Ok(()); }, @@ -226,30 +310,13 @@ impl UpContractCommand { let spinner = spinner(); spinner.start("Uploading and instantiating the contract..."); let contract_info = instantiate_smart_contract(instantiate_exec, weight_limit).await?; - spinner.stop(format!( - "Contract deployed and instantiated:\n{}", - style(format!( - "{}\n{}", - style(format!( - "{} The contract address is {:?}", - console::Emoji("●", ">"), - contract_info.address - )) - .dim(), - contract_info - .code_hash - .map(|hash| style(format!( - "{} The contract code hash is {:?}", - console::Emoji("●", ">"), - hash - )) - .dim() - .to_string()) - .unwrap_or_default(), - )) - .dim() - )); - Self::terminate_node(process)?; + display_contract_info( + &spinner, + contract_info.address.to_string(), + contract_info.code_hash, + ); + + terminate_node(&mut Cli, process)?; Cli.outro(COMPLETE)?; } @@ -291,27 +358,25 @@ impl UpContractCommand { Ok(()) } - /// Handles the optional termination of a local running node. - fn terminate_node(process: Option<(Child, NamedTempFile)>) -> anyhow::Result<()> { - // Prompt to close any launched node - let Some((process, log)) = process else { - return Ok(()); - }; - if confirm("Would you like to terminate the local node?") - .initial_value(true) - .interact()? - { - // Stop the process contracts-node - Command::new("kill") - .args(["-s", "TERM", &process.id().to_string()]) - .spawn()? - .wait()?; + // get the call data and contract code hash + async fn get_contract_data(&self) -> anyhow::Result<(Vec, [u8; 32])> { + let contract_code = get_contract_code(self.path.as_ref())?; + let hash = contract_code.code_hash(); + if self.upload_only { + let call_data = get_upload_payload(contract_code, self.url.as_str()).await?; + Ok((call_data, hash)) } else { - log.keep()?; - log::warning(format!("NOTE: The node is running in the background with process ID {}. Please terminate it manually when done.", process.id()))?; - } + let instantiate_exec = set_up_deployment(self.clone().into()).await?; - Ok(()) + let weight_limit = if self.gas_limit.is_some() && self.proof_size.is_some() { + Weight::from_parts(self.gas_limit.unwrap(), self.proof_size.unwrap()) + } else { + // Frontend will do dry run and update call data. + Weight::zero() + }; + let call_data = get_instantiate_payload(instantiate_exec, weight_limit)?; + Ok((call_data, hash)) + } } } @@ -331,14 +396,44 @@ impl From for UpOpts { } } +fn display_contract_info(spinner: &ProgressBar, address: String, code_hash: Option) { + spinner.stop(format!( + "Contract deployed and instantiated:\n{}", + style(format!( + "{}\n{}", + style(format!("{} The contract address is {:?}", console::Emoji("●", ">"), address)) + .dim(), + code_hash + .map(|hash| style(format!( + "{} The contract code hash is {:?}", + console::Emoji("●", ">"), + hash + )) + .dim() + .to_string()) + .unwrap_or_default(), + )) + .dim() + )); +} + #[cfg(test)] mod tests { use super::*; + use pop_common::{find_free_port, set_executable_permission}; + use pop_contracts::{contracts_node_generator, mock_build_process, new_environment}; + use std::{ + env, + process::{Child, Command}, + time::Duration, + }; + use subxt::{tx::Payload, SubstrateConfig}; + use tempfile::TempDir; + use tokio::time::sleep; use url::Url; - #[test] - fn conversion_up_contract_command_to_up_opts_works() -> anyhow::Result<()> { - let command = UpContractCommand { + fn default_up_contract_command() -> UpContractCommand { + UpContractCommand { path: None, constructor: "new".to_string(), args: vec![], @@ -346,12 +441,40 @@ mod tests { gas_limit: None, proof_size: None, salt: None, - url: Url::parse("ws://localhost:9944")?, + url: Url::parse("ws://localhost:9944").expect("default url is valid"), suri: "//Alice".to_string(), dry_run: false, upload_only: false, skip_confirm: false, - }; + use_wallet: false, + } + } + + async fn start_test_environment() -> anyhow::Result<(Child, u16, TempDir)> { + let random_port = find_free_port(None); + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("../pop-contracts/tests/files/testing.contract"), + current_dir.join("../pop-contracts/tests/files/testing.json"), + )?; + let cache = temp_dir.path().join(""); + let binary = contracts_node_generator(cache.clone(), None).await?; + binary.source(false, &(), true).await?; + set_executable_permission(binary.path())?; + let process = run_contracts_node(binary.path(), None, random_port).await?; + Ok((process, random_port, temp_dir)) + } + + fn stop_test_environment(id: &str) -> anyhow::Result<()> { + Command::new("kill").args(["-s", "TERM", id]).spawn()?.wait()?; + Ok(()) + } + + #[test] + fn conversion_up_contract_command_to_up_opts_works() -> anyhow::Result<()> { + let command = default_up_contract_command(); let opts: UpOpts = command.into(); assert_eq!( opts, @@ -369,4 +492,108 @@ mod tests { ); Ok(()) } + + #[tokio::test] + async fn get_upload_and_instantiate_call_data_works() -> anyhow::Result<()> { + let (contracts_node_process, port, temp_dir) = start_test_environment().await?; + sleep(Duration::from_secs(5)).await; + + get_upload_call_data_works(port, temp_dir.path().join("testing")).await?; + get_instantiate_call_data_works(port, temp_dir.path().join("testing")).await?; + + // Stop running contracts-node + stop_test_environment(&contracts_node_process.id().to_string())?; + Ok(()) + } + + async fn get_upload_call_data_works(port: u16, temp_dir: PathBuf) -> anyhow::Result<()> { + let localhost_url = format!("ws://127.0.0.1:{}", port); + + let up_contract_opts = UpContractCommand { + path: Some(temp_dir), + constructor: "new".to_string(), + args: vec![], + value: "0".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + url: Url::parse(&localhost_url).expect("given url is valid"), + suri: "//Alice".to_string(), + dry_run: false, + upload_only: true, + skip_confirm: true, + use_wallet: true, + }; + + let rpc_client = subxt::backend::rpc::RpcClient::from_url(&up_contract_opts.url).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + // Retrieve call data based on the above command options. + let (retrieved_call_data, _) = match up_contract_opts.get_contract_data().await { + Ok(data) => data, + Err(e) => { + error(format!("An error occurred getting the call data: {e}"))?; + return Err(e); + }, + }; + // We have retrieved some payload. + assert!(!retrieved_call_data.is_empty()); + + // Craft encoded call data for an upload code call. + let contract_code = get_contract_code(up_contract_opts.path.as_ref())?; + let storage_deposit_limit: Option = None; + let upload_code = contract_extrinsics::extrinsic_calls::UploadCode::new( + contract_code, + storage_deposit_limit, + contract_extrinsics::upload::Determinism::Enforced, + ); + let expected_call_data = upload_code.build(); + let mut encoded_expected_call_data = Vec::::new(); + expected_call_data + .encode_call_data_to(&client.metadata(), &mut encoded_expected_call_data)?; + + // Retrieved call data and calculated match. + assert_eq!(retrieved_call_data, encoded_expected_call_data); + Ok(()) + } + + async fn get_instantiate_call_data_works(port: u16, temp_dir: PathBuf) -> anyhow::Result<()> { + let localhost_url = format!("ws://127.0.0.1:{}", port); + + let up_contract_opts = UpContractCommand { + path: Some(temp_dir), + constructor: "new".to_string(), + args: vec!["false".to_string()], + value: "0".to_string(), + gas_limit: Some(200_000_000), + proof_size: Some(30_000), + salt: None, + url: Url::parse(&localhost_url).expect("given url is valid"), + suri: "//Alice".to_string(), + dry_run: false, + upload_only: false, + skip_confirm: true, + use_wallet: true, + }; + + // Retrieve call data based on the above command options. + let (retrieved_call_data, _) = match up_contract_opts.get_contract_data().await { + Ok(data) => data, + Err(e) => { + error(format!("An error occurred getting the call data: {e}"))?; + return Err(e); + }, + }; + // We have retrieved some payload. + assert!(!retrieved_call_data.is_empty()); + + // Craft instantiate call data. + let weight = Weight::from_parts(200_000_000, 30_000); + let expected_call_data = + get_instantiate_payload(set_up_deployment(up_contract_opts.into()).await?, weight)?; + // Retrieved call data matches the one crafted above. + assert_eq!(retrieved_call_data, expected_call_data); + + Ok(()) + } } diff --git a/crates/pop-cli/src/common/contracts.rs b/crates/pop-cli/src/common/contracts.rs index 3d0be11f6..a3b8f878c 100644 --- a/crates/pop-cli/src/common/contracts.rs +++ b/crates/pop-cli/src/common/contracts.rs @@ -1,22 +1,33 @@ // SPDX-License-Identifier: GPL-3.0 -use cliclack::{confirm, log::warning, spinner}; +use crate::cli::traits::*; +use cliclack::spinner; use pop_common::{manifest::from_path, sourcing::set_executable_permission}; use pop_contracts::contracts_node_generator; -use std::path::{Path, PathBuf}; +use std::{ + path::{Path, PathBuf}, + process::{Child, Command}, +}; +use tempfile::NamedTempFile; /// Checks the status of the `substrate-contracts-node` binary, sources it if necessary, and /// prompts the user to update it if the existing binary is not the latest version. /// /// # Arguments +/// * `cli`: Command line interface. +/// * `cache_path`: The cache directory path. /// * `skip_confirm`: A boolean indicating whether to skip confirmation prompts. -pub async fn check_contracts_node_and_prompt(skip_confirm: bool) -> anyhow::Result { - let cache_path: PathBuf = crate::cache()?; - let mut binary = contracts_node_generator(cache_path, None).await?; +pub async fn check_contracts_node_and_prompt( + cli: &mut impl Cli, + cache_path: &Path, + skip_confirm: bool, +) -> anyhow::Result { + let mut binary = contracts_node_generator(PathBuf::from(cache_path), None).await?; let mut node_path = binary.path(); if !binary.exists() { - warning("⚠️ The substrate-contracts-node binary is not found.")?; - if confirm("📦 Would you like to source it automatically now?") + cli.warning("⚠️ The substrate-contracts-node binary is not found.")?; + if cli + .confirm("📦 Would you like to source it automatically now?") .initial_value(true) .interact()? { @@ -33,14 +44,14 @@ pub async fn check_contracts_node_and_prompt(skip_confirm: bool) -> anyhow::Resu } } if binary.stale() { - warning(format!( + cli.warning(format!( "ℹ️ There is a newer version of {} available:\n {} -> {}", binary.name(), binary.version().unwrap_or("None"), binary.latest().unwrap_or("None") ))?; let latest = if !skip_confirm { - confirm( + cli.confirm( "📦 Would you like to source it automatically now? It may take some time..." .to_string(), ) @@ -68,6 +79,36 @@ pub async fn check_contracts_node_and_prompt(skip_confirm: bool) -> anyhow::Resu Ok(node_path) } +/// Handles the optional termination of a local running node. +/// # Arguments +/// * `cli`: Command line interface. +/// * `process`: Tuple identifying the child process to terminate and its log file. +pub fn terminate_node( + cli: &mut impl Cli, + process: Option<(Child, NamedTempFile)>, +) -> anyhow::Result<()> { + // Prompt to close any launched node + let Some((process, log)) = process else { + return Ok(()); + }; + if cli + .confirm("Would you like to terminate the local node?") + .initial_value(true) + .interact()? + { + // Stop the process contracts-node + Command::new("kill") + .args(["-s", "TERM", &process.id().to_string()]) + .spawn()? + .wait()?; + } else { + log.keep()?; + cli.warning(format!("NOTE: The node is running in the background with process ID {}. Please terminate it manually when done.", process.id()))?; + } + + Ok(()) +} + /// Checks if a contract has been built by verifying the existence of the build directory and the /// .contract file. /// @@ -88,8 +129,12 @@ pub fn has_contract_been_built(path: Option<&Path>) -> bool { #[cfg(test)] mod tests { use super::*; + use crate::cli::MockCli; use duct::cmd; + use pop_common::find_free_port; + use pop_contracts::{is_chain_alive, run_contracts_node}; use std::fs::{self, File}; + use url::Url; #[test] fn has_contract_been_built_works() -> anyhow::Result<()> { @@ -111,4 +156,38 @@ mod tests { assert!(has_contract_been_built(Some(&path.join(name)))); Ok(()) } + + #[tokio::test] + async fn check_contracts_node_and_prompt_works() -> anyhow::Result<()> { + let cache_path = tempfile::tempdir().expect("Could create temp dir"); + let mut cli = MockCli::new() + .expect_warning("⚠️ The substrate-contracts-node binary is not found.") + .expect_confirm("📦 Would you like to source it automatically now?", true) + .expect_warning("⚠️ The substrate-contracts-node binary is not found."); + + let node_path = check_contracts_node_and_prompt(&mut cli, cache_path.path(), false).await?; + // Binary path is at least equal to the cache path + "substrate-contracts-node". + assert!(node_path + .to_str() + .unwrap() + .starts_with(&cache_path.path().join("substrate-contracts-node").to_str().unwrap())); + cli.verify() + } + + #[tokio::test] + async fn node_is_terminated() -> anyhow::Result<()> { + let cache = tempfile::tempdir().expect("Could not create temp dir"); + let binary = contracts_node_generator(PathBuf::from(cache.path()), None).await?; + binary.source(false, &(), true).await?; + set_executable_permission(binary.path())?; + let port = find_free_port(None); + let process = run_contracts_node(binary.path(), None, port).await?; + let log = NamedTempFile::new()?; + // Terminate the process. + let mut cli = + MockCli::new().expect_confirm("Would you like to terminate the local node?", true); + assert!(terminate_node(&mut cli, Some((process, log))).is_ok()); + assert_eq!(is_chain_alive(Url::parse(&format!("ws://localhost:{}", port))?).await?, false); + cli.verify() + } } diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index 1cb3ee579..4a89036e5 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -3,3 +3,4 @@ #[cfg(feature = "contract")] pub mod contracts; pub mod helpers; +pub mod wallet; diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs new file mode 100644 index 000000000..1aedd0294 --- /dev/null +++ b/crates/pop-cli/src/common/wallet.rs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::{ + cli::traits::Cli, + wallet_integration::{FrontendFromString, TransactionData, WalletIntegrationManager}, +}; +use cliclack::{log, spinner}; + +/// The prompt to ask the user if they want to use the wallet for signing. +pub const USE_WALLET_PROMPT: &str = "Do you want to use your browser wallet to sign the extrinsic? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')"; + +/// Launches the wallet integration for in-browser signing. Blocks until the signature is received. +/// +/// # Arguments +/// * `call_data` - The call data to be signed. +/// * `url` - Chain rpc. +/// # Returns +/// * The signed payload, if it exists. +pub async fn request_signature(call_data: Vec, rpc: String) -> anyhow::Result> { + let ui = FrontendFromString::new(include_str!("../assets/index.html").to_string()); + + let transaction_data = TransactionData::new(rpc, call_data); + // Starts server with port 9090. + let mut wallet = WalletIntegrationManager::new(ui, transaction_data, Some(9090)); + let url = format!("http://{}", &wallet.server_url); + log::step(format!("Wallet signing portal started at {url}."))?; + + let spinner = spinner(); + spinner.start(format!("Opening browser to {url}")); + if let Err(e) = open::that(url) { + spinner.error(format!("Failed to launch browser. Please open link manually. {e}")); + } + + spinner.start("Waiting for signature... Press Ctrl+C to terminate early."); + loop { + // Display error, if any. + if let Some(error) = wallet.take_error().await { + log::error(format!("Signing portal error: {error}"))?; + } + + let state = wallet.state.lock().await; + // If the payload is submitted we terminate the frontend. + if !wallet.is_running() || state.signed_payload.is_some() { + wallet.task_handle.await??; + break; + } + } + spinner.stop(""); + + let signed_payload = wallet.state.lock().await.signed_payload.take(); + Ok(signed_payload) +} + +/// Prompts the user to use the wallet for signing. +/// # Arguments +/// * `cli` - The CLI instance. +/// # Returns +/// * `true` if the user wants to use the wallet, `false` otherwise. +pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { + use crate::cli::traits::Confirm; + + if cli.confirm(USE_WALLET_PROMPT).initial_value(true).interact()? { + Ok(true) + } else { + Ok(false) + } +} diff --git a/crates/pop-cli/src/main.rs b/crates/pop-cli/src/main.rs index 9a41fd7c1..f8e3504e5 100644 --- a/crates/pop-cli/src/main.rs +++ b/crates/pop-cli/src/main.rs @@ -19,6 +19,7 @@ mod cli; mod commands; mod common; mod style; +mod wallet_integration; #[tokio::main] async fn main() -> Result<()> { diff --git a/crates/pop-cli/src/wallet_integration.rs b/crates/pop-cli/src/wallet_integration.rs new file mode 100644 index 000000000..92e3395ec --- /dev/null +++ b/crates/pop-cli/src/wallet_integration.rs @@ -0,0 +1,579 @@ +use axum::{ + http::HeaderValue, + response::Html, + routing::{get, post}, + Router, +}; +use pop_common::find_free_port; +use serde::Serialize; +use std::{path::PathBuf, sync::Arc}; +use tokio::{ + sync::{oneshot, Mutex}, + task::JoinHandle, +}; +use tower_http::{cors::Any, services::ServeDir}; + +/// Make frontend sourcing more flexible by allowing a custom route to be defined. +pub trait Frontend { + /// Serves the content via a [Router]. + fn serve_content(&self) -> Router; +} + +/// Transaction payload to be sent to frontend for signing. +#[derive(Serialize, Debug)] +#[cfg_attr(test, derive(serde::Deserialize, Clone))] +pub struct TransactionData { + chain_rpc: String, + call_data: Vec, +} + +impl TransactionData { + /// Create a new transaction payload. + /// # Arguments + /// * `chain_rpc`: The RPC of the chain. + /// * `call_data`: the call data. + /// # Returns + /// The transaction payload to be sent to frontend for signing. + pub fn new(chain_rpc: String, call_data: Vec) -> Self { + Self { chain_rpc, call_data } + } +} + +/// Shared state between routes. Serves two purposes: +/// - Maintains a channel to signal shutdown to the main app. +/// - Stores the signed payload received from the wallet. +#[derive(Default)] +pub struct StateHandler { + /// Channel to signal shutdown to the main app. + shutdown_tx: Option>, + /// Received from UI. + pub signed_payload: Option, + /// Holds a single error message. + /// Only method for consuming error removes (takes) it from state. + error: Option, +} + +/// Manages the wallet integration for secure signing of transactions. +pub struct WalletIntegrationManager { + pub server_url: String, + /// Shared state between routes. + pub state: Arc>, + /// Web server task handle. + pub task_handle: JoinHandle>, +} + +impl WalletIntegrationManager { + /// Launches a server for hosting the wallet integration. Server launched in separate task. + /// # Arguments + /// * `frontend`: A frontend with custom route to serve content. + /// * `payload`: Payload to be sent to the frontend for signing. + /// * `maybe_port`: Optional port for server to bind to. `None` will result in a random port. + /// + /// # Returns + /// A `WalletIntegrationManager` instance, with access to the state and task handle for the + /// server. + pub fn new( + frontend: F, + payload: TransactionData, + maybe_port: Option, + ) -> Self { + let port = find_free_port(maybe_port); + Self::new_with_address(frontend, payload, format!("127.0.0.1:{}", port)) + } + + /// Same as `new`, but allows specifying the address to bind to. + /// # Arguments + /// * `frontend`: A frontend with custom route to serve content. + /// * `payload`: Payload to be sent to the frontend for signing. + /// * `server_url`: The address to bind to. + /// + /// # Returns + /// A `WalletIntegrationManager` instance, with access to the state and task handle for the + pub fn new_with_address( + frontend: F, + payload: TransactionData, + server_url: String, + ) -> Self { + // Channel to signal shutdown. + let (tx, rx) = oneshot::channel(); + + let state = Arc::new(Mutex::new(StateHandler { + shutdown_tx: Some(tx), + signed_payload: None, + error: None, + })); + + let payload = Arc::new(payload); + + let cors = tower_http::cors::CorsLayer::new() + .allow_origin(server_url.parse::().expect("invalid server url")) + .allow_methods(Any) // Allow any HTTP method + .allow_headers(Any); // Allow any headers (like 'Content-Type') + + let app = Router::new() + .route("/payload", get(routes::get_payload_handler).with_state(payload)) + .route("/submit", post(routes::submit_handler).with_state(state.clone())) + .route("/error", post(routes::error_handler).with_state(state.clone())) + .route("/terminate", post(routes::terminate_handler).with_state(state.clone())) + .merge(frontend.serve_content()) // Custom route for serving frontend. + .layer(cors); + + let url_owned = server_url.to_string(); + + // Will shut down when the signed payload is received. + let task_handle = tokio::spawn(async move { + let listener = tokio::net::TcpListener::bind(&url_owned) + .await + .map_err(|e| anyhow::anyhow!("Failed to bind to {}: {}", url_owned, e))?; + + axum::serve(listener, app) + .with_graceful_shutdown(async move { + let _ = rx.await.ok(); + }) + .await + .map_err(|e| anyhow::anyhow!("Server encountered an error: {}", e))?; + Ok(()) + }); + + Self { state, server_url, task_handle } + } + + /// Signals the wallet integration server to shut down. + #[allow(dead_code)] + pub async fn terminate(&mut self) -> anyhow::Result<()> { + terminate_helper(&self.state).await + } + + /// Checks if the server task is still running. + pub fn is_running(&self) -> bool { + !self.task_handle.is_finished() + } + + /// Takes the error from the state if it exists. + pub async fn take_error(&mut self) -> Option { + self.state.lock().await.error.take() + } +} + +mod routes { + use super::{terminate_helper, Arc, Mutex, StateHandler, TransactionData}; + use anyhow::Error; + use axum::{ + extract::State, + http::StatusCode, + response::{IntoResponse, Response}, + Json, + }; + use serde_json::json; + + pub(super) struct ApiError(Error); + + impl From for ApiError { + fn from(err: Error) -> Self { + ApiError(err) + } + } + + // Implementing IntoResponse for ApiError allows us to return it directly from a route handler. + impl IntoResponse for ApiError { + fn into_response(self) -> Response { + let body = json!({ + "error": self.0.to_string(), + }); + (StatusCode::INTERNAL_SERVER_ERROR, Json(body)).into_response() + } + } + + /// Responds with the serialized JSON data for signing. + pub(super) async fn get_payload_handler( + State(payload): State>, + ) -> Result, ApiError> { + // Error should never occur. + let json_payload = serde_json::to_value(&*payload) + .map_err(|e| anyhow::anyhow!("Failed to serialize payload: {}", e))?; + Ok(Json(json_payload)) + } + + /// Receives the signed payload from the wallet. + /// Will signal for shutdown on success. + pub(super) async fn submit_handler( + State(state): State>>, + Json(payload): Json, + ) -> Result, ApiError> { + // Signal shutdown. + let res = terminate_helper(&state).await; + + let mut state_locked = state.lock().await; + state_locked.signed_payload = Some(payload); + + res?; + + // Graceful shutdown ensures response is sent before shutdown. + Ok(Json(json!({"status": "success"}))) + } + + /// Receives an error message from the wallet. + pub(super) async fn error_handler( + State(state): State>>, + Json(error): Json, + ) { + let mut state = state.lock().await; + state.error = Some(error); + } + + /// Allows the server to be terminated from the frontend. + pub(super) async fn terminate_handler( + State(state): State>>, + ) -> Result<(), ApiError> { + Ok(terminate_helper(&state).await?) + } +} + +async fn terminate_helper(handle: &Arc>) -> anyhow::Result<()> { + if let Some(shutdown_tx) = handle.lock().await.shutdown_tx.take() { + shutdown_tx + .send(()) + .map_err(|_| anyhow::anyhow!("Failed to send shutdown signal"))?; + } + Ok(()) +} + +/// Serves static files from a directory. +pub struct FrontendFromDir { + content: PathBuf, +} +#[allow(dead_code)] +impl FrontendFromDir { + /// A new static server. + /// # Arguments + /// * `content`: A directory path. + pub fn new(content: PathBuf) -> Self { + Self { content } + } +} + +impl Frontend for FrontendFromDir { + fn serve_content(&self) -> Router { + Router::new().nest_service("/", ServeDir::new(self.content.clone())) + } +} + +/// Serves a hard-coded HTML string as the frontend. +pub struct FrontendFromString { + content: String, +} + +#[allow(dead_code)] +impl FrontendFromString { + /// A new static server. + /// # Arguments + /// * `content`: A hard-coded HTML string + pub fn new(content: String) -> Self { + Self { content } + } +} + +impl Frontend for FrontendFromString { + fn serve_content(&self) -> Router { + let content = self.content.clone(); + Router::new().route("/", get(move || async { Html(content) })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + const TEST_HTML: &str = "Hello, world!"; + + // Wait for server to launch. + async fn wait() { + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + } + + fn default_payload() -> TransactionData { + TransactionData { chain_rpc: "localhost:9944".to_string(), call_data: vec![1, 2, 3] } + } + + #[tokio::test] + async fn new_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), Some(9190)); + + assert_eq!(wim.server_url, "127.0.0.1:9190"); + assert_eq!(wim.is_running(), true); + assert!(wim.state.lock().await.shutdown_tx.is_some()); + assert!(wim.state.lock().await.signed_payload.is_none()); + + // Terminate the server and make sure result is ok. + wim.terminate().await.expect("Termination should not fail."); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn new_with_random_port_works() { + let servers = (0..3) + .map(|_| { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + WalletIntegrationManager::new(frontend, default_payload(), None) + }) + .collect::>(); + + // Ensure all server URLs are unique + for i in 0..servers.len() { + for j in (i + 1)..servers.len() { + assert_ne!(servers[i].server_url, servers[j].server_url); + } + } + + assert!(servers.iter().all(|server| server.is_running())); + for mut server in servers.into_iter() { + assert!(server.state.lock().await.shutdown_tx.is_some()); + assert!(server.state.lock().await.signed_payload.is_none()); + server.terminate().await.expect("Server termination should not fail"); + + let task_result = server.task_handle.await; + assert!(task_result.is_ok()); + } + } + + #[test] + fn new_transaction_data_works() { + let chain_rpc = "localhost:9944".to_string(); + let call_data = vec![1, 2, 3]; + let transaction_data = TransactionData::new(chain_rpc.clone(), call_data.clone()); + + assert_eq!(transaction_data.chain_rpc, chain_rpc); + assert_eq!(transaction_data.call_data, call_data); + } + + #[tokio::test] + async fn take_error_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), None); + + assert_eq!(wim.take_error().await, None); + + let error = "An error occurred".to_string(); + wim.state.lock().await.error = Some(error.clone()); + + let taken_error = wim.take_error().await; + assert_eq!(taken_error, Some(error)); + } + + #[tokio::test] + async fn payload_handler_works() { + // offset port per test to avoid conflicts + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let expected_payload = + TransactionData { chain_rpc: "localhost:9944".to_string(), call_data: vec![1, 2, 3] }; + let mut wim = WalletIntegrationManager::new(frontend, expected_payload.clone(), None); + wait().await; + + let addr = format!("http://{}", wim.server_url); + let actual_payload = reqwest::get(&format!("{}/payload", addr)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + + assert_eq!(actual_payload.chain_rpc, expected_payload.chain_rpc); + assert_eq!(actual_payload.call_data, expected_payload.call_data); + + wim.terminate().await.expect("Termination should not fail"); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn submit_handler_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), None); + wait().await; + + let addr = format!("http://{}", wim.server_url); + let response = reqwest::Client::new() + .post(&format!("{}/submit", addr)) + .json(&"0xDEADBEEF") + .send() + .await + .expect("Failed to submit payload") + .text() + .await + .expect("Failed to parse response"); + + assert_eq!(response, json!({"status": "success"}).to_string()); + assert_eq!(wim.state.lock().await.signed_payload, Some("0xDEADBEEF".to_string())); + assert_eq!(wim.is_running(), false); + + wim.terminate().await.expect("Termination should not fail"); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn error_handler_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), None); + wait().await; + + let addr = format!("http://{}", wim.server_url); + let response = reqwest::Client::new() + .post(&format!("{}/error", addr)) + .json(&"an error occurred") + .send() + .await + .expect("Failed to submit error") + .text() + .await + .expect("Failed to parse response"); + + // no response expected + assert_eq!(response.len(), 0); + assert_eq!(wim.state.lock().await.error, Some("an error occurred".to_string())); + assert_eq!(wim.is_running(), true); + + wim.terminate().await.expect("Termination should not fail"); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn terminate_handler_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + + let wim = WalletIntegrationManager::new(frontend, default_payload(), None); + wait().await; + + let addr = format!("http://{}", wim.server_url); + let response = reqwest::Client::new() + .post(&format!("{}/terminate", addr)) + .send() + .await + .expect("Failed to terminate") + .text() + .await + .expect("Failed to parse response"); + + // No response expected. + assert_eq!(response.len(), 0); + assert_eq!(wim.is_running(), false); + + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn wallet_terminate_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), None); + assert_eq!(wim.is_running(), true); + wim.terminate().await.expect("Termination should not fail"); + wait().await; + assert_eq!(wim.is_running(), false); + + wim.terminate().await.expect("Termination should not fail"); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn frontend_from_string_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), None); + wait().await; + + let actual_content = reqwest::get(&format!("http://{}", wim.server_url)) + .await + .expect("Failed to get web page") + .text() + .await + .expect("Failed to parse page"); + + assert_eq!(actual_content, TEST_HTML); + + wim.terminate().await.expect("Termination should not fail"); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn frontend_from_dir_works() { + use std::fs; + use tempfile::tempdir; + + let temp_dir = tempdir().expect("Failed to create temp directory"); + let index_file_path = temp_dir.path().join("index.html"); + + let test_html = "Hello, world from Directory!"; + fs::write(&index_file_path, test_html).expect("Failed to write index.html"); + + let frontend = FrontendFromDir::new(temp_dir.path().to_path_buf()); + let mut wim = WalletIntegrationManager::new(frontend, default_payload(), None); + wait().await; + + let actual_content = reqwest::get(&format!("http://{}", wim.server_url)) + .await + .expect("Failed to get web page") + .text() + .await + .expect("Failed to parse page"); + + assert_eq!(actual_content, test_html); + + wim.terminate().await.expect("Termination should not fail"); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn large_payload_works() { + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + + let call_data_5mb = vec![99u8; 5 * 1024 * 1024]; + + let expected_payload = TransactionData { + chain_rpc: "localhost:9944".to_string(), + call_data: call_data_5mb.clone(), + }; + let mut wim = WalletIntegrationManager::new(frontend, expected_payload.clone(), None); + wait().await; + + let addr = format!("http://{}", wim.server_url); + let actual_payload = reqwest::get(&format!("{}/payload", addr)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + + assert_eq!(actual_payload.chain_rpc, expected_payload.chain_rpc); + assert_eq!(actual_payload.call_data, call_data_5mb); + + wim.terminate().await.expect("Termination should not fail."); + assert!(wim.task_handle.await.is_ok()); + } + + #[tokio::test] + async fn new_with_conflicting_address_fails() { + // offset port per test to avoid conflicts + let addr = "127.0.0.1:9099".to_string(); + + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let wim = + WalletIntegrationManager::new_with_address(frontend, default_payload(), addr.clone()); + wait().await; + + assert_eq!(wim.is_running(), true); + + let frontend = FrontendFromString::new(TEST_HTML.to_string()); + let wim_conflict = + WalletIntegrationManager::new_with_address(frontend, default_payload(), addr.clone()); + wait().await; + + assert_eq!(wim_conflict.is_running(), false); + let task_result = wim_conflict.task_handle.await.unwrap(); + match task_result { + Err(e) => assert!(e + .to_string() + .starts_with(&format!("Failed to bind to {}: Address already in use", addr))), + Ok(_) => panic!("Expected error, but task succeeded"), + } + } +} diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index 77c8dba5d..1af21b385 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -2,19 +2,57 @@ use anyhow::Result; use assert_cmd::Command; -use pop_common::{set_executable_permission, templates::Template}; +use pop_common::{find_free_port, set_executable_permission, templates::Template}; use pop_contracts::{ contracts_node_generator, dry_run_gas_estimate_instantiate, instantiate_smart_contract, run_contracts_node, set_up_deployment, Contract, UpOpts, }; -use std::{path::Path, process::Command as Cmd}; +use serde::{Deserialize, Serialize}; +use std::{path::Path, process::Command as Cmd, time::Duration}; use strum::VariantArray; +use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload, utils::to_hex}; +use subxt_signer::sr25519::dev; +use tokio::time::sleep; use url::Url; +// This struct implements the [`Payload`] trait and is used to submit +// pre-encoded SCALE call data directly, without the dynamic construction of transactions. +struct CallData(Vec); +impl Payload for CallData { + fn encode_call_data_to( + &self, + _: &subxt::Metadata, + out: &mut Vec, + ) -> Result<(), subxt::ext::subxt_core::Error> { + out.extend_from_slice(&self.0); + Ok(()) + } +} + +// TransactionData has been copied from wallet_integration.rs +/// Transaction payload to be sent to frontend for signing. +#[derive(Serialize, Debug)] +#[cfg_attr(test, derive(Deserialize, Clone))] +pub struct TransactionData { + chain_rpc: String, + call_data: Vec, +} +impl TransactionData { + pub fn new(chain_rpc: String, call_data: Vec) -> Self { + Self { chain_rpc, call_data } + } + pub fn call_data(&self) -> Vec { + self.call_data.clone() + } +} + /// Test the contract lifecycle: new, build, up, call #[tokio::test] async fn contract_lifecycle() -> Result<()> { - const DEFAULT_PORT: u16 = 9944; + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + const WAIT_SECS: u64 = 240; + let endpoint_port = find_free_port(None); + let default_endpoint: &str = &format!("ws://127.0.0.1:{}", endpoint_port); let temp = tempfile::tempdir().unwrap(); let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally @@ -46,14 +84,15 @@ async fn contract_lifecycle() -> Result<()> { let binary = contracts_node_generator(temp_dir.to_path_buf().clone(), None).await?; binary.source(false, &(), true).await?; set_executable_permission(binary.path())?; - let process = run_contracts_node(binary.path(), None, DEFAULT_PORT).await?; + let process = run_contracts_node(binary.path(), None, endpoint_port).await?; + sleep(Duration::from_secs(5)).await; // Only upload the contract // pop up contract --upload-only Command::cargo_bin("pop") .unwrap() .current_dir(&temp_dir.join("test_contract")) - .args(&["up", "contract", "--upload-only"]) + .args(&["up", "contract", "--upload-only", "--url", default_endpoint]) .assert() .success(); // Instantiate contract, only dry-run @@ -70,6 +109,8 @@ async fn contract_lifecycle() -> Result<()> { "--suri", "//Alice", "--dry-run", + "--url", + default_endpoint, ]) .assert() .success(); @@ -83,7 +124,7 @@ async fn contract_lifecycle() -> Result<()> { gas_limit: None, proof_size: None, salt: None, - url: Url::parse("ws://127.0.0.1:9944")?, + url: Url::parse(default_endpoint)?, suri: "//Alice".to_string(), }) .await?; @@ -103,6 +144,8 @@ async fn contract_lifecycle() -> Result<()> { "get", "--suri", "//Alice", + "--url", + default_endpoint, ]) .assert() .success(); @@ -122,10 +165,70 @@ async fn contract_lifecycle() -> Result<()> { "--suri", "//Alice", "-x", + "--url", + default_endpoint, ]) .assert() .success(); + // pop up contract --upload-only --use-wallet + // Will run http server for wallet integration. + // Using `cargo run --` as means for the CI to pass. + // Possibly there's room for improvement here. + let _ = tokio::process::Command::new("cargo") + .args(&[ + "run", + "--", + "up", + "contract", + "--upload-only", + "--use-wallet", + "--skip-confirm", + "--dry-run", + "-p", + temp_dir.join("test_contract").to_str().expect("to_str"), + "--url", + default_endpoint, + ]) + .spawn()?; + // Wait a moment for node and server to be up. + sleep(Duration::from_secs(WAIT_SECS)).await; + + // Request payload from server. + let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + // We have received some payload. + assert!(!response.call_data().is_empty()); + + let rpc_client = subxt::backend::rpc::RpcClient::from_url(default_endpoint).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + // Sign payload. + let signer = dev::alice(); + let payload = CallData(response.call_data()); + let ext_params = Params::new().build(); + let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; + + // Submit signed payload. This kills the wallet integration server. + let _ = reqwest::Client::new() + .post(&format!("{}/submit", WALLET_INT_URI)) + .json(&to_hex(signed.encoded())) + .send() + .await + .expect("Failed to submit payload") + .text() + .await + .expect("Failed to parse JSON response"); + + // Request payload from server after signed payload has been sent. + // Server should not be running! + let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)).await; + assert!(response.is_err()); + // Stop the process contracts-node Cmd::new("kill") .args(["-s", "TERM", &process.id().to_string()]) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 67132e6d4..31ad6bded 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -95,7 +95,7 @@ async fn parachain_lifecycle() -> Result<()> { // Overwrite the config file to manually set the port to test pop call parachain. let network_toml_path = temp_parachain_dir.join("network.toml"); fs::create_dir_all(&temp_parachain_dir)?; - let random_port = find_free_port(); + let random_port = find_free_port(None); let localhost_url = format!("ws://127.0.0.1:{}", random_port); fs::write( &network_toml_path, diff --git a/crates/pop-common/src/lib.rs b/crates/pop-common/src/lib.rs index 70dbcc8f8..b4c0c1973 100644 --- a/crates/pop-common/src/lib.rs +++ b/crates/pop-common/src/lib.rs @@ -64,12 +64,20 @@ pub fn target() -> Result<&'static str, Error> { Err(Error::UnsupportedPlatform { arch: ARCH, os: OS }) } -/// Finds an available port by binding to port 0 and retrieving the assigned port. -pub fn find_free_port() -> u16 { +/// Checks if preferred port is available, otherwise returns a random available port. +pub fn find_free_port(preferred_port: Option) -> u16 { + // Try to bind to preferred port if provided. + if let Some(port) = preferred_port { + if TcpListener::bind(format!("127.0.0.1:{}", port)).is_ok() { + return port; + } + } + + // Else, fallback to a random available port TcpListener::bind("127.0.0.1:0") .expect("Failed to bind to an available port") .local_addr() - .expect("Failed to retrieve local address") + .expect("Failed to retrieve local address. This should never occur.") .port() } @@ -105,7 +113,7 @@ mod test { #[test] fn find_free_port_works() -> Result<()> { - let port = find_free_port(); + let port = find_free_port(None); let addr = format!("127.0.0.1:{}", port); // Constructs the TcpListener from the above port let listener = TcpListener::bind(&addr); diff --git a/crates/pop-contracts/Cargo.toml b/crates/pop-contracts/Cargo.toml index b3ab82cb8..f44332299 100644 --- a/crates/pop-contracts/Cargo.toml +++ b/crates/pop-contracts/Cargo.toml @@ -27,11 +27,13 @@ sp-core.workspace = true sp-weights.workspace = true strum.workspace = true strum_macros.workspace = true +subxt-signer.workspace = true +subxt.workspace = true # cargo-contracts contract-build.workspace = true contract-extrinsics.workspace = true -contract-transcode.workspace = true +contract-transcode.workspace = true scale-info.workspace = true # pop pop-common = { path = "../pop-common", version = "0.5.0" } @@ -39,4 +41,4 @@ pop-common = { path = "../pop-common", version = "0.5.0" } [dev-dependencies] dirs.workspace = true mockito.workspace = true -tokio-test.workspace = true +tokio-test.workspace = true \ No newline at end of file diff --git a/crates/pop-contracts/src/call.rs b/crates/pop-contracts/src/call.rs index 03effcb0a..2ffbd3258 100644 --- a/crates/pop-contracts/src/call.rs +++ b/crates/pop-contracts/src/call.rs @@ -2,6 +2,7 @@ use crate::{ errors::Error, + submit_signed_payload, utils::{ get_manifest_path, metadata::{process_function_args, FunctionType}, @@ -11,16 +12,18 @@ use crate::{ use anyhow::Context; use contract_build::Verbosity; use contract_extrinsics::{ - BalanceVariant, CallCommandBuilder, CallExec, ContractArtifacts, DisplayEvents, ErrorVariant, - ExtrinsicOptsBuilder, TokenMetadata, + extrinsic_calls::Call, BalanceVariant, CallCommandBuilder, CallExec, ContractArtifacts, + DisplayEvents, ErrorVariant, ExtrinsicOptsBuilder, TokenMetadata, }; use ink_env::{DefaultEnvironment, Environment}; use pop_common::{create_signer, Config, DefaultConfig, Keypair}; use sp_weights::Weight; use std::path::PathBuf; +use subxt::{tx::Payload, SubstrateConfig}; use url::Url; /// Attributes for the `call` command. +#[derive(Clone, Debug, PartialEq)] pub struct CallOpts { /// Path to the contract build directory. pub path: Option, @@ -173,6 +176,53 @@ pub async fn call_smart_contract( Ok(output) } +/// Executes a smart contract call using a signed payload. +/// +/// # Arguments +/// +/// * `call_exec` - A struct containing the details of the contract call. +/// * `payload` - The signed payload string to be submitted for executing the call. +/// * `url` - The endpoint of the node where the call is executed. +pub async fn call_smart_contract_from_signed_payload( + call_exec: CallExec, + payload: String, + url: &Url, +) -> anyhow::Result { + let token_metadata = TokenMetadata::query::(url).await?; + let metadata = call_exec.client().metadata(); + let events = submit_signed_payload(url.as_str(), payload).await?; + let display_events = DisplayEvents::from_events::( + &events, None, &metadata, + )?; + + let output = + display_events.display_events::(Verbosity::Default, &token_metadata)?; + Ok(output) +} + +/// Generates the payload for executing a smart contract call. +/// +/// # Arguments +/// * `call_exec` - A struct containing the details of the contract call. +/// * `gas_limit` - The maximum amount of gas allocated for executing the contract call. +pub fn get_call_payload( + call_exec: &CallExec, + gas_limit: Weight, +) -> anyhow::Result> { + let storage_deposit_limit: Option = call_exec.opts().storage_deposit_limit(); + let mut encoded_data = Vec::::new(); + Call::new( + call_exec.contract().into(), + call_exec.value(), + gas_limit, + storage_deposit_limit.as_ref(), + call_exec.call_data().clone(), + ) + .build() + .encode_call_data_to(&call_exec.client().metadata(), &mut encoded_data)?; + Ok(encoded_data) +} + #[cfg(test)] mod tests { use super::*; @@ -335,7 +385,7 @@ mod tests { #[tokio::test] async fn call_works() -> Result<()> { - let random_port = find_free_port(); + let random_port = find_free_port(None); let localhost_url = format!("ws://127.0.0.1:{}", random_port); let temp_dir = new_environment("testing")?; let current_dir = env::current_dir().expect("Failed to get current directory"); diff --git a/crates/pop-contracts/src/lib.rs b/crates/pop-contracts/src/lib.rs index c5e76e2f1..6dded2fe0 100644 --- a/crates/pop-contracts/src/lib.rs +++ b/crates/pop-contracts/src/lib.rs @@ -14,7 +14,8 @@ mod utils; pub use build::{build_smart_contract, is_supported, Verbosity}; pub use call::{ - call_smart_contract, dry_run_call, dry_run_gas_estimate_call, set_up_call, CallOpts, + call_smart_contract, call_smart_contract_from_signed_payload, dry_run_call, + dry_run_gas_estimate_call, get_call_payload, set_up_call, CallOpts, }; pub use new::{create_smart_contract, is_valid_contract_name}; pub use node::{contracts_node_generator, is_chain_alive, run_contracts_node}; @@ -22,10 +23,15 @@ pub use templates::{Contract, ContractType}; pub use test::{test_e2e_smart_contract, test_smart_contract}; pub use testing::{mock_build_process, new_environment}; pub use up::{ - dry_run_gas_estimate_instantiate, dry_run_upload, instantiate_smart_contract, - set_up_deployment, set_up_upload, upload_smart_contract, UpOpts, + dry_run_gas_estimate_instantiate, dry_run_upload, get_code_hash_from_event, get_contract_code, + get_instantiate_payload, get_upload_payload, instantiate_contract_signed, + instantiate_smart_contract, set_up_deployment, set_up_upload, submit_signed_payload, + upload_contract_signed, upload_smart_contract, ContractInfo, UpOpts, }; pub use utils::{ - metadata::{get_messages, ContractFunction}, + metadata::{get_message, get_messages, ContractFunction}, parse_account, parse_hex_bytes, }; +// External exports +pub use contract_extrinsics::CallExec; +pub use ink_env::DefaultEnvironment; diff --git a/crates/pop-contracts/src/node/mod.rs b/crates/pop-contracts/src/node.rs similarity index 91% rename from crates/pop-contracts/src/node/mod.rs rename to crates/pop-contracts/src/node.rs index be5680a04..158865d40 100644 --- a/crates/pop-contracts/src/node/mod.rs +++ b/crates/pop-contracts/src/node.rs @@ -19,9 +19,11 @@ use std::{ process::{Child, Command, Stdio}, time::Duration, }; +use subxt::{dynamic::Value, SubstrateConfig}; use tokio::time::sleep; const BIN_NAME: &str = "substrate-contracts-node"; +const STARTUP: Duration = Duration::from_millis(20_000); /// Checks if the specified node is alive and responsive. /// @@ -131,8 +133,24 @@ pub async fn run_contracts_node( let process = command.spawn()?; - // Wait 5 secs until the node is ready - sleep(Duration::from_millis(5000)).await; + // Wait until the node is ready + sleep(STARTUP).await; + + let data = Value::from_bytes(subxt::utils::to_hex("initialize contracts node")); + let payload = subxt::dynamic::tx("System", "remark", [data].to_vec()); + + let client = subxt::client::OnlineClient::::from_url(format!( + "ws://127.0.0.1:{}", + port + )) + .await + .map_err(|e| Error::AnyhowError(e.into()))?; + client + .tx() + .sign_and_submit_default(&payload, &subxt_signer::sr25519::dev::alice()) + .await + .map_err(|e| Error::AnyhowError(e.into()))?; + Ok(process) } @@ -222,7 +240,7 @@ mod tests { #[ignore = "Works fine locally but is causing issues when running tests in parallel in the CI environment."] #[tokio::test] async fn run_contracts_node_works() -> Result<(), Error> { - let random_port = find_free_port(); + let random_port = find_free_port(None); let localhost_url = format!("ws://127.0.0.1:{}", random_port); let local_url = url::Url::parse(&localhost_url)?; diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index bed7dfa45..3ed614c7e 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 + use crate::{ errors::Error, utils::{ @@ -8,17 +9,29 @@ use crate::{ }, }; use contract_extrinsics::{ - BalanceVariant, ErrorVariant, ExtrinsicOptsBuilder, InstantiateCommandBuilder, InstantiateExec, - TokenMetadata, UploadCommandBuilder, UploadExec, + events::{CodeStored, ContractInstantiated}, + extrinsic_calls::{Instantiate, InstantiateWithCode, UploadCode}, + upload::Determinism, + BalanceVariant, Code, ErrorVariant, ExtrinsicOptsBuilder, InstantiateCommandBuilder, + InstantiateExec, InstantiateExecResult, TokenMetadata, UploadCommandBuilder, UploadExec, + UploadResult, WasmCode, }; use ink_env::{DefaultEnvironment, Environment}; use pop_common::{create_signer, DefaultConfig, Keypair}; -use sp_core::Bytes; +use sp_core::{bytes::from_hex, Bytes}; use sp_weights::Weight; -use std::{fmt::Write, path::PathBuf}; +use std::{ + fmt::Write, + path::{Path, PathBuf}, +}; +use subxt::{ + blocks::ExtrinsicEvents, + tx::{Payload, SubmittableExtrinsic}, + Config, SubstrateConfig, +}; /// Attributes for the `up` command -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct UpOpts { /// Path to the contract build directory. pub path: Option, @@ -100,14 +113,177 @@ pub async fn set_up_upload( let upload_exec: UploadExec = UploadCommandBuilder::new(extrinsic_opts).done().await?; + Ok(upload_exec) } +/// Gets the encoded payload call data for contract upload (not instantiate). +/// +/// # Arguments +/// * `code` - contract code to upload. +/// * `url` - the rpc of the chain node. +pub async fn get_upload_payload(code: WasmCode, url: &str) -> anyhow::Result> { + let storage_deposit_limit: Option = None; + let upload_code = UploadCode::new(code, storage_deposit_limit, Determinism::Enforced); + + let rpc_client = subxt::backend::rpc::RpcClient::from_url(url).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + let call_data = upload_code.build(); + let mut encoded_data = Vec::::new(); + call_data.encode_call_data_to(&client.metadata(), &mut encoded_data)?; + Ok(encoded_data) +} + +/// Gets the encoded payload call data for a contract instantiation. +/// +/// # Arguments +/// * `instantiate_exec` - arguments for contract instantiate. +/// * `gas_limit` - max amount of gas to be used for instantiation. +pub fn get_instantiate_payload( + instantiate_exec: InstantiateExec, + gas_limit: Weight, +) -> anyhow::Result> { + let storage_deposit_limit: Option = None; + let mut encoded_data = Vec::::new(); + let args = instantiate_exec.args(); + match args.code() { + Code::Upload(code) => InstantiateWithCode::new( + args.value(), + gas_limit, + storage_deposit_limit, + code.clone(), + args.data().into(), + args.salt().into(), + ) + .build() + .encode_call_data_to(&instantiate_exec.client().metadata(), &mut encoded_data), + Code::Existing(hash) => Instantiate::new( + args.value(), + gas_limit, + storage_deposit_limit, + hash, + args.data().into(), + args.salt().into(), + ) + .build() + .encode_call_data_to(&instantiate_exec.client().metadata(), &mut encoded_data), + }?; + + Ok(encoded_data) +} + +/// Reads the contract code from contract file. +/// +/// # Arguments +/// * `path` - path to the contract file. +pub fn get_contract_code(path: Option<&PathBuf>) -> anyhow::Result { + let manifest_path = get_manifest_path(path.map(|p| p as &Path))?; + + // signer does not matter for this + let signer = create_signer("//Alice")?; + let extrinsic_opts = + ExtrinsicOptsBuilder::::new(signer) + .manifest_path(Some(manifest_path)) + .done(); + let artifacts = extrinsic_opts.contract_artifacts()?; + + let artifacts_path = artifacts.artifact_path().to_path_buf(); + let code = artifacts.code.ok_or_else(|| { + Error::UploadContractError(format!( + "Contract code not found from artifact file {}", + artifacts_path.display() + )) + })?; + Ok(code) +} + +/// Submit a pre-signed payload for uploading a contract. +/// +/// # Arguments +/// * `url` - rpc for chain. +/// * `payload` - the signed payload to submit (encoded call data). +pub async fn upload_contract_signed( + url: &str, + payload: String, +) -> anyhow::Result> { + let events = submit_signed_payload(url, payload).await?; + + let code_stored = events.find_first::>()?; + + Ok(UploadResult { code_stored, events }) +} + +/// Submit a pre-signed payload for instantiating a contract. +/// +/// # Arguments +/// * `url` - rpc for chain. +/// * `payload` - the signed payload to submit (encoded call data). +pub async fn instantiate_contract_signed( + url: &str, + payload: String, +) -> anyhow::Result> { + let events = submit_signed_payload(url, payload).await?; + + // The CodeStored event is only raised if the contract has not already been + // uploaded. + let code_hash = events + .find_first::>()? + .map(|code_stored| code_stored.code_hash); + + let instantiated = events + .find_first::>()? + .ok_or_else(|| { + Error::InstantiateContractError("Failed to find Instantiated event".to_string()) + })?; + + Ok(InstantiateExecResult { events, code_hash, contract_address: instantiated.contract }) +} + +/// Submit a pre-signed payload. +/// +/// # Arguments +/// * `url` - rpc for chain. +/// * `payload` - the signed payload to submit (encoded call data). +pub async fn submit_signed_payload( + url: &str, + payload: String, +) -> anyhow::Result> { + let rpc_client = subxt::backend::rpc::RpcClient::from_url(url).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + let hex_encoded = from_hex(&payload)?; + + let extrinsic = SubmittableExtrinsic::from_bytes(client, hex_encoded); + + // src: https://github.com/use-ink/cargo-contract/blob/68691b9b6cdb7c6ec52ea441b3dc31fcb1ce08e0/crates/extrinsics/src/lib.rs#L143 + + use subxt::{ + error::{RpcError, TransactionError}, + tx::TxStatus, + }; + + let mut tx = extrinsic.submit_and_watch().await?; + + while let Some(status) = tx.next().await { + match status? { + TxStatus::InFinalizedBlock(tx_in_block) => { + let events = tx_in_block.wait_for_success().await?; + return Ok(events) + }, + TxStatus::Error { message } => return Err(TransactionError::Error(message).into()), + TxStatus::Invalid { message } => return Err(TransactionError::Invalid(message).into()), + TxStatus::Dropped { message } => return Err(TransactionError::Dropped(message).into()), + _ => continue, + } + } + Err(RpcError::SubscriptionDropped.into()) +} + /// Estimate the gas required for instantiating a contract without modifying the state of the /// blockchain. /// /// # Arguments -/// /// * `instantiate_exec` - the preprocessed data to instantiate a contract. pub async fn dry_run_gas_estimate_instantiate( instantiate_exec: &InstantiateExec, @@ -136,14 +312,15 @@ pub async fn dry_run_gas_estimate_instantiate( /// Result of a dry-run upload of a smart contract. pub struct UploadDryRunResult { + /// The key under which the new code is stored. pub code_hash: String, + /// The deposit that was reserved at the caller. Is zero when the code already existed. pub deposit: String, } /// Performs a dry-run for uploading a contract without modifying the state of the blockchain. /// /// # Arguments -/// /// * `upload_exec` - the preprocessed data to upload a contract. pub async fn dry_run_upload( upload_exec: &UploadExec, @@ -175,7 +352,6 @@ pub struct ContractInfo { /// Instantiate a contract. /// /// # Arguments -/// /// * `instantiate_exec` - the preprocessed data to instantiate a contract. /// * `gas_limit` - maximum amount of gas to be used for this call. pub async fn instantiate_smart_contract( @@ -195,7 +371,6 @@ pub async fn instantiate_smart_contract( /// Upload a contract. /// /// # Arguments -/// /// * `upload_exec` - the preprocessed data to upload a contract. pub async fn upload_smart_contract( upload_exec: &UploadExec, @@ -204,14 +379,26 @@ pub async fn upload_smart_contract( .upload_code() .await .map_err(|error_variant| Error::UploadContractError(format!("{:?}", error_variant)))?; - if let Some(code_stored) = upload_result.code_stored { + get_code_hash_from_event(&upload_result, upload_exec.code().code_hash()) +} + +/// Get the code hash of a contract from the upload event. +/// +/// # Arguments +/// * `upload_result` - the result of uploading the contract. +/// * `metadata_code_hash` - the code hash from the metadata Used only for error reporting. +pub fn get_code_hash_from_event( + upload_result: &UploadResult, + // used for error reporting + metadata_code_hash: [u8; 32], +) -> Result { + if let Some(code_stored) = upload_result.code_stored.as_ref() { Ok(format!("{:?}", code_stored.code_hash)) } else { - let code_hash: String = - upload_exec.code().code_hash().iter().fold(String::new(), |mut output, b| { - write!(output, "{:02x}", b).expect("expected to write to string"); - output - }); + let code_hash: String = metadata_code_hash.iter().fold(String::new(), |mut output, b| { + write!(output, "{:02x}", b).expect("expected to write to string"); + output + }); Err(Error::UploadContractError(format!( "This contract has already been uploaded with code hash: 0x{code_hash}" ))) @@ -228,6 +415,10 @@ mod tests { use anyhow::Result; use pop_common::{find_free_port, set_executable_permission}; use std::{env, process::Command, time::Duration}; + use subxt::{ + config::{substrate::BlakeTwo256, Hasher}, + utils::H256, + }; use tokio::time::sleep; use url::Url; @@ -281,6 +472,43 @@ mod tests { Ok(()) } + #[tokio::test] + async fn get_payload_works() -> Result<()> { + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + let up_opts = UpOpts { + path: Some(temp_dir.path().join("testing")), + constructor: "new".to_string(), + args: ["false".to_string()].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + suri: "//Alice".to_string(), + }; + let contract_code = get_contract_code(up_opts.path.as_ref())?; + let call_data = get_upload_payload(contract_code, CONTRACTS_NETWORK_URL).await?; + let payload_hash = BlakeTwo256::hash(&call_data); + // We know that for the above opts the payload hash should be: + // 0x98c24584107b3a01d12e8e02c0bb634d15dc86123c44d186206813ede42f478d + let hex_bytes = + from_hex("98c24584107b3a01d12e8e02c0bb634d15dc86123c44d186206813ede42f478d") + .expect("Invalid hex string"); + + let hex_array: [u8; 32] = hex_bytes.try_into().expect("Expected 32-byte array"); + + // Create `H256` from the `[u8; 32]` array + let expected_hash = H256::from(hex_array); + assert_eq!(expected_hash, payload_hash); + Ok(()) + } + #[tokio::test] async fn dry_run_gas_estimate_instantiate_works() -> Result<()> { let temp_dir = new_environment("testing")?; @@ -365,7 +593,7 @@ mod tests { #[tokio::test] async fn instantiate_and_upload() -> Result<()> { - let random_port = find_free_port(); + let random_port = find_free_port(None); let localhost_url = format!("ws://127.0.0.1:{}", random_port); let temp_dir = new_environment("testing")?; let current_dir = env::current_dir().expect("Failed to get current directory"); diff --git a/crates/pop-contracts/src/utils/metadata.rs b/crates/pop-contracts/src/utils/metadata.rs index 111468f0e..0c2d26d6d 100644 --- a/crates/pop-contracts/src/utils/metadata.rs +++ b/crates/pop-contracts/src/utils/metadata.rs @@ -117,7 +117,7 @@ fn get_contract_functions( /// # Arguments /// * `path` - Location path of the project or contract artifact. /// * `message` - The label of the contract message. -fn get_message

(path: P, message: &str) -> Result +pub fn get_message

(path: P, message: &str) -> Result where P: AsRef, { diff --git a/crates/pop-contracts/tests/files/testing.wasm b/crates/pop-contracts/tests/files/testing.wasm index 430888669464e770d310139f29e8af1ebf865ba4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 3710 zcmb_fO>ZPu6}|7Hx~pC8aXC1S84uE{iiI30?T;bxhzSyYkzz9%A+uqL>>j(uxZBen zSNAv>DN1{y2#GCQ#0H5i8-4|1#S$U0VGA2J2nk5ed9T|aN+LFl<*Daa@1A?_x#!;3 zE#2WWB_isb)`1?6<-m-`V>LbyIj~r9$(_kxa-hOX?BcK*>ro>aR^8Lh?6?~34@YNp z_qY-h_BN7}!Sfd*k?`03Z2099*z2C|*VX7nJrEnM<}bRZFRJ}f-5m^{R&`w+iI{3| z(RfxpJF8!6rE~MfVxsU*8i-r0G*gWXMC-<)87qHzX|u8Tf4@rjxcoH9n)BKoc27^` zn~kID`RUn9xjXaUrN!69DQ9-iF>FHZX2 zoR>+jFisVgZwwAP!(-anj>tn1XT9p-t>%NKnKQ28l0E=GJ}*;j0$#bKS0;e_0Jce? zBG3=pKyzwU%a%rT-u4QF$cLE~1m_Y$*tietQQ7oM=S2&#HTzsOorQ5>r*N=#*0nAI zW@%k>RDTU}1F;kc(;+~lt6c+01Q;9BQP zJgAPeA85lN+6ms3h;YK42}eV!npF4877Hr2Db zo1sVqd%(S+6dZ{Xi7JaWKpB-nVI%>cE9+891aw{lswp5C9Yw7emt0J>a*ge>9z@cj zRuWVRe?gRi06;+FfnDStV+^_9(9IMFNhU!hR7ew%LA;<2+=j*qX$FO$nQk`~_=L+q zAOduHLf{NG0m>?slmMGb2c>}f|JhX3Rhh-6g7pXu2A)#HWkXXXE}J%0fQwC)UNu$n zwM}(J6awT7LDNqe+G~@s8|1L5Ch3J%Z)RTUu^yXk1foA^tGm!0l!j3SPS{I)U*7pqR3A@Pcalxt>?~of6Z>3xev%T=q~D zUvjcc{Quy}|HOVv&+6@9Ka1wd?*zH5>e(nGAUK)9ct_o%b`_XbX5B9xmfqyPQZ}u9mW9#KT5f2sSZ%Fn% zW{TMk3i+;ps?gQoUWcPBpZ`zT*BPgR(<~@br|~at$=d1nFE7;toq|O`fqOO#+PSp( zyxyi%)3U+GY#G=L+J#yWBq+iFN3VcH#*YSN?Ub`QB?f?K2SA{(00|3>VnD*09Z7|g ze%6NW(I`f;G$fxdX20fj0=74%Y$Mbyy%J7}U%_X9TE;Wxrm8s z#1kk+nYtwZ29+1i8^3f>pzVErftnbfUK9=XI1HD1_&8ufn;XBr3k&1_D)IR@*+p&s zsdj5jZwvxIw~Ji3_||S+w=L34^BKBWMM#(tK>}|Rw<9a~G=kPw{*OS3<_`fB?SBKz zw!x@Tv%GQrpVais&*&{4`m^8=Mr~}k>u`A=gv6H+{{rI2^$_oPM6!fwh2f?c)dEtJ zSoYN^M=Kl{q*S45A#$|is(!oFY$2>x7(Oz?t&yMx=qimL=!DZPo!Gse;VlgqpFk1T<3Se(<8FPCwND(&g%kyOvL!IYK=m*JP zmn?rRu%GO+T7t~3Wx7;Cb352U zVE2gGk%gcx0Q?1J5#YDif+qqbh$}I~I=K&n)}dPr8cqZo|G0B3lr{xdIV0 z)*uXc#b5}-hXn@X=lNp3FeRT=fVksU_Ni86)@J`w>pvb=^>Dp=T7C4S>JHY|4!ia7 z*?L_apA1L!%k^P>xPCG?syR+XyDr;P_-vt)C6{4}aP{8LU5f@f6>v*6N{%lIJ4w6a3!6@BhXB^KN}Idf6G` hi&oV+JUglmJKtH`THCxIVhI~`eF@)v`23K*zW{;(I0gU! diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index 64bef2d67..6380fbd10 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -24,11 +24,11 @@ tokio.workspace = true url.workspace = true askama.workspace = true -hex.workspace = true indexmap.workspace = true reqwest.workspace = true scale-info.workspace = true scale-value.workspace = true +sp-core.workspace = true subxt.workspace = true symlink.workspace = true toml_edit.workspace = true diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 1f53f523c..5a9a9a2cb 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -4,7 +4,7 @@ use crate::errors::Error; use params::Param; use scale_value::stringify::custom_parsers; use std::fmt::{Display, Formatter}; -use subxt::{dynamic::Value, Metadata, OnlineClient, SubstrateConfig}; +use subxt::{dynamic::Value, utils::to_hex, Metadata, OnlineClient, SubstrateConfig}; pub mod action; pub mod params; @@ -179,7 +179,7 @@ pub fn parse_dispatchable_arguments( .map(|(param, raw_param)| { // Convert sequence parameters to hex if is_sequence let processed_param = if param.is_sequence && !raw_param.starts_with("0x") { - format!("0x{}", hex::encode(raw_param)) + to_hex(&raw_param) } else { raw_param }; @@ -199,6 +199,7 @@ mod tests { use crate::{call::tests::POP_NETWORK_TESTNET_URL, set_up_client}; use anyhow::Result; + use sp_core::bytes::from_hex; use subxt::ext::scale_bits; #[tokio::test] @@ -283,7 +284,7 @@ mod tests { ] .to_vec(); let addr: Vec<_> = - hex::decode("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48") + from_hex("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48") .unwrap() .into_iter() .map(|b| Value::u128(b as u128)) diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 1f7e61b22..f5bb05b71 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -5,12 +5,12 @@ use pop_common::{ call::{DefaultEnvironment, DisplayEvents, TokenMetadata, Verbosity}, create_signer, }; +use sp_core::bytes::{from_hex, to_hex}; use subxt::{ dynamic::Value, - tx::{DynamicPayload, Payload}, + tx::{DynamicPayload, Payload, SubmittableExtrinsic}, OnlineClient, SubstrateConfig, }; - pub mod metadata; /// Sets up an [OnlineClient] instance for connecting to a blockchain. @@ -82,6 +82,28 @@ pub async fn sign_and_submit_extrinsic( Ok(format!("Extrinsic Submitted with hash: {:?}\n\n{}", result.extrinsic_hash(), events)) } +/// Submits a signed extrinsic. +/// +/// # Arguments +/// * `client` - The client used to interact with the chain. +/// * `payload` - The signed payload string to be submitted. +pub async fn submit_signed_extrinsic( + client: OnlineClient, + payload: String, +) -> Result { + let hex_encoded = + from_hex(&payload).map_err(|e| Error::CallDataDecodingError(e.to_string()))?; + let extrinsic = SubmittableExtrinsic::from_bytes(client, hex_encoded); + let result = extrinsic + .submit_and_watch() + .await + .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))? + .wait_for_finalized_success() + .await + .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?; + Ok(format!("{:?}", result.extrinsic_hash())) +} + /// Encodes the call data for a given extrinsic into a hexadecimal string. /// /// # Arguments @@ -94,7 +116,7 @@ pub fn encode_call_data( let call_data = xt .encode_call_data(&client.metadata()) .map_err(|e| Error::CallDataEncodingError(e.to_string()))?; - Ok(format!("0x{}", hex::encode(call_data))) + Ok(to_hex(&call_data, false)) } /// Decodes a hex-encoded string into a vector of bytes representing the call data. @@ -102,8 +124,7 @@ pub fn encode_call_data( /// # Arguments /// * `call_data` - The hex-encoded string representing call data. pub fn decode_call_data(call_data: &str) -> Result, Error> { - hex::decode(call_data.trim_start_matches("0x")) - .map_err(|e| Error::CallDataDecodingError(e.to_string())) + from_hex(call_data).map_err(|e| Error::CallDataDecodingError(e.to_string())) } /// This struct implements the [`Payload`] trait and is used to submit diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 5bba81542..7e70c2149 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -24,14 +24,17 @@ pub use call::{ params::Param, parse_chain_metadata, Function, Pallet, }, - set_up_client, sign_and_submit_extrinsic, CallData, + set_up_client, sign_and_submit_extrinsic, submit_signed_extrinsic, CallData, }; pub use errors::Error; pub use indexmap::IndexSet; pub use new_pallet::{create_pallet_template, new_pallet_options::*, TemplatePalletConfig}; pub use new_parachain::instantiate_template_dir; // External export from subxt. -pub use subxt::{tx::DynamicPayload, OnlineClient, SubstrateConfig}; +pub use subxt::{ + tx::{DynamicPayload, Payload}, + OnlineClient, SubstrateConfig, +}; pub use templates::{Config, Parachain, Provider}; pub use up::Zombienet; pub use utils::helpers::is_initial_endowment_valid; From c1ed1ceaf28a7f176a9097680aca32d26dc1865c Mon Sep 17 00:00:00 2001 From: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:28:46 +0000 Subject: [PATCH 002/117] fix: hrmp (#278) * chore: switch pop network configuration to paseo with hrmp channels * test: add relay+asset hub network config files * feat: clean dmpq state at genesis (#279) * dispatch kill tx * paseo-local metadata * chore: address hrmp channels at creation * clean unnecessary deps * fix: don't wait for finalisation * Provide documentation * refactor: improve ux * feat: add rococo-local metadata * fix: resolve merge conflict * chore: update zombienet-sdk * chore: update cargo.lock * refactor: use dynamic tx * docs: add variant doc comment * refactor: address clippy warning * chore: remove duplicate dependency after rebase * refactor: identify hrmp_channels earlier * test: add supported relay chains test * refactor: use construct_sudo_extrinsic * refactor: split into smaller functions for better testing * chore: add westend network configs * refactor: replace rococo with westend * refactor: remove duplicate import Co-authored-by: Peter White * refactor: remove unusued parameter Co-authored-by: Peter White * refactor: use global import Co-authored-by: Peter White * fix: update error message * refactor: improve function name * refactor: use non-clashing variable name to support global import * refactor: reuse spinner --------- Co-authored-by: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Co-authored-by: Peter White --- Cargo.lock | 1 + crates/pop-cli/src/commands/call/chain.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 43 +++++- crates/pop-parachains/Cargo.toml | 3 +- crates/pop-parachains/src/call/mod.rs | 6 +- crates/pop-parachains/src/errors.rs | 3 + crates/pop-parachains/src/lib.rs | 2 + crates/pop-parachains/src/relay.rs | 146 ++++++++++++++++++++ crates/pop-parachains/src/up/mod.rs | 69 ++++++++- crates/pop-parachains/src/utils/helpers.rs | 4 +- tests/networks/kusama+asset-hub.toml | 19 +++ tests/networks/paseo+asset-hub.toml | 19 +++ tests/networks/polkadot+asset-hub.toml | 19 +++ tests/networks/pop.toml | 14 +- tests/networks/westend+asset-hub.toml | 19 +++ tests/networks/westend.toml | 12 ++ 16 files changed, 364 insertions(+), 17 deletions(-) create mode 100644 crates/pop-parachains/src/relay.rs create mode 100644 tests/networks/kusama+asset-hub.toml create mode 100644 tests/networks/paseo+asset-hub.toml create mode 100644 tests/networks/polkadot+asset-hub.toml create mode 100644 tests/networks/westend+asset-hub.toml create mode 100644 tests/networks/westend.toml diff --git a/Cargo.lock b/Cargo.lock index f581a91d5..ac67e52da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9469,6 +9469,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "subxt", + "subxt-signer", "symlink", "tar", "tempfile", diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 41348826f..18f61fe77 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -403,7 +403,7 @@ impl Call { }, }; // If sudo is required, wrap the call in a sudo call. - let xt = if self.sudo { construct_sudo_extrinsic(xt)? } else { xt }; + let xt = if self.sudo { construct_sudo_extrinsic(xt) } else { xt }; let encoded_data = encode_call_data(client, &xt)?; // If the encoded call data is too long, don't display it all. if encoded_data.len() < ENCODED_CALL_DATA_MAX_LEN { diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 4d59646d0..30fc6fce0 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -3,13 +3,13 @@ use crate::style::{style, Theme}; use clap::Args; use cliclack::{ - clear_screen, confirm, intro, log, multi_progress, outro, outro_cancel, set_theme, ProgressBar, - Theme as _, ThemeState, + clear_screen, confirm, intro, log, multi_progress, outro, outro_cancel, set_theme, spinner, + ProgressBar, Theme as _, ThemeState, }; use console::{Emoji, Style, Term}; use duct::cmd; use pop_common::Status; -use pop_parachains::{Error, IndexSet, NetworkNode, Zombienet}; +use pop_parachains::{clear_dmpq, Error, IndexSet, NetworkNode, RelayChain, Zombienet}; use std::{path::Path, time::Duration}; use tokio::time::sleep; @@ -91,8 +91,8 @@ impl ZombienetCommand { } // Finally spawn network and wait for signal to terminate - let spinner = cliclack::spinner(); - spinner.start("🚀 Launching local network..."); + let progress = spinner(); + progress.start("🚀 Launching local network..."); match zombienet.spawn().await { Ok(network) => { let mut result = @@ -143,10 +143,39 @@ impl ZombienetCommand { } if let Some(command) = &self.command { - run_custom_command(&spinner, command).await?; + run_custom_command(&progress, command).await?; + } + + progress.stop(result); + + // Check for any specified channels + if zombienet.hrmp_channels() { + let relay_chain = zombienet.relay_chain(); + match RelayChain::from(relay_chain) { + None => { + log::error(format!("🚫 Using `{relay_chain}` with HRMP channels is currently unsupported. Please use `paseo-local` or `westend-local`."))?; + }, + Some(_) => { + let progress = spinner(); + progress.start("Connecting to relay chain to prepare channels..."); + // Allow relay node time to start + sleep(Duration::from_secs(10)).await; + progress.set_message("Preparing channels..."); + let relay_endpoint = network.relaychain().nodes()[0].client().await?; + let para_ids: Vec<_> = + network.parachains().iter().map(|p| p.para_id()).collect(); + tokio::spawn(async move { + if let Err(e) = clear_dmpq(relay_endpoint, ¶_ids).await { + progress.stop(format!("🚫 Could not prepare channels: {e}")); + return Ok::<(), Error>(()); + } + progress.stop("Channels successfully prepared for initialization."); + Ok::<(), Error>(()) + }); + }, + } } - spinner.stop(result); tokio::signal::ctrl_c().await?; outro("Done")?; }, diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index 6380fbd10..89c1df6d9 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -17,6 +17,8 @@ glob.workspace = true serde_json.workspace = true strum.workspace = true strum_macros.workspace = true +subxt-signer.workspace = true +subxt.workspace = true tar.workspace = true tempfile.workspace = true thiserror.workspace = true @@ -29,7 +31,6 @@ reqwest.workspace = true scale-info.workspace = true scale-value.workspace = true sp-core.workspace = true -subxt.workspace = true symlink.workspace = true toml_edit.workspace = true walkdir.workspace = true diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index f5bb05b71..4569687ec 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -41,8 +41,8 @@ pub fn construct_extrinsic( /// # Arguments /// * `xt`: The extrinsic representing the dispatchable function call to be dispatched with `Root` /// privileges. -pub fn construct_sudo_extrinsic(xt: DynamicPayload) -> Result { - Ok(subxt::dynamic::tx("Sudo", "sudo", [xt.into_value()].to_vec())) +pub fn construct_sudo_extrinsic(xt: DynamicPayload) -> DynamicPayload { + subxt::dynamic::tx("Sudo", "sudo", [xt.into_value()].to_vec()) } /// Signs and submits a given extrinsic. @@ -255,7 +255,7 @@ mod tests { "100".to_string(), ], )?; - let xt = construct_sudo_extrinsic(xt)?; + let xt = construct_sudo_extrinsic(xt); assert_eq!(xt.call_name(), "sudo"); assert_eq!(xt.pallet_name(), "Sudo"); Ok(()) diff --git a/crates/pop-parachains/src/errors.rs b/crates/pop-parachains/src/errors.rs index 8bd97434a..2dd8805ac 100644 --- a/crates/pop-parachains/src/errors.rs +++ b/crates/pop-parachains/src/errors.rs @@ -62,6 +62,9 @@ pub enum Error { RustfmtError(std::io::Error), #[error("Template error: {0}")] SourcingError(#[from] pop_common::sourcing::Error), + /// An error occurred whilst interacting with a chain using `subxt`. + #[error("Subxt error: {0}")] + SubXtError(#[from] subxt::Error), #[error("Toml error: {0}")] TomlError(#[from] toml_edit::de::Error), #[error("Unsupported command: {0}")] diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 7e70c2149..50bb6af5b 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -8,6 +8,7 @@ mod errors; mod generator; mod new_pallet; mod new_parachain; +mod relay; mod templates; mod up; mod utils; @@ -30,6 +31,7 @@ pub use errors::Error; pub use indexmap::IndexSet; pub use new_pallet::{create_pallet_template, new_pallet_options::*, TemplatePalletConfig}; pub use new_parachain::instantiate_template_dir; +pub use relay::{clear_dmpq, RelayChain}; // External export from subxt. pub use subxt::{ tx::{DynamicPayload, Payload}, diff --git a/crates/pop-parachains/src/relay.rs b/crates/pop-parachains/src/relay.rs new file mode 100644 index 000000000..6ded8c497 --- /dev/null +++ b/crates/pop-parachains/src/relay.rs @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::{call, DynamicPayload, Error}; +use sp_core::twox_128; +use subxt::{ + config::BlockHash, + dynamic::{self, Value}, + ext::sp_core, + OnlineClient, PolkadotConfig, +}; + +/// Clears the DMPQ state for the given parachain IDs. +/// +/// # Arguments +/// * `client` - Client for the network which state is to be modified. +/// * `para_ids` - List of ids to build the keys that will be mutated. +pub async fn clear_dmpq( + client: OnlineClient, + para_ids: &[u32], +) -> Result { + // Wait for blocks to be produced. + let mut sub = client.blocks().subscribe_finalized().await?; + for _ in 0..2 { + sub.next().await; + } + + // Generate storage keys to be removed + let clear_dmq_keys = generate_storage_keys(para_ids); + + // Submit calls to remove specified keys + let kill_storage = construct_kill_storage_call(clear_dmq_keys); + let sudo = subxt_signer::sr25519::dev::alice(); + let sudo_call = call::construct_sudo_extrinsic(kill_storage); + Ok(client.tx().sign_and_submit_default(&sudo_call, &sudo).await?) +} + +fn construct_kill_storage_call(keys: Vec>) -> DynamicPayload { + dynamic::tx( + "System", + "kill_storage", + vec![Value::unnamed_composite(keys.into_iter().map(Value::from_bytes))], + ) +} + +fn generate_storage_keys(para_ids: &[u32]) -> Vec> { + let dmp = twox_128("Dmp".as_bytes()); + let dmp_queue_heads = twox_128("DownwardMessageQueueHeads".as_bytes()); + let dmp_queues = twox_128("DownwardMessageQueues".as_bytes()); + let mut clear_dmq_keys = Vec::>::new(); + for id in para_ids { + let id = id.to_le_bytes(); + // DMP Queue Head + let mut key = dmp.to_vec(); + key.extend(&dmp_queue_heads); + key.extend(sp_core::twox_64(&id)); + key.extend(id); + clear_dmq_keys.push(key); + // DMP Queue + let mut key = dmp.to_vec(); + key.extend(&dmp_queues); + key.extend(sp_core::twox_64(&id)); + key.extend(id); + clear_dmq_keys.push(key); + } + clear_dmq_keys +} + +/// A supported relay chain. +#[derive(Debug, PartialEq)] +pub enum RelayChain { + /// Paseo. + PaseoLocal, + /// Westend. + WestendLocal, +} + +impl RelayChain { + /// Attempts to convert a chain identifier into a supported `RelayChain` variant. + /// + /// # Arguments + /// * `id` - The relay chain identifier. + pub fn from(id: &str) -> Option { + match id { + "paseo-local" => Some(RelayChain::PaseoLocal), + "westend-local" => Some(RelayChain::WestendLocal), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use subxt::ext::sp_core::twox_64; + use RelayChain::*; + + #[test] + fn construct_kill_storage_call_works() { + let keys = vec!["key".as_bytes().to_vec()]; + assert_eq!( + construct_kill_storage_call(keys.clone()), + dynamic::tx( + "System", + "kill_storage", + vec![Value::unnamed_composite(keys.into_iter().map(Value::from_bytes))], + ) + ) + } + + #[test] + fn generate_storage_keys_works() { + let para_ids = vec![1_000, 4_385]; + let dmp = twox_128("Dmp".as_bytes()); + let dmp_queue_heads = [dmp, twox_128("DownwardMessageQueueHeads".as_bytes())].concat(); + let dmp_queues = [dmp, twox_128("DownwardMessageQueues".as_bytes())].concat(); + + assert_eq!( + generate_storage_keys(¶_ids), + para_ids + .iter() + .flat_map(|id| { + let id = id.to_le_bytes().to_vec(); + [ + // DMP Queue Head + [dmp_queue_heads.clone(), twox_64(&id).to_vec(), id.clone()].concat(), + // DMP Queue + [dmp_queues.clone(), twox_64(&id).to_vec(), id].concat(), + ] + }) + .collect::>() + ) + } + + #[test] + fn supported_relay_chains() { + for (s, e) in [ + // Only chains with sudo supported + ("paseo-local", Some(PaseoLocal)), + ("westend-local", Some(WestendLocal)), + ("kusama-local", None), + ("polkadot-local", None), + ] { + assert_eq!(RelayChain::from(s), e) + } + } +} diff --git a/crates/pop-parachains/src/up/mod.rs b/crates/pop-parachains/src/up/mod.rs index 036ae889d..91c1678af 100644 --- a/crates/pop-parachains/src/up/mod.rs +++ b/crates/pop-parachains/src/up/mod.rs @@ -30,6 +30,8 @@ pub struct Zombienet { relay_chain: RelayChain, /// The configuration required to launch parachains. parachains: IndexMap, + /// Whether any HRMP channels are to be pre-opened. + hrmp_channels: bool, } impl Zombienet { @@ -59,7 +61,7 @@ impl Zombienet { // Parse network config let network_config = NetworkConfiguration::from(network_config)?; // Determine relay and parachain requirements based on arguments and config - let relay_chain = Self::relay_chain( + let relay_chain = Self::init_relay_chain( relay_chain_version, relay_chain_runtime_version, &network_config, @@ -84,7 +86,9 @@ impl Zombienet { cache, ) .await?; - Ok(Self { network_config, relay_chain, parachains }) + let hrmp_channels = + network_config.hrmp_channels().map(|c| !c.is_empty()).unwrap_or_default(); + Ok(Self { network_config, relay_chain, parachains, hrmp_channels }) } /// The binaries required to launch the network. @@ -216,7 +220,7 @@ impl Zombienet { /// will use the latest available version). /// * `network_config` - The network configuration to be used to launch a network. /// * `cache` - The location used for caching binaries. - async fn relay_chain( + async fn init_relay_chain( version: Option<&str>, runtime_version: Option<&str>, network_config: &NetworkConfiguration, @@ -276,6 +280,16 @@ impl Zombienet { Ok(relay::default(version, runtime_version, chain, cache).await?) } + /// The name of the relay chain. + pub fn relay_chain(&self) -> &str { + &self.relay_chain.chain + } + + /// Whether any HRMP channels are to be pre-opened. + pub fn hrmp_channels(&self) -> bool { + self.hrmp_channels + } + /// Launches the local network. pub async fn spawn(&mut self) -> Result, Error> { // Symlink polkadot workers @@ -354,6 +368,11 @@ impl NetworkConfiguration { self.0.get_mut("parachains").and_then(|p| p.as_array_of_tables_mut()) } + /// Returns the `hrmp_channels` configuration. + fn hrmp_channels(&self) -> Option<&ArrayOfTables> { + self.0.get("hrmp_channels").and_then(|p| p.as_array_of_tables()) + } + /// Returns the `command` configuration. fn command(config: &Table) -> Option<&Item> { config.get("command") @@ -706,6 +725,8 @@ chain = "paseo-local" if *tag == Some(version.to_string()) )); assert!(zombienet.parachains.is_empty()); + assert_eq!(zombienet.relay_chain(), "paseo-local"); + assert!(!zombienet.hrmp_channels()); Ok(()) } @@ -1186,6 +1207,48 @@ default_command = "moonbeam" Ok(()) } + #[tokio::test] + async fn new_with_hrmp_channels_works() -> Result<()> { + let temp_dir = tempdir()?; + let cache = PathBuf::from(temp_dir.path()); + let config = Builder::new().suffix(".toml").tempfile()?; + writeln!( + config.as_file(), + r#" +[relaychain] +chain = "paseo-local" + +[[parachains]] +id = 1000 +chain = "asset-hub-paseo-local" + +[[parachains]] +id = 4385 +default_command = "pop-node" + +[[hrmp_channels]] +sender = 4385 +recipient = 1000 +max_capacity = 1000 +max_message_size = 8000 +"# + )?; + + let zombienet = Zombienet::new( + &cache, + config.path().to_str().unwrap(), + None, + None, + None, + None, + None, + ) + .await?; + + assert!(zombienet.hrmp_channels()); + Ok(()) + } + #[tokio::test] async fn new_ensures_parachain_id_exists() -> Result<()> { let temp_dir = tempdir()?; diff --git a/crates/pop-parachains/src/utils/helpers.rs b/crates/pop-parachains/src/utils/helpers.rs index bd39eec9e..fd8e91d68 100644 --- a/crates/pop-parachains/src/utils/helpers.rs +++ b/crates/pop-parachains/src/utils/helpers.rs @@ -33,7 +33,8 @@ pub fn is_initial_endowment_valid(initial_endowment: &str) -> bool { initial_endowment.parse::().is_ok() || is_valid_bitwise_left_shift(initial_endowment).is_ok() } -// Auxiliar method to check if the endowment input with a shift left (1u64 << 60) format is valid. + +// Auxiliary method to check if the endowment input with a shift left (1u64 << 60) format is valid. // Parse the self << rhs format and check the shift left operation is valid. fn is_valid_bitwise_left_shift(initial_endowment: &str) -> Result { let v: Vec<&str> = initial_endowment.split(" << ").collect(); @@ -87,6 +88,7 @@ mod tests { use super::*; use crate::generator::parachain::ChainSpec; use askama::Template; + use std::env::var; use tempfile::tempdir; #[test] diff --git a/tests/networks/kusama+asset-hub.toml b/tests/networks/kusama+asset-hub.toml new file mode 100644 index 000000000..c652bd841 --- /dev/null +++ b/tests/networks/kusama+asset-hub.toml @@ -0,0 +1,19 @@ +# pop up parachain -f ./tests/networks/kusama+asset-hub.toml + +[relaychain] +chain = "kusama-local" + +[[relaychain.nodes]] +name = "alice" +validator = true + +[[relaychain.nodes]] +name = "bob" +validator = true + +[[parachains]] +id = 1000 +chain = "asset-hub-kusama-local" + +[[parachains.collators]] +name = "asset-hub" \ No newline at end of file diff --git a/tests/networks/paseo+asset-hub.toml b/tests/networks/paseo+asset-hub.toml new file mode 100644 index 000000000..a75719376 --- /dev/null +++ b/tests/networks/paseo+asset-hub.toml @@ -0,0 +1,19 @@ +# pop up parachain -f ./tests/networks/paseo+asset-hub.toml + +[relaychain] +chain = "paseo-local" + +[[relaychain.nodes]] +name = "alice" +validator = true + +[[relaychain.nodes]] +name = "bob" +validator = true + +[[parachains]] +id = 1000 +chain = "asset-hub-paseo-local" + +[[parachains.collators]] +name = "asset-hub" \ No newline at end of file diff --git a/tests/networks/polkadot+asset-hub.toml b/tests/networks/polkadot+asset-hub.toml new file mode 100644 index 000000000..9cf6735b2 --- /dev/null +++ b/tests/networks/polkadot+asset-hub.toml @@ -0,0 +1,19 @@ +# pop up parachain -f ./tests/networks/polkadot+asset-hub.toml + +[relaychain] +chain = "polkadot-local" + +[[relaychain.nodes]] +name = "alice" +validator = true + +[[relaychain.nodes]] +name = "bob" +validator = true + +[[parachains]] +id = 1000 +chain = "asset-hub-polkadot-local" + +[[parachains.collators]] +name = "asset-hub" \ No newline at end of file diff --git a/tests/networks/pop.toml b/tests/networks/pop.toml index 9ad7c871d..643f1a0b5 100644 --- a/tests/networks/pop.toml +++ b/tests/networks/pop.toml @@ -24,4 +24,16 @@ default_command = "pop-node" [[parachains.collators]] name = "pop" -args = ["-lruntime::contracts=debug"] \ No newline at end of file +args = ["-lruntime::contracts=debug"] + +[[hrmp_channels]] +sender = 1000 +recipient = 4385 +max_capacity = 1000 +max_message_size = 5000 + +[[hrmp_channels]] +sender = 4385 +recipient = 1000 +max_capacity = 1000 +max_message_size = 8000 \ No newline at end of file diff --git a/tests/networks/westend+asset-hub.toml b/tests/networks/westend+asset-hub.toml new file mode 100644 index 000000000..3404173f8 --- /dev/null +++ b/tests/networks/westend+asset-hub.toml @@ -0,0 +1,19 @@ +# pop up parachain -f ./tests/networks/westend+asset-hub.toml + +[relaychain] +chain = "westend-local" + +[[relaychain.nodes]] +name = "alice" +validator = true + +[[relaychain.nodes]] +name = "bob" +validator = true + +[[parachains]] +id = 1000 +chain = "asset-hub-westend-local" + +[[parachains.collators]] +name = "asset-hub" \ No newline at end of file diff --git a/tests/networks/westend.toml b/tests/networks/westend.toml new file mode 100644 index 000000000..7bdb66bdd --- /dev/null +++ b/tests/networks/westend.toml @@ -0,0 +1,12 @@ +# pop up parachain -f ./tests/networks/westend.toml + +[relaychain] +chain = "westend-local" + +[[relaychain.nodes]] +name = "alice" +validator = true + +[[relaychain.nodes]] +name = "bob" +validator = true From 21d02dbfd836067f6cce12716cf8e818b982c51f Mon Sep 17 00:00:00 2001 From: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:29:13 +0000 Subject: [PATCH 003/117] refactor(up): bump fallback versions (#393) * refactor: bump fallback versions * test: bump versions --- crates/pop-parachains/src/up/chain_specs.rs | 12 ++-- crates/pop-parachains/src/up/mod.rs | 61 ++++++++++----------- crates/pop-parachains/src/up/parachains.rs | 20 +++---- crates/pop-parachains/src/up/relay.rs | 16 +++--- 4 files changed, 52 insertions(+), 57 deletions(-) diff --git a/crates/pop-parachains/src/up/chain_specs.rs b/crates/pop-parachains/src/up/chain_specs.rs index 3513a587e..e81db7ee9 100644 --- a/crates/pop-parachains/src/up/chain_specs.rs +++ b/crates/pop-parachains/src/up/chain_specs.rs @@ -22,7 +22,7 @@ pub(super) enum Runtime { Repository = "https://github.com/r0gue-io/polkadot-runtimes", Binary = "chain-spec-generator", Chain = "kusama-local", - Fallback = "v1.2.7" + Fallback = "v1.3.3" ))] Kusama, /// Paseo. @@ -30,7 +30,7 @@ pub(super) enum Runtime { Repository = "https://github.com/r0gue-io/paseo-runtimes", Binary = "chain-spec-generator", Chain = "paseo-local", - Fallback = "v1.2.6" + Fallback = "v1.3.4" ))] Paseo, /// Polkadot. @@ -38,7 +38,7 @@ pub(super) enum Runtime { Repository = "https://github.com/r0gue-io/polkadot-runtimes", Binary = "chain-spec-generator", Chain = "polkadot-local", - Fallback = "v1.2.7" + Fallback = "v1.3.3" ))] Polkadot, } @@ -111,7 +111,7 @@ mod tests { #[tokio::test] async fn kusama_works() -> anyhow::Result<()> { let expected = Runtime::Kusama; - let version = "v1.2.7"; + let version = "v1.3.3"; let temp_dir = tempdir()?; let binary = chain_spec_generator("kusama-local", Some(version), temp_dir.path()) .await? @@ -135,7 +135,7 @@ mod tests { #[tokio::test] async fn paseo_works() -> anyhow::Result<()> { let expected = Runtime::Paseo; - let version = "v1.2.4"; + let version = "v1.3.4"; let temp_dir = tempdir()?; let binary = chain_spec_generator("paseo-local", Some(version), temp_dir.path()) .await? @@ -159,7 +159,7 @@ mod tests { #[tokio::test] async fn polkadot_works() -> anyhow::Result<()> { let expected = Runtime::Polkadot; - let version = "v1.2.7"; + let version = "v1.3.3"; let temp_dir = tempdir()?; let binary = chain_spec_generator("polkadot-local", Some(version), temp_dir.path()) .await? diff --git a/crates/pop-parachains/src/up/mod.rs b/crates/pop-parachains/src/up/mod.rs index 91c1678af..abefa8051 100644 --- a/crates/pop-parachains/src/up/mod.rs +++ b/crates/pop-parachains/src/up/mod.rs @@ -679,6 +679,8 @@ mod tests { use std::{env::current_dir, fs::File, io::Write}; use tempfile::tempdir; + pub(crate) const VERSION: &str = "stable2409"; + mod zombienet { use super::*; use pop_common::Status; @@ -702,12 +704,11 @@ mod tests { chain = "paseo-local" "# )?; - let version = "v1.12.0"; let zombienet = Zombienet::new( &cache, config.path().to_str().unwrap(), - Some(version), + Some(VERSION), None, None, None, @@ -717,12 +718,12 @@ chain = "paseo-local" let relay_chain = &zombienet.relay_chain.binary; assert_eq!(relay_chain.name(), "polkadot"); - assert_eq!(relay_chain.path(), temp_dir.path().join(format!("polkadot-{version}"))); - assert_eq!(relay_chain.version().unwrap(), version); + assert_eq!(relay_chain.path(), temp_dir.path().join(format!("polkadot-{VERSION}"))); + assert_eq!(relay_chain.version().unwrap(), VERSION); assert!(matches!( relay_chain, Binary::Source { source: Source::GitHub(ReleaseArchive { tag, .. }), .. } - if *tag == Some(version.to_string()) + if *tag == Some(VERSION.to_string()) )); assert!(zombienet.parachains.is_empty()); assert_eq!(zombienet.relay_chain(), "paseo-local"); @@ -742,7 +743,7 @@ chain = "paseo-local" chain = "paseo-local" "# )?; - let version = "v1.2.7"; + let version = "v1.3.3"; let zombienet = Zombienet::new( &cache, @@ -782,15 +783,14 @@ chain = "paseo-local" r#" [relaychain] chain = "paseo-local" -default_command = "./bin-v1.6.0/polkadot" +default_command = "./bin-stable2409/polkadot" "# )?; - let version = "v1.12.0"; let zombienet = Zombienet::new( &cache, config.path().to_str().unwrap(), - Some(version), + Some(VERSION), None, None, None, @@ -800,12 +800,12 @@ default_command = "./bin-v1.6.0/polkadot" let relay_chain = &zombienet.relay_chain.binary; assert_eq!(relay_chain.name(), "polkadot"); - assert_eq!(relay_chain.path(), temp_dir.path().join(format!("polkadot-{version}"))); - assert_eq!(relay_chain.version().unwrap(), version); + assert_eq!(relay_chain.path(), temp_dir.path().join(format!("polkadot-{VERSION}"))); + assert_eq!(relay_chain.version().unwrap(), VERSION); assert!(matches!( relay_chain, Binary::Source { source: Source::GitHub(ReleaseArchive { tag, .. }), .. } - if *tag == Some(version.to_string()) + if *tag == Some(VERSION.to_string()) )); assert!(zombienet.parachains.is_empty()); Ok(()) @@ -828,12 +828,11 @@ validator = true command = "polkadot" "# )?; - let version = "v1.12.0"; let zombienet = Zombienet::new( &cache, config.path().to_str().unwrap(), - Some(version), + Some(VERSION), None, None, None, @@ -843,12 +842,12 @@ command = "polkadot" let relay_chain = &zombienet.relay_chain.binary; assert_eq!(relay_chain.name(), "polkadot"); - assert_eq!(relay_chain.path(), temp_dir.path().join(format!("polkadot-{version}"))); - assert_eq!(relay_chain.version().unwrap(), version); + assert_eq!(relay_chain.path(), temp_dir.path().join(format!("polkadot-{VERSION}"))); + assert_eq!(relay_chain.version().unwrap(), VERSION); assert!(matches!( relay_chain, Binary::Source { source: Source::GitHub(ReleaseArchive { tag, .. }), .. } - if *tag == Some(version.to_string()) + if *tag == Some(VERSION.to_string()) )); assert!(zombienet.parachains.is_empty()); Ok(()) @@ -873,14 +872,14 @@ command = "polkadot" [[relaychain.nodes]] name = "bob" validator = true -command = "polkadot-v1.12.0" +command = "polkadot-stable2409" "# )?; assert!(matches!( Zombienet::new(&cache, config.path().to_str().unwrap(), None, None, None, None, None).await, Err(Error::UnsupportedCommand(error)) - if error == "the relay chain command is unsupported: polkadot-v1.12.0" + if error == "the relay chain command is unsupported: polkadot-stable2409" )); Ok(()) } @@ -900,14 +899,14 @@ default_command = "polkadot" [[relaychain.nodes]] name = "alice" validator = true -command = "polkadot-v1.12.0" +command = "polkadot-stable2409" "# )?; assert!(matches!( Zombienet::new(&cache, config.path().to_str().unwrap(), None, None, None, None, None).await, Err(Error::UnsupportedCommand(error)) - if error == "the relay chain command is unsupported: polkadot-v1.12.0" + if error == "the relay chain command is unsupported: polkadot-stable2409" )); Ok(()) } @@ -928,12 +927,12 @@ id = 1000 chain = "asset-hub-paseo-local" "# )?; - let system_parachain_version = "v1.12.0"; + let system_parachain_version = "stable2407"; let zombienet = Zombienet::new( &cache, config.path().to_str().unwrap(), - Some("v1.11.0"), + Some(VERSION), None, Some(system_parachain_version), None, @@ -973,7 +972,6 @@ id = 1000 chain = "asset-hub-paseo-local" "# )?; - let version = "v1.12.0"; let zombienet = Zombienet::new( &cache, @@ -981,7 +979,7 @@ chain = "asset-hub-paseo-local" None, None, None, - Some(version), + Some(VERSION), None, ) .await?; @@ -993,13 +991,13 @@ chain = "asset-hub-paseo-local" assert_eq!(chain_spec_generator.name(), "paseo-chain-spec-generator"); assert_eq!( chain_spec_generator.path(), - temp_dir.path().join(format!("paseo-chain-spec-generator-{version}")) + temp_dir.path().join(format!("paseo-chain-spec-generator-{VERSION}")) ); - assert_eq!(chain_spec_generator.version().unwrap(), version); + assert_eq!(chain_spec_generator.version().unwrap(), VERSION); assert!(matches!( chain_spec_generator, Binary::Source { source: Source::GitHub(ReleaseArchive { tag, .. }), .. } - if *tag == Some(version.to_string()) + if *tag == Some(VERSION.to_string()) )); Ok(()) } @@ -1447,10 +1445,9 @@ chain = "paseo-local" chain = "paseo-local" "# )?; - let version = "v1.12.0"; - File::create(cache.join(format!("polkadot-{version}")))?; - File::create(cache.join(format!("polkadot-execute-worker-{version}")))?; - File::create(cache.join(format!("polkadot-prepare-worker-{version}")))?; + File::create(cache.join(format!("polkadot-{VERSION}")))?; + File::create(cache.join(format!("polkadot-execute-worker-{VERSION}")))?; + File::create(cache.join(format!("polkadot-prepare-worker-{VERSION}")))?; let mut zombienet = Zombienet::new( &cache, diff --git a/crates/pop-parachains/src/up/parachains.rs b/crates/pop-parachains/src/up/parachains.rs index 06eda16e6..ad6075454 100644 --- a/crates/pop-parachains/src/up/parachains.rs +++ b/crates/pop-parachains/src/up/parachains.rs @@ -22,7 +22,7 @@ pub(super) enum Parachain { Repository = "https://github.com/r0gue-io/polkadot", Binary = "polkadot-parachain", TagFormat = "polkadot-{tag}", - Fallback = "v1.12.0" + Fallback = "stable2409" ))] System, /// Pop Network makes it easy for smart contract developers to use the power of Polkadot. @@ -30,7 +30,7 @@ pub(super) enum Parachain { Repository = "https://github.com/r0gue-io/pop-node", Binary = "pop-node", Prerelease = "false", - Fallback = "testnet-v0.4.1" + Fallback = "testnet-v0.4.2" ))] Pop, } @@ -148,7 +148,7 @@ pub(super) async fn from( #[cfg(test)] mod tests { - use super::*; + use super::{super::tests::VERSION, *}; use std::path::PathBuf; use tempfile::tempdir; @@ -159,7 +159,7 @@ mod tests { "polkadot", None, None, - "v1.12.0", + VERSION, Some("asset-hub-paseo-local"), tempdir()?.path() ) @@ -170,13 +170,12 @@ mod tests { #[tokio::test] async fn system_using_relay_version() -> anyhow::Result<()> { - let version = "v1.12.0"; let expected = Parachain::System; let para_id = 1000; let temp_dir = tempdir()?; let parachain = - system(para_id, expected.binary(), None, None, version, None, temp_dir.path()) + system(para_id, expected.binary(), None, None, VERSION, None, temp_dir.path()) .await? .unwrap(); assert_eq!(para_id, parachain.id); @@ -184,7 +183,7 @@ mod tests { if name == expected.binary() && source == Source::GitHub(ReleaseArchive { owner: "r0gue-io".to_string(), repository: "polkadot".to_string(), - tag: Some(version.to_string()), + tag: Some(VERSION.to_string()), tag_format: Some("polkadot-{tag}".to_string()), archive: format!("{name}-{}.tar.gz", target()?), contents: vec![(expected.binary(), None)], @@ -196,13 +195,12 @@ mod tests { #[tokio::test] async fn system_works() -> anyhow::Result<()> { - let version = "v1.12.0"; let expected = Parachain::System; let para_id = 1000; let temp_dir = tempdir()?; let parachain = - system(para_id, expected.binary(), Some(version), None, version, None, temp_dir.path()) + system(para_id, expected.binary(), Some(VERSION), None, VERSION, None, temp_dir.path()) .await? .unwrap(); assert_eq!(para_id, parachain.id); @@ -210,7 +208,7 @@ mod tests { if name == expected.binary() && source == Source::GitHub(ReleaseArchive { owner: "r0gue-io".to_string(), repository: "polkadot".to_string(), - tag: Some(version.to_string()), + tag: Some(VERSION.to_string()), tag_format: Some("polkadot-{tag}".to_string()), archive: format!("{name}-{}.tar.gz", target()?), contents: vec![(expected.binary(), None)], @@ -223,7 +221,7 @@ mod tests { #[tokio::test] async fn system_with_chain_spec_generator_works() -> anyhow::Result<()> { let expected = Parachain::System; - let runtime_version = "v1.2.7"; + let runtime_version = "v1.3.3"; let para_id = 1000; let temp_dir = tempdir()?; diff --git a/crates/pop-parachains/src/up/relay.rs b/crates/pop-parachains/src/up/relay.rs index 662e639fd..2390e446a 100644 --- a/crates/pop-parachains/src/up/relay.rs +++ b/crates/pop-parachains/src/up/relay.rs @@ -24,7 +24,7 @@ pub(super) enum RelayChain { Repository = "https://github.com/r0gue-io/polkadot", Binary = "polkadot", TagFormat = "polkadot-{tag}", - Fallback = "v1.12.0" + Fallback = "stable2409" ))] Polkadot, } @@ -131,17 +131,18 @@ mod tests { use super::*; use tempfile::tempdir; + const VERSION: &str = "stable2409"; + #[tokio::test] async fn default_works() -> anyhow::Result<()> { let expected = RelayChain::Polkadot; - let version = "v1.12.0"; let temp_dir = tempdir()?; - let relay = default(Some(version), None, None, temp_dir.path()).await?; + let relay = default(Some(VERSION), None, None, temp_dir.path()).await?; assert!(matches!(relay.binary, Binary::Source { name, source, cache } if name == expected.binary() && source == Source::GitHub(ReleaseArchive { owner: "r0gue-io".to_string(), repository: "polkadot".to_string(), - tag: Some(version.to_string()), + tag: Some(VERSION.to_string()), tag_format: Some("polkadot-{tag}".to_string()), archive: format!("{name}-{}.tar.gz", target()?), contents: ["polkadot", "polkadot-execute-worker", "polkadot-prepare-worker"].map(|b| (b, None)).to_vec(), @@ -154,7 +155,7 @@ mod tests { #[tokio::test] async fn default_with_chain_spec_generator_works() -> anyhow::Result<()> { - let runtime_version = "v1.2.7"; + let runtime_version = "v1.3.3"; let temp_dir = tempdir()?; let relay = default(None, Some(runtime_version), Some("paseo-local"), temp_dir.path()).await?; @@ -186,15 +187,14 @@ mod tests { #[tokio::test] async fn from_handles_local_command() -> anyhow::Result<()> { let expected = RelayChain::Polkadot; - let version = "v1.12.0"; let temp_dir = tempdir()?; let relay = - from("./bin-v1.6.0/polkadot", Some(version), None, None, temp_dir.path()).await?; + from("./bin-stable2409/polkadot", Some(VERSION), None, None, temp_dir.path()).await?; assert!(matches!(relay.binary, Binary::Source { name, source, cache } if name == expected.binary() && source == Source::GitHub(ReleaseArchive { owner: "r0gue-io".to_string(), repository: "polkadot".to_string(), - tag: Some(version.to_string()), + tag: Some(VERSION.to_string()), tag_format: Some("polkadot-{tag}".to_string()), archive: format!("{name}-{}.tar.gz", target()?), contents: ["polkadot", "polkadot-execute-worker", "polkadot-prepare-worker"].map(|b| (b, None)).to_vec(), From 2a58bf4c66008368530d29fbeee679b966422d02 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Thu, 19 Dec 2024 10:29:29 +0100 Subject: [PATCH 004/117] chore: replace codeowners (#388) --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e3fe3ffae..a2715e0fc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ @AlexD10S -@brunopgalvao -@weezy20 \ No newline at end of file +@al3mart +@evilrobot-01 \ No newline at end of file From 661c6257c8c00f6cda5d1be236141573d7f0d737 Mon Sep 17 00:00:00 2001 From: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:18:19 +0000 Subject: [PATCH 005/117] build(deps): remove unused dependencies (#369) * build(deps): remove unused dependencies * refactor: remove unused import * refactor: silence warning as function will be used in the future * revert: revert removal of tokio-test as used by doc tests --- Cargo.lock | 27 ---------------------- Cargo.toml | 1 - crates/pop-cli/Cargo.toml | 1 - crates/pop-cli/src/cli.rs | 1 + crates/pop-contracts/Cargo.toml | 7 ++---- crates/pop-parachains/Cargo.toml | 5 +--- crates/pop-parachains/src/utils/helpers.rs | 1 - 7 files changed, 4 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac67e52da..48e49894a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3551,15 +3551,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - [[package]] name = "fnv" version = "1.0.7" @@ -6062,12 +6053,6 @@ dependencies = [ "nom", ] -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -9369,7 +9354,6 @@ dependencies = [ "pop-contracts", "pop-parachains", "pop-telemetry", - "predicates", "reqwest 0.12.9", "serde", "serde_json", @@ -9425,12 +9409,9 @@ dependencies = [ "contract-build", "contract-extrinsics", "contract-transcode", - "dirs", "duct", - "flate2", "heck 0.5.0", "ink_env", - "mockito", "pop-common", "reqwest 0.12.9", "scale-info", @@ -9440,7 +9421,6 @@ dependencies = [ "strum_macros 0.26.4", "subxt", "subxt-signer", - "tar", "tempfile", "thiserror 1.0.69", "tokio", @@ -9456,12 +9436,9 @@ dependencies = [ "askama", "clap", "duct", - "flate2", "glob", "indexmap 2.7.0", - "mockito", "pop-common", - "reqwest 0.12.9", "scale-info", "scale-value", "serde_json", @@ -9471,7 +9448,6 @@ dependencies = [ "subxt", "subxt-signer", "symlink", - "tar", "tempfile", "thiserror 1.0.69", "tokio", @@ -9527,10 +9503,7 @@ checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "difflib", - "float-cmp", - "normalize-line-endings", "predicates-core", - "regex", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 44dd1fbd3..8b284b10a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ git2 = { version = "0.18", features = ["vendored-openssl"] } glob = "0.3.1" log = "0.4.20" mockito = "1.4.0" -predicates = "3.1.0" tar = "0.4.40" tempfile = "3.10" thiserror = "1.0.58" diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 34e51111d..6093569dc 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -55,7 +55,6 @@ tower-http = { workspace = true, features = ["fs", "cors"] } [dev-dependencies] assert_cmd.workspace = true contract-extrinsics.workspace = true -predicates.workspace = true subxt.workspace = true subxt-signer.workspace = true sp-weights.workspace = true diff --git a/crates/pop-cli/src/cli.rs b/crates/pop-cli/src/cli.rs index f98c934e9..9d2b86ec7 100644 --- a/crates/pop-cli/src/cli.rs +++ b/crates/pop-cli/src/cli.rs @@ -304,6 +304,7 @@ pub(crate) mod tests { self } + #[allow(dead_code)] pub(crate) fn expect_success(mut self, message: impl Display) -> Self { self.success_expectations.push(message.to_string()); self diff --git a/crates/pop-contracts/Cargo.toml b/crates/pop-contracts/Cargo.toml index f44332299..cd880213c 100644 --- a/crates/pop-contracts/Cargo.toml +++ b/crates/pop-contracts/Cargo.toml @@ -13,9 +13,7 @@ version.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -flate2.workspace = true reqwest.workspace = true -tar.workspace = true tempfile.workspace = true thiserror.workspace = true tokio.workspace = true @@ -35,10 +33,9 @@ contract-build.workspace = true contract-extrinsics.workspace = true contract-transcode.workspace = true scale-info.workspace = true -# pop +# pop pop-common = { path = "../pop-common", version = "0.5.0" } [dev-dependencies] -dirs.workspace = true -mockito.workspace = true +# Used in doc tests. tokio-test.workspace = true \ No newline at end of file diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index 89c1df6d9..4b2ee4111 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -12,14 +12,12 @@ version.workspace = true anyhow.workspace = true clap.workspace = true duct.workspace = true -flate2.workspace = true glob.workspace = true serde_json.workspace = true strum.workspace = true strum_macros.workspace = true subxt-signer.workspace = true subxt.workspace = true -tar.workspace = true tempfile.workspace = true thiserror.workspace = true tokio.workspace = true @@ -27,7 +25,6 @@ url.workspace = true askama.workspace = true indexmap.workspace = true -reqwest.workspace = true scale-info.workspace = true scale-value.workspace = true sp-core.workspace = true @@ -41,5 +38,5 @@ zombienet-sdk.workspace = true pop-common = { path = "../pop-common", version = "0.5.0" } [dev-dependencies] -mockito.workspace = true +# Used in doc tests. tokio-test.workspace = true diff --git a/crates/pop-parachains/src/utils/helpers.rs b/crates/pop-parachains/src/utils/helpers.rs index fd8e91d68..f38d7bce5 100644 --- a/crates/pop-parachains/src/utils/helpers.rs +++ b/crates/pop-parachains/src/utils/helpers.rs @@ -88,7 +88,6 @@ mod tests { use super::*; use crate::generator::parachain::ChainSpec; use askama::Template; - use std::env::var; use tempfile::tempdir; #[test] From e96d8c7fd03d03c2a0935e7db51585a7242fd953 Mon Sep 17 00:00:00 2001 From: Peter White Date: Thu, 19 Dec 2024 10:35:15 -0700 Subject: [PATCH 006/117] chore(wallet): update frontend with papi support (#395) --- crates/pop-cli/src/assets/index.html | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/pop-cli/src/assets/index.html b/crates/pop-cli/src/assets/index.html index d1d5a8ef7..2b4430f8b 100644 --- a/crates/pop-cli/src/assets/index.html +++ b/crates/pop-cli/src/assets/index.html @@ -7,7 +7,7 @@ href="data:image/svg+xml,%3Csvg width='512' height='512' viewBox='0 0 512 512' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_873_174)'%3E%3Crect width='512' height='512' rx='256' fill='%231C0533'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint0_radial_873_174)' fill-opacity='0.8'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint1_radial_873_174)' fill-opacity='0.6'/%3E%3Cmask id='mask0_873_174' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='-429' y='-502' width='1428' height='1351'%3E%3Cpath d='M127.637 -501.289L998.911 322.903L984.661 336.383L113.388 -487.809L127.637 -501.289Z' fill='%23D9D9D9'/%3E%3Cpath d='M88.9594 -464.701L960.232 359.491L945.983 372.971L74.7096 -451.221L88.9594 -464.701Z' fill='%23D9D9D9'/%3E%3Cpath d='M50.2814 -428.113L921.554 396.079L907.305 409.559L36.0316 -414.633L50.2814 -428.113Z' fill='%23D9D9D9'/%3E%3Cpath d='M11.6034 -391.525L882.876 432.667L868.627 446.147L-2.64642 -378.045L11.6034 -391.525Z' fill='%23D9D9D9'/%3E%3Cpath d='M-27.0746 -354.937L844.198 469.255L829.949 482.735L-41.3244 -341.457L-27.0746 -354.937Z' fill='%23D9D9D9'/%3E%3Cpath d='M-65.7526 -318.349L805.52 505.843L791.271 519.323L-80.0024 -304.869L-65.7526 -318.349Z' fill='%23D9D9D9'/%3E%3Cpath d='M-104.431 -281.761L766.842 542.431L752.593 555.911L-118.68 -268.281L-104.431 -281.761Z' fill='%23D9D9D9'/%3E%3Cpath d='M-143.109 -245.173L728.164 579.019L713.915 592.499L-157.358 -231.693L-143.109 -245.173Z' fill='%23D9D9D9'/%3E%3Cpath d='M-181.787 -208.585L689.486 615.607L675.237 629.087L-196.036 -195.105L-181.787 -208.585Z' fill='%23D9D9D9'/%3E%3Cpath d='M-220.465 -171.997L650.808 652.195L636.559 665.675L-234.714 -158.517L-220.465 -171.997Z' fill='%23D9D9D9'/%3E%3Cpath d='M-259.143 -135.409L612.13 688.783L597.881 702.263L-273.392 -121.929L-259.143 -135.409Z' fill='%23D9D9D9'/%3E%3Cpath d='M-297.821 -98.8211L573.452 725.371L559.203 738.851L-312.07 -85.3413L-297.821 -98.8211Z' fill='%23D9D9D9'/%3E%3Cpath d='M-336.499 -62.2331L534.774 761.959L520.525 775.439L-350.748 -48.7533L-336.499 -62.2331Z' fill='%23D9D9D9'/%3E%3Cpath d='M-375.177 -25.6452L496.096 798.547L481.847 812.026L-389.426 -12.1654L-375.177 -25.6452Z' fill='%23D9D9D9'/%3E%3Cpath d='M-413.855 10.9428L457.418 835.135L443.169 848.615L-428.104 24.4225L-413.855 10.9428Z' fill='%23D9D9D9'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_873_174)'%3E%3Cpath d='M511.169 254.929C511.169 396.905 396.739 512 255.584 512C114.428 512 -0.000976562 396.905 -0.000976562 254.929C-0.000976562 112.953 114.428 -2.14209 255.584 -2.14209C396.739 -2.14209 511.169 112.953 511.169 254.929Z' fill='%23E6007A'/%3E%3C/g%3E%3Cpath d='M183.804 160.44C171.052 151.144 156.458 147.168 141.864 147.168H52.2334L5.63086 367.567H122.684L132.083 322.765C141.879 321.622 151.384 318.84 160.403 314.394C161.8 313.705 163.178 312.986 164.536 312.239C168.249 321.014 173.177 329.173 179.315 336.581C187.573 346.548 197.657 354.366 209.117 360.016L209.12 360.018L209.517 360.213L209.917 360.399C222.605 366.289 236.098 368.784 249.63 368.784C263.218 368.784 276.507 366.489 289.292 361.868C295.36 359.675 301.212 357.046 306.832 353.988L303.961 367.567H421.013L430.413 322.765C440.209 321.622 449.714 318.84 458.733 314.394C469.61 309.03 479.317 301.877 487.672 293.046L487.682 293.035L487.841 292.867L487.995 292.7L487.998 292.697C496.09 283.931 502.611 273.995 507.338 262.975C512.203 251.631 514.88 239.536 514.88 226.953C514.88 214.447 512.231 202.327 507.001 190.977C501.508 178.753 493.141 168.335 482.134 160.44C469.382 151.144 454.788 147.168 440.194 147.168H350.563L345.323 171.949C338.297 165.041 330.227 159.445 321.314 155.191C308.55 148.958 294.91 146.357 281.28 146.357C267.691 146.357 254.402 148.652 241.618 153.273C229.473 157.663 218.174 163.78 207.814 171.541C205.377 173.358 203.011 175.252 200.715 177.221C196.062 170.823 190.408 165.177 183.804 160.44Z' fill='%232B0532'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M229.569 331.163L229.632 331.194L229.696 331.224C237.592 334.889 246.407 336.65 256.019 336.65C265.979 336.65 275.609 334.978 284.877 331.628C294.055 328.31 302.528 323.689 310.277 317.771C317.967 311.898 324.68 305.15 330.397 297.531L330.413 297.509L330.429 297.487C336.137 289.737 340.585 281.451 343.748 272.638C347.074 263.611 348.753 254.388 348.753 244.999C348.753 236.541 347.45 228.398 344.825 220.6C342.311 212.598 338.452 205.423 333.24 199.132C327.963 192.763 321.484 187.769 313.875 184.16C306.078 180.325 297.295 178.491 287.67 178.491C277.709 178.491 268.079 180.164 258.812 183.513C249.647 186.826 241.125 191.437 233.261 197.332C225.542 203.084 218.751 209.844 212.899 217.597L212.889 217.611C207.16 225.246 202.642 233.54 199.345 242.478L199.33 242.518L199.316 242.559C196.135 251.575 194.532 260.778 194.532 270.142C194.532 278.624 195.843 286.844 198.472 294.776L198.477 294.792L198.482 294.808C201.135 302.663 205.057 309.744 210.248 316.009C215.517 322.37 221.981 327.422 229.569 331.163ZM287.732 276.015L287.742 276C290.74 271.657 292.939 267.045 294.364 262.148C295.831 257.108 296.54 252.276 296.54 247.635C296.54 243.3 295.843 239.458 294.528 236.043L294.487 235.934L294.449 235.824C293.328 232.545 291.583 230.131 289.252 228.349C287.226 226.801 284.199 225.736 279.606 225.736C274.271 225.736 269.81 226.979 266.027 229.305L265.974 229.337L265.921 229.368C261.869 231.746 258.495 234.905 255.765 238.909C252.893 243.123 250.673 247.698 249.104 252.654C247.65 257.67 246.947 262.48 246.947 267.101C246.947 271.459 247.585 275.422 248.809 279.033C250.055 282.339 251.89 284.885 254.293 286.836C256.34 288.379 259.301 289.405 263.68 289.405C269.175 289.405 273.697 288.15 277.46 285.837C281.567 283.313 284.975 280.058 287.722 276.029L287.732 276.015Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M171.542 186.53C164.879 181.64 157.017 179.302 148.254 179.302H84.4909L51.4775 335.434H103.163L112.436 291.23H128.094C136.792 291.23 145.029 289.337 152.73 285.54C160.225 281.844 166.856 276.954 172.594 270.888L172.619 270.861L172.644 270.834C178.339 264.665 182.824 257.798 186.062 250.247C189.323 242.645 190.992 234.866 190.992 226.953C190.992 219.052 189.327 211.496 186.009 204.348C182.762 197.062 177.927 191.083 171.542 186.53ZM135.575 242.131L135.598 242.101C136.363 241.1 137.06 239.71 137.567 237.773L137.604 237.631L137.648 237.49C138.222 235.663 138.577 233.269 138.577 230.197C138.577 227.864 138.266 226.771 138.111 226.428C138.024 226.235 137.948 226.093 137.889 225.994L137.862 225.949L137.849 225.939H126.349L122.444 244.594H132.693C132.756 244.581 132.999 244.516 133.45 244.244C133.961 243.935 134.685 243.325 135.553 242.16L135.575 242.131Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M469.872 186.53C463.209 181.64 455.347 179.302 446.583 179.302H382.821L349.807 335.434H401.493L410.766 291.23H426.424C435.122 291.23 443.359 289.337 451.06 285.54C458.555 281.844 465.186 276.954 470.923 270.888L470.949 270.861L470.973 270.834C476.669 264.665 481.153 257.798 484.392 250.247C487.652 242.645 489.322 234.866 489.322 226.953C489.322 219.052 487.657 211.496 484.339 204.348C481.092 197.062 476.257 191.083 469.872 186.53ZM433.905 242.131L433.928 242.101C434.693 241.1 435.39 239.71 435.896 237.773L435.934 237.631L435.978 237.49C436.552 235.663 436.907 233.269 436.907 230.197C436.907 227.864 436.596 226.771 436.441 226.428C436.354 226.235 436.278 226.093 436.219 225.994L436.192 225.949L436.178 225.939H424.678L420.774 244.594H431.023C431.086 244.581 431.329 244.516 431.779 244.244C432.29 243.935 433.014 243.325 433.883 242.16L433.905 242.131Z' fill='%23E6007A'/%3E%3Cmask id='path-6-outside-1_873_174' maskUnits='userSpaceOnUse' x='35.8232' y='166.788' width='435' height='156' fill='black'%3E%3Crect fill='white' x='35.8232' y='166.788' width='435' height='156'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z'/%3E%3C/mask%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' stroke='white' stroke-width='0.3' mask='url(%23path-6-outside-1_873_174)'/%3E%3C/g%3E%3Cdefs%3E%3CradialGradient id='paint0_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(-62.8 -86.4) rotate(70.436) scale(686.854 394.059)'%3E%3Cstop stop-color='%236335EC'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='paint1_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(640.4 677.6) rotate(-116.448) scale(1055.25 662.094)'%3E%3Cstop stop-color='%23E6007A'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='clip0_873_174'%3E%3Crect width='512' height='512' rx='256' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A" /> Pop CLI Signing Portal - + `],K);d$=function(e,t,n,r){var o,i=arguments.length,s=i<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(i<3?o(s):i>3?o(t,n,s):o(t,n))||s);return i>3&&s&&Object.defineProperty(t,n,s),s}([RA("dc-connection-button")],d$);const f$={chains:{},wallets:[new class extends QE{constructor(e){super(),X(this,"options"),this.options=e}async getWallets(){return await lS,(()=>{const{injectedWeb3:e}=window;return e?Object.keys(e):[]})().map((e=>new aS(e,this.options)))}}]};function p$(e,t,{checkForDefaultPrevented:n=!0}={}){return function(r){if(null==e||e(r),!1===n||!r.defaultPrevented)return null==t?void 0:t(r)}}function h$(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function v$(...e){return t=>{let n=!1;const r=e.map((e=>{const r=h$(e,t);return n||"function"!=typeof r||(n=!0),r}));if(n)return()=>{for(let t=0;t{const t=n.map((e=>qe.createContext(e)));return function(n){const r=(null==n?void 0:n[e])||t;return qe.useMemo((()=>({[`__scope${e}`]:{...n,[e]:r}})),[n,r])}};return r.scopeName=e,[function(t,r){const o=qe.createContext(r),i=n.length;n=[...n,r];const s=t=>{var n;const{scope:r,children:s,...a}=t,l=(null==(n=null==r?void 0:r[e])?void 0:n[i])||o,c=qe.useMemo((()=>a),Object.values(a));return nt.jsx(l.Provider,{value:c,children:s})};return s.displayName=t+"Provider",[s,function(n,s){var a;const l=(null==(a=null==s?void 0:s[e])?void 0:a[i])||o,c=qe.useContext(l);if(c)return c;if(void 0!==r)return r;throw new Error(`\`${n}\` must be used within \`${t}\``)}]},y$(r,...t)]}function y$(...e){const t=e[0];if(1===e.length)return t;const n=()=>{const n=e.map((e=>({useScope:e(),scopeName:e.scopeName})));return function(e){const r=n.reduce(((t,{useScope:n,scopeName:r})=>({...t,...n(e)[`__scope${r}`]})),{});return qe.useMemo((()=>({[`__scope${t.scopeName}`]:r})),[r])}};return n.scopeName=t.scopeName,n}function b$(e){const t=qe.useRef(e);return qe.useEffect((()=>{t.current=e})),qe.useMemo((()=>(...e)=>{var n;return null==(n=t.current)?void 0:n.call(t,...e)}),[])}function w$({prop:e,defaultProp:t,onChange:n=()=>{}}){const[r,o]=function({defaultProp:e,onChange:t}){const n=qe.useState(e),[r]=n,o=qe.useRef(r),i=b$(t);return qe.useEffect((()=>{o.current!==r&&(i(r),o.current=r)}),[r,o,i]),n}({defaultProp:t,onChange:n}),i=void 0!==e,s=i?e:r,a=b$(n);return[s,qe.useCallback((t=>{if(i){const n="function"==typeof t?t(e):t;n!==e&&a(n)}else o(t)}),[i,e,o,a])]}!function(e){_A.next(e.wallets)}({wallets:f$.wallets});var x$=qe.forwardRef(((e,t)=>{const{children:n,...r}=e,o=qe.Children.toArray(n),i=o.find(S$);if(i){const e=i.props.children,n=o.map((t=>t===i?qe.Children.count(e)>1?qe.Children.only(null):qe.isValidElement(e)?e.props.children:null:t));return nt.jsx(k$,{...r,ref:t,children:qe.isValidElement(e)?qe.cloneElement(e,void 0,n):null})}return nt.jsx(k$,{...r,ref:t,children:n})}));x$.displayName="Slot";var k$=qe.forwardRef(((e,t)=>{const{children:n,...r}=e;if(qe.isValidElement(n)){const e=function(e){var t,n;let r=null==(t=Object.getOwnPropertyDescriptor(e.props,"ref"))?void 0:t.get,o=r&&"isReactWarning"in r&&r.isReactWarning;if(o)return e.ref;if(r=null==(n=Object.getOwnPropertyDescriptor(e,"ref"))?void 0:n.get,o=r&&"isReactWarning"in r&&r.isReactWarning,o)return e.props.ref;return e.props.ref||e.ref}(n);return qe.cloneElement(n,{...C$(r,n.props),ref:t?v$(t,e):e})}return qe.Children.count(n)>1?qe.Children.only(null):null}));k$.displayName="SlotClone";var E$=({children:e})=>nt.jsx(nt.Fragment,{children:e});function S$(e){return qe.isValidElement(e)&&e.type===E$}function C$(e,t){const n={...t};for(const r in t){const o=e[r],i=t[r];/^on[A-Z]/.test(r)?o&&i?n[r]=(...e)=>{i(...e),o(...e)}:o&&(n[r]=o):"style"===r?n[r]={...o,...i}:"className"===r&&(n[r]=[o,i].filter(Boolean).join(" "))}return{...e,...n}}var _$=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","span","svg","ul"].reduce(((e,t)=>{const n=qe.forwardRef(((e,n)=>{const{asChild:r,...o}=e,i=r?x$:t;return"undefined"!=typeof window&&(window[Symbol.for("radix-ui")]=!0),nt.jsx(i,{...o,ref:n})}));return n.displayName=`Primitive.${t}`,{...e,[t]:n}}),{});function A$(e,t){e&&df.flushSync((()=>e.dispatchEvent(t)))}function N$(e){const t=e+"CollectionProvider",[n,r]=g$(t),[o,i]=n(t,{collectionRef:{current:null},itemMap:new Map}),s=e=>{const{scope:t,children:n}=e,r=Ke.useRef(null),i=Ke.useRef(new Map).current;return nt.jsx(o,{scope:t,itemMap:i,collectionRef:r,children:n})};s.displayName=t;const a=e+"CollectionSlot",l=Ke.forwardRef(((e,t)=>{const{scope:n,children:r}=e,o=m$(t,i(a,n).collectionRef);return nt.jsx(x$,{ref:o,children:r})}));l.displayName=a;const c=e+"CollectionItemSlot",u="data-radix-collection-item",d=Ke.forwardRef(((e,t)=>{const{scope:n,children:r,...o}=e,s=Ke.useRef(null),a=m$(t,s),l=i(c,n);return Ke.useEffect((()=>(l.itemMap.set(s,{ref:s,...o}),()=>{l.itemMap.delete(s)}))),nt.jsx(x$,{[u]:"",ref:a,children:r})}));return d.displayName=c,[{Provider:s,Slot:l,ItemSlot:d},function(t){const n=i(e+"CollectionConsumer",t),r=Ke.useCallback((()=>{const e=n.collectionRef.current;if(!e)return[];const t=Array.from(e.querySelectorAll(`[${u}]`)),r=Array.from(n.itemMap.values()).sort(((e,n)=>t.indexOf(e.ref.current)-t.indexOf(n.ref.current)));return r}),[n.collectionRef,n.itemMap]);return r},r]}var P$=qe.createContext(void 0);function $$(e){const t=qe.useContext(P$);return e||t||"ltr"}var j$,O$="dismissableLayer.update",R$="dismissableLayer.pointerDownOutside",M$="dismissableLayer.focusOutside",T$=qe.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),L$=qe.forwardRef(((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:o,onFocusOutside:i,onInteractOutside:s,onDismiss:a,...l}=e,c=qe.useContext(T$),[u,d]=qe.useState(null),f=(null==u?void 0:u.ownerDocument)??(null==globalThis?void 0:globalThis.document),[,p]=qe.useState({}),h=m$(t,(e=>d(e))),v=Array.from(c.layers),[m]=[...c.layersWithOutsidePointerEventsDisabled].slice(-1),g=v.indexOf(m),y=u?v.indexOf(u):-1,b=c.layersWithOutsidePointerEventsDisabled.size>0,w=y>=g,x=function(e,t=(null==globalThis?void 0:globalThis.document)){const n=b$(e),r=qe.useRef(!1),o=qe.useRef((()=>{}));return qe.useEffect((()=>{const e=e=>{if(e.target&&!r.current){let r=function(){z$(R$,n,i,{discrete:!0})};const i={originalEvent:e};"touch"===e.pointerType?(t.removeEventListener("click",o.current),o.current=r,t.addEventListener("click",o.current,{once:!0})):r()}else t.removeEventListener("click",o.current);r.current=!1},i=window.setTimeout((()=>{t.addEventListener("pointerdown",e)}),0);return()=>{window.clearTimeout(i),t.removeEventListener("pointerdown",e),t.removeEventListener("click",o.current)}}),[t,n]),{onPointerDownCapture:()=>r.current=!0}}((e=>{const t=e.target,n=[...c.branches].some((e=>e.contains(t)));w&&!n&&(null==o||o(e),null==s||s(e),e.defaultPrevented||null==a||a())}),f),k=function(e,t=(null==globalThis?void 0:globalThis.document)){const n=b$(e),r=qe.useRef(!1);return qe.useEffect((()=>{const e=e=>{if(e.target&&!r.current){z$(M$,n,{originalEvent:e},{discrete:!1})}};return t.addEventListener("focusin",e),()=>t.removeEventListener("focusin",e)}),[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}((e=>{const t=e.target;[...c.branches].some((e=>e.contains(t)))||(null==i||i(e),null==s||s(e),e.defaultPrevented||null==a||a())}),f);return function(e,t=(null==globalThis?void 0:globalThis.document)){const n=b$(e);qe.useEffect((()=>{const e=e=>{"Escape"===e.key&&n(e)};return t.addEventListener("keydown",e,{capture:!0}),()=>t.removeEventListener("keydown",e,{capture:!0})}),[n,t])}((e=>{y===c.layers.size-1&&(null==r||r(e),!e.defaultPrevented&&a&&(e.preventDefault(),a()))}),f),qe.useEffect((()=>{if(u)return n&&(0===c.layersWithOutsidePointerEventsDisabled.size&&(j$=f.body.style.pointerEvents,f.body.style.pointerEvents="none"),c.layersWithOutsidePointerEventsDisabled.add(u)),c.layers.add(u),I$(),()=>{n&&1===c.layersWithOutsidePointerEventsDisabled.size&&(f.body.style.pointerEvents=j$)}}),[u,f,n,c]),qe.useEffect((()=>()=>{u&&(c.layers.delete(u),c.layersWithOutsidePointerEventsDisabled.delete(u),I$())}),[u,c]),qe.useEffect((()=>{const e=()=>p({});return document.addEventListener(O$,e),()=>document.removeEventListener(O$,e)}),[]),nt.jsx(_$.div,{...l,ref:h,style:{pointerEvents:b?w?"auto":"none":void 0,...e.style},onFocusCapture:p$(e.onFocusCapture,k.onFocusCapture),onBlurCapture:p$(e.onBlurCapture,k.onBlurCapture),onPointerDownCapture:p$(e.onPointerDownCapture,x.onPointerDownCapture)})}));L$.displayName="DismissableLayer";function I$(){const e=new CustomEvent(O$);document.dispatchEvent(e)}function z$(e,t,n,{discrete:r}){const o=n.originalEvent.target,i=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&o.addEventListener(e,t,{once:!0}),r?A$(o,i):o.dispatchEvent(i)}qe.forwardRef(((e,t)=>{const n=qe.useContext(T$),r=qe.useRef(null),o=m$(t,r);return qe.useEffect((()=>{const e=r.current;if(e)return n.branches.add(e),()=>{n.branches.delete(e)}}),[n.branches]),nt.jsx(_$.div,{...e,ref:o})})).displayName="DismissableLayerBranch";var D$=0;function B$(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var U$="focusScope.autoFocusOnMount",F$="focusScope.autoFocusOnUnmount",H$={bubbles:!1,cancelable:!0},V$=qe.forwardRef(((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:i,...s}=e,[a,l]=qe.useState(null),c=b$(o),u=b$(i),d=qe.useRef(null),f=m$(t,(e=>l(e))),p=qe.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;qe.useEffect((()=>{if(r){let e=function(e){if(p.paused||!a)return;const t=e.target;a.contains(t)?d.current=t:Q$(d.current,{select:!0})},t=function(e){if(p.paused||!a)return;const t=e.relatedTarget;null!==t&&(a.contains(t)||Q$(d.current,{select:!0}))},n=function(e){if(document.activeElement===document.body)for(const t of e)t.removedNodes.length>0&&Q$(a)};document.addEventListener("focusin",e),document.addEventListener("focusout",t);const r=new MutationObserver(n);return a&&r.observe(a,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",e),document.removeEventListener("focusout",t),r.disconnect()}}}),[r,a,p.paused]),qe.useEffect((()=>{if(a){Y$.add(p);const t=document.activeElement;if(!a.contains(t)){const n=new CustomEvent(U$,H$);a.addEventListener(U$,c),a.dispatchEvent(n),n.defaultPrevented||(!function(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(Q$(r,{select:t}),document.activeElement!==n)return}((e=W$(a),e.filter((e=>"A"!==e.tagName))),{select:!0}),document.activeElement===t&&Q$(a))}return()=>{a.removeEventListener(U$,c),setTimeout((()=>{const e=new CustomEvent(F$,H$);a.addEventListener(F$,u),a.dispatchEvent(e),e.defaultPrevented||Q$(t??document.body,{select:!0}),a.removeEventListener(F$,u),Y$.remove(p)}),0)}}var e}),[a,c,u,p]);const h=qe.useCallback((e=>{if(!n&&!r)return;if(p.paused)return;const t="Tab"===e.key&&!e.altKey&&!e.ctrlKey&&!e.metaKey,o=document.activeElement;if(t&&o){const t=e.currentTarget,[r,i]=function(e){const t=W$(e),n=q$(t,e),r=q$(t.reverse(),e);return[n,r]}(t);r&&i?e.shiftKey||o!==i?e.shiftKey&&o===r&&(e.preventDefault(),n&&Q$(i,{select:!0})):(e.preventDefault(),n&&Q$(r,{select:!0})):o===t&&e.preventDefault()}}),[n,r,p.paused]);return nt.jsx(_$.div,{tabIndex:-1,...s,ref:f,onKeyDown:h})}));function W$(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:e=>{const t="INPUT"===e.tagName&&"hidden"===e.type;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function q$(e,t){for(const n of e)if(!K$(n,{upTo:t}))return n}function K$(e,{upTo:t}){if("hidden"===getComputedStyle(e).visibility)return!0;for(;e;){if(void 0!==t&&e===t)return!1;if("none"===getComputedStyle(e).display)return!0;e=e.parentElement}return!1}function Q$(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&function(e){return e instanceof HTMLInputElement&&"select"in e}(e)&&t&&e.select()}}V$.displayName="FocusScope";var Y$=function(){let e=[];return{add(t){const n=e[0];t!==n&&(null==n||n.pause()),e=G$(e,t),e.unshift(t)},remove(t){var n;e=G$(e,t),null==(n=e[0])||n.resume()}}}();function G$(e,t){const n=[...e],r=n.indexOf(t);return-1!==r&&n.splice(r,1),n}var J$=Boolean(null==globalThis?void 0:globalThis.document)?qe.useLayoutEffect:()=>{},X$=Qe["useId".toString()]||(()=>{}),Z$=0;function ej(e){const[t,n]=qe.useState(X$());return J$((()=>{n((e=>e??String(Z$++)))}),[e]),t?`radix-${t}`:""}const tj=["top","right","bottom","left"],nj=Math.min,rj=Math.max,oj=Math.round,ij=Math.floor,sj=e=>({x:e,y:e}),aj={left:"right",right:"left",bottom:"top",top:"bottom"},lj={start:"end",end:"start"};function cj(e,t,n){return rj(e,nj(t,n))}function uj(e,t){return"function"==typeof e?e(t):e}function dj(e){return e.split("-")[0]}function fj(e){return e.split("-")[1]}function pj(e){return"x"===e?"y":"x"}function hj(e){return"y"===e?"height":"width"}function vj(e){return["top","bottom"].includes(dj(e))?"y":"x"}function mj(e){return pj(vj(e))}function gj(e){return e.replace(/start|end/g,(e=>lj[e]))}function yj(e){return e.replace(/left|right|bottom|top/g,(e=>aj[e]))}function bj(e){return"number"!=typeof e?function(e){return{top:0,right:0,bottom:0,left:0,...e}}(e):{top:e,right:e,bottom:e,left:e}}function wj(e){const{x:t,y:n,width:r,height:o}=e;return{width:r,height:o,top:n,left:t,right:t+r,bottom:n+o,x:t,y:n}}function xj(e,t,n){let{reference:r,floating:o}=e;const i=vj(t),s=mj(t),a=hj(s),l=dj(t),c="y"===i,u=r.x+r.width/2-o.width/2,d=r.y+r.height/2-o.height/2,f=r[a]/2-o[a]/2;let p;switch(l){case"top":p={x:u,y:r.y-o.height};break;case"bottom":p={x:u,y:r.y+r.height};break;case"right":p={x:r.x+r.width,y:d};break;case"left":p={x:r.x-o.width,y:d};break;default:p={x:r.x,y:r.y}}switch(fj(t)){case"start":p[s]-=f*(n&&c?-1:1);break;case"end":p[s]+=f*(n&&c?-1:1)}return p}async function kj(e,t){var n;void 0===t&&(t={});const{x:r,y:o,platform:i,rects:s,elements:a,strategy:l}=e,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:d="floating",altBoundary:f=!1,padding:p=0}=uj(t,e),h=bj(p),v=a[f?"floating"===d?"reference":"floating":d],m=wj(await i.getClippingRect({element:null==(n=await(null==i.isElement?void 0:i.isElement(v)))||n?v:v.contextElement||await(null==i.getDocumentElement?void 0:i.getDocumentElement(a.floating)),boundary:c,rootBoundary:u,strategy:l})),g="floating"===d?{x:r,y:o,width:s.floating.width,height:s.floating.height}:s.reference,y=await(null==i.getOffsetParent?void 0:i.getOffsetParent(a.floating)),b=await(null==i.isElement?void 0:i.isElement(y))&&await(null==i.getScale?void 0:i.getScale(y))||{x:1,y:1},w=wj(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:a,rect:g,offsetParent:y,strategy:l}):g);return{top:(m.top-w.top+h.top)/b.y,bottom:(w.bottom-m.bottom+h.bottom)/b.y,left:(m.left-w.left+h.left)/b.x,right:(w.right-m.right+h.right)/b.x}}function Ej(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function Sj(e){return tj.some((t=>e[t]>=0))}function Cj(){return"undefined"!=typeof window}function _j(e){return Pj(e)?(e.nodeName||"").toLowerCase():"#document"}function Aj(e){var t;return(null==e||null==(t=e.ownerDocument)?void 0:t.defaultView)||window}function Nj(e){var t;return null==(t=(Pj(e)?e.ownerDocument:e.document)||window.document)?void 0:t.documentElement}function Pj(e){return!!Cj()&&(e instanceof Node||e instanceof Aj(e).Node)}function $j(e){return!!Cj()&&(e instanceof Element||e instanceof Aj(e).Element)}function jj(e){return!!Cj()&&(e instanceof HTMLElement||e instanceof Aj(e).HTMLElement)}function Oj(e){return!(!Cj()||"undefined"==typeof ShadowRoot)&&(e instanceof ShadowRoot||e instanceof Aj(e).ShadowRoot)}function Rj(e){const{overflow:t,overflowX:n,overflowY:r,display:o}=Dj(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&!["inline","contents"].includes(o)}function Mj(e){return["table","td","th"].includes(_j(e))}function Tj(e){return[":popover-open",":modal"].some((t=>{try{return e.matches(t)}catch(n){return!1}}))}function Lj(e){const t=Ij(),n=$j(e)?Dj(e):e;return"none"!==n.transform||"none"!==n.perspective||!!n.containerType&&"normal"!==n.containerType||!t&&!!n.backdropFilter&&"none"!==n.backdropFilter||!t&&!!n.filter&&"none"!==n.filter||["transform","perspective","filter"].some((e=>(n.willChange||"").includes(e)))||["paint","layout","strict","content"].some((e=>(n.contain||"").includes(e)))}function Ij(){return!("undefined"==typeof CSS||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function zj(e){return["html","body","#document"].includes(_j(e))}function Dj(e){return Aj(e).getComputedStyle(e)}function Bj(e){return $j(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function Uj(e){if("html"===_j(e))return e;const t=e.assignedSlot||e.parentNode||Oj(e)&&e.host||Nj(e);return Oj(t)?t.host:t}function Fj(e){const t=Uj(e);return zj(t)?e.ownerDocument?e.ownerDocument.body:e.body:jj(t)&&Rj(t)?t:Fj(t)}function Hj(e,t,n){var r;void 0===t&&(t=[]),void 0===n&&(n=!0);const o=Fj(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),s=Aj(o);if(i){const e=Vj(s);return t.concat(s,s.visualViewport||[],Rj(o)?o:[],e&&n?Hj(e):[])}return t.concat(o,Hj(o,[],n))}function Vj(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function Wj(e){const t=Dj(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const o=jj(e),i=o?e.offsetWidth:n,s=o?e.offsetHeight:r,a=oj(n)!==i||oj(r)!==s;return a&&(n=i,r=s),{width:n,height:r,$:a}}function qj(e){return $j(e)?e:e.contextElement}function Kj(e){const t=qj(e);if(!jj(t))return sj(1);const n=t.getBoundingClientRect(),{width:r,height:o,$:i}=Wj(t);let s=(i?oj(n.width):n.width)/r,a=(i?oj(n.height):n.height)/o;return s&&Number.isFinite(s)||(s=1),a&&Number.isFinite(a)||(a=1),{x:s,y:a}}const Qj=sj(0);function Yj(e){const t=Aj(e);return Ij()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:Qj}function Gj(e,t,n,r){void 0===t&&(t=!1),void 0===n&&(n=!1);const o=e.getBoundingClientRect(),i=qj(e);let s=sj(1);t&&(r?$j(r)&&(s=Kj(r)):s=Kj(e));const a=function(e,t,n){return void 0===t&&(t=!1),!(!n||t&&n!==Aj(e))&&t}(i,n,r)?Yj(i):sj(0);let l=(o.left+a.x)/s.x,c=(o.top+a.y)/s.y,u=o.width/s.x,d=o.height/s.y;if(i){const e=Aj(i),t=r&&$j(r)?Aj(r):r;let n=e,o=Vj(n);for(;o&&r&&t!==n;){const e=Kj(o),t=o.getBoundingClientRect(),r=Dj(o),i=t.left+(o.clientLeft+parseFloat(r.paddingLeft))*e.x,s=t.top+(o.clientTop+parseFloat(r.paddingTop))*e.y;l*=e.x,c*=e.y,u*=e.x,d*=e.y,l+=i,c+=s,n=Aj(o),o=Vj(n)}}return wj({width:u,height:d,x:l,y:c})}function Jj(e,t){const n=Bj(e).scrollLeft;return t?t.left+n:Gj(Nj(e)).left+n}function Xj(e,t,n){void 0===n&&(n=!1);const r=e.getBoundingClientRect();return{x:r.left+t.scrollLeft-(n?0:Jj(e,r)),y:r.top+t.scrollTop}}function Zj(e,t,n){let r;if("viewport"===t)r=function(e,t){const n=Aj(e),r=Nj(e),o=n.visualViewport;let i=r.clientWidth,s=r.clientHeight,a=0,l=0;if(o){i=o.width,s=o.height;const e=Ij();(!e||e&&"fixed"===t)&&(a=o.offsetLeft,l=o.offsetTop)}return{width:i,height:s,x:a,y:l}}(e,n);else if("document"===t)r=function(e){const t=Nj(e),n=Bj(e),r=e.ownerDocument.body,o=rj(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),i=rj(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight);let s=-n.scrollLeft+Jj(e);const a=-n.scrollTop;return"rtl"===Dj(r).direction&&(s+=rj(t.clientWidth,r.clientWidth)-o),{width:o,height:i,x:s,y:a}}(Nj(e));else if($j(t))r=function(e,t){const n=Gj(e,!0,"fixed"===t),r=n.top+e.clientTop,o=n.left+e.clientLeft,i=jj(e)?Kj(e):sj(1);return{width:e.clientWidth*i.x,height:e.clientHeight*i.y,x:o*i.x,y:r*i.y}}(t,n);else{const n=Yj(e);r={x:t.x-n.x,y:t.y-n.y,width:t.width,height:t.height}}return wj(r)}function eO(e,t){const n=Uj(e);return!(n===t||!$j(n)||zj(n))&&("fixed"===Dj(n).position||eO(n,t))}function tO(e,t,n){const r=jj(t),o=Nj(t),i="fixed"===n,s=Gj(e,!0,i,t);let a={scrollLeft:0,scrollTop:0};const l=sj(0);if(r||!r&&!i)if(("body"!==_j(t)||Rj(o))&&(a=Bj(t)),r){const e=Gj(t,!0,i,t);l.x=e.x+t.clientLeft,l.y=e.y+t.clientTop}else o&&(l.x=Jj(o));const c=!o||r||i?sj(0):Xj(o,a);return{x:s.left+a.scrollLeft-l.x-c.x,y:s.top+a.scrollTop-l.y-c.y,width:s.width,height:s.height}}function nO(e){return"static"===Dj(e).position}function rO(e,t){if(!jj(e)||"fixed"===Dj(e).position)return null;if(t)return t(e);let n=e.offsetParent;return Nj(e)===n&&(n=n.ownerDocument.body),n}function oO(e,t){const n=Aj(e);if(Tj(e))return n;if(!jj(e)){let t=Uj(e);for(;t&&!zj(t);){if($j(t)&&!nO(t))return t;t=Uj(t)}return n}let r=rO(e,t);for(;r&&Mj(r)&&nO(r);)r=rO(r,t);return r&&zj(r)&&nO(r)&&!Lj(r)?n:r||function(e){let t=Uj(e);for(;jj(t)&&!zj(t);){if(Lj(t))return t;if(Tj(t))return null;t=Uj(t)}return null}(e)||n}const iO={convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{elements:t,rect:n,offsetParent:r,strategy:o}=e;const i="fixed"===o,s=Nj(r),a=!!t&&Tj(t.floating);if(r===s||a&&i)return n;let l={scrollLeft:0,scrollTop:0},c=sj(1);const u=sj(0),d=jj(r);if((d||!d&&!i)&&(("body"!==_j(r)||Rj(s))&&(l=Bj(r)),jj(r))){const e=Gj(r);c=Kj(r),u.x=e.x+r.clientLeft,u.y=e.y+r.clientTop}const f=!s||d||i?sj(0):Xj(s,l,!0);return{width:n.width*c.x,height:n.height*c.y,x:n.x*c.x-l.scrollLeft*c.x+u.x+f.x,y:n.y*c.y-l.scrollTop*c.y+u.y+f.y}},getDocumentElement:Nj,getClippingRect:function(e){let{element:t,boundary:n,rootBoundary:r,strategy:o}=e;const i=[..."clippingAncestors"===n?Tj(t)?[]:function(e,t){const n=t.get(e);if(n)return n;let r=Hj(e,[],!1).filter((e=>$j(e)&&"body"!==_j(e))),o=null;const i="fixed"===Dj(e).position;let s=i?Uj(e):e;for(;$j(s)&&!zj(s);){const t=Dj(s),n=Lj(s);n||"fixed"!==t.position||(o=null),(i?!n&&!o:!n&&"static"===t.position&&o&&["absolute","fixed"].includes(o.position)||Rj(s)&&!n&&eO(e,s))?r=r.filter((e=>e!==s)):o=t,s=Uj(s)}return t.set(e,r),r}(t,this._c):[].concat(n),r],s=i[0],a=i.reduce(((e,n)=>{const r=Zj(t,n,o);return e.top=rj(r.top,e.top),e.right=nj(r.right,e.right),e.bottom=nj(r.bottom,e.bottom),e.left=rj(r.left,e.left),e}),Zj(t,s,o));return{width:a.right-a.left,height:a.bottom-a.top,x:a.left,y:a.top}},getOffsetParent:oO,getElementRects:async function(e){const t=this.getOffsetParent||oO,n=this.getDimensions,r=await n(e.floating);return{reference:tO(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}},getClientRects:function(e){return Array.from(e.getClientRects())},getDimensions:function(e){const{width:t,height:n}=Wj(e);return{width:t,height:n}},getScale:Kj,isElement:$j,isRTL:function(e){return"rtl"===Dj(e).direction}};function sO(e,t,n,r){void 0===r&&(r={});const{ancestorScroll:o=!0,ancestorResize:i=!0,elementResize:s="function"==typeof ResizeObserver,layoutShift:a="function"==typeof IntersectionObserver,animationFrame:l=!1}=r,c=qj(e),u=o||i?[...c?Hj(c):[],...Hj(t)]:[];u.forEach((e=>{o&&e.addEventListener("scroll",n,{passive:!0}),i&&e.addEventListener("resize",n)}));const d=c&&a?function(e,t){let n,r=null;const o=Nj(e);function i(){var e;clearTimeout(n),null==(e=r)||e.disconnect(),r=null}return function s(a,l){void 0===a&&(a=!1),void 0===l&&(l=1),i();const{left:c,top:u,width:d,height:f}=e.getBoundingClientRect();if(a||t(),!d||!f)return;const p={rootMargin:-ij(u)+"px "+-ij(o.clientWidth-(c+d))+"px "+-ij(o.clientHeight-(u+f))+"px "+-ij(c)+"px",threshold:rj(0,nj(1,l))||1};let h=!0;function v(e){const t=e[0].intersectionRatio;if(t!==l){if(!h)return s();t?s(!1,t):n=setTimeout((()=>{s(!1,1e-7)}),1e3)}h=!1}try{r=new IntersectionObserver(v,{...p,root:o.ownerDocument})}catch(m){r=new IntersectionObserver(v,p)}r.observe(e)}(!0),i}(c,n):null;let f,p=-1,h=null;s&&(h=new ResizeObserver((e=>{let[r]=e;r&&r.target===c&&h&&(h.unobserve(t),cancelAnimationFrame(p),p=requestAnimationFrame((()=>{var e;null==(e=h)||e.observe(t)}))),n()})),c&&!l&&h.observe(c),h.observe(t));let v=l?Gj(e):null;return l&&function t(){const r=Gj(e);!v||r.x===v.x&&r.y===v.y&&r.width===v.width&&r.height===v.height||n();v=r,f=requestAnimationFrame(t)}(),n(),()=>{var e;u.forEach((e=>{o&&e.removeEventListener("scroll",n),i&&e.removeEventListener("resize",n)})),null==d||d(),null==(e=h)||e.disconnect(),h=null,l&&cancelAnimationFrame(f)}}const aO=function(e){return void 0===e&&(e=0),{name:"offset",options:e,async fn(t){var n,r;const{x:o,y:i,placement:s,middlewareData:a}=t,l=await async function(e,t){const{placement:n,platform:r,elements:o}=e,i=await(null==r.isRTL?void 0:r.isRTL(o.floating)),s=dj(n),a=fj(n),l="y"===vj(n),c=["left","top"].includes(s)?-1:1,u=i&&l?-1:1,d=uj(t,e);let{mainAxis:f,crossAxis:p,alignmentAxis:h}="number"==typeof d?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return a&&"number"==typeof h&&(p="end"===a?-1*h:h),l?{x:p*u,y:f*c}:{x:f*c,y:p*u}}(t,e);return s===(null==(n=a.offset)?void 0:n.placement)&&null!=(r=a.arrow)&&r.alignmentOffset?{}:{x:o+l.x,y:i+l.y,data:{...l,placement:s}}}}},lO=function(e){return void 0===e&&(e={}),{name:"shift",options:e,async fn(t){const{x:n,y:r,placement:o}=t,{mainAxis:i=!0,crossAxis:s=!1,limiter:a={fn:e=>{let{x:t,y:n}=e;return{x:t,y:n}}},...l}=uj(e,t),c={x:n,y:r},u=await kj(t,l),d=vj(dj(o)),f=pj(d);let p=c[f],h=c[d];if(i){const e="y"===f?"bottom":"right";p=cj(p+u["y"===f?"top":"left"],p,p-u[e])}if(s){const e="y"===d?"bottom":"right";h=cj(h+u["y"===d?"top":"left"],h,h-u[e])}const v=a.fn({...t,[f]:p,[d]:h});return{...v,data:{x:v.x-n,y:v.y-r,enabled:{[f]:i,[d]:s}}}}}},cO=function(e){return void 0===e&&(e={}),{name:"flip",options:e,async fn(t){var n,r;const{placement:o,middlewareData:i,rects:s,initialPlacement:a,platform:l,elements:c}=t,{mainAxis:u=!0,crossAxis:d=!0,fallbackPlacements:f,fallbackStrategy:p="bestFit",fallbackAxisSideDirection:h="none",flipAlignment:v=!0,...m}=uj(e,t);if(null!=(n=i.arrow)&&n.alignmentOffset)return{};const g=dj(o),y=vj(a),b=dj(a)===a,w=await(null==l.isRTL?void 0:l.isRTL(c.floating)),x=f||(b||!v?[yj(a)]:function(e){const t=yj(e);return[gj(e),t,gj(t)]}(a)),k="none"!==h;!f&&k&&x.push(...function(e,t,n,r){const o=fj(e);let i=function(e,t,n){const r=["left","right"],o=["right","left"],i=["top","bottom"],s=["bottom","top"];switch(e){case"top":case"bottom":return n?t?o:r:t?r:o;case"left":case"right":return t?i:s;default:return[]}}(dj(e),"start"===n,r);return o&&(i=i.map((e=>e+"-"+o)),t&&(i=i.concat(i.map(gj)))),i}(a,v,h,w));const E=[a,...x],S=await kj(t,m),C=[];let _=(null==(r=i.flip)?void 0:r.overflows)||[];if(u&&C.push(S[g]),d){const e=function(e,t,n){void 0===n&&(n=!1);const r=fj(e),o=mj(e),i=hj(o);let s="x"===o?r===(n?"end":"start")?"right":"left":"start"===r?"bottom":"top";return t.reference[i]>t.floating[i]&&(s=yj(s)),[s,yj(s)]}(o,s,w);C.push(S[e[0]],S[e[1]])}if(_=[..._,{placement:o,overflows:C}],!C.every((e=>e<=0))){var A,N;const e=((null==(A=i.flip)?void 0:A.index)||0)+1,t=E[e];if(t)return{data:{index:e,overflows:_},reset:{placement:t}};let n=null==(N=_.filter((e=>e.overflows[0]<=0)).sort(((e,t)=>e.overflows[1]-t.overflows[1]))[0])?void 0:N.placement;if(!n)switch(p){case"bestFit":{var P;const e=null==(P=_.filter((e=>{if(k){const t=vj(e.placement);return t===y||"y"===t}return!0})).map((e=>[e.placement,e.overflows.filter((e=>e>0)).reduce(((e,t)=>e+t),0)])).sort(((e,t)=>e[1]-t[1]))[0])?void 0:P[0];e&&(n=e);break}case"initialPlacement":n=a}if(o!==n)return{reset:{placement:n}}}return{}}}},uO=function(e){return void 0===e&&(e={}),{name:"size",options:e,async fn(t){var n,r;const{placement:o,rects:i,platform:s,elements:a}=t,{apply:l=()=>{},...c}=uj(e,t),u=await kj(t,c),d=dj(o),f=fj(o),p="y"===vj(o),{width:h,height:v}=i.floating;let m,g;"top"===d||"bottom"===d?(m=d,g=f===(await(null==s.isRTL?void 0:s.isRTL(a.floating))?"start":"end")?"left":"right"):(g=d,m="end"===f?"top":"bottom");const y=v-u.top-u.bottom,b=h-u.left-u.right,w=nj(v-u[m],y),x=nj(h-u[g],b),k=!t.middlewareData.shift;let E=w,S=x;if(null!=(n=t.middlewareData.shift)&&n.enabled.x&&(S=b),null!=(r=t.middlewareData.shift)&&r.enabled.y&&(E=y),k&&!f){const e=rj(u.left,0),t=rj(u.right,0),n=rj(u.top,0),r=rj(u.bottom,0);p?S=h-2*(0!==e||0!==t?e+t:rj(u.left,u.right)):E=v-2*(0!==n||0!==r?n+r:rj(u.top,u.bottom))}await l({...t,availableWidth:S,availableHeight:E});const C=await s.getDimensions(a.floating);return h!==C.width||v!==C.height?{reset:{rects:!0}}:{}}}},dO=function(e){return void 0===e&&(e={}),{name:"hide",options:e,async fn(t){const{rects:n}=t,{strategy:r="referenceHidden",...o}=uj(e,t);switch(r){case"referenceHidden":{const e=Ej(await kj(t,{...o,elementContext:"reference"}),n.reference);return{data:{referenceHiddenOffsets:e,referenceHidden:Sj(e)}}}case"escaped":{const e=Ej(await kj(t,{...o,altBoundary:!0}),n.floating);return{data:{escapedOffsets:e,escaped:Sj(e)}}}default:return{}}}}},fO=e=>({name:"arrow",options:e,async fn(t){const{x:n,y:r,placement:o,rects:i,platform:s,elements:a,middlewareData:l}=t,{element:c,padding:u=0}=uj(e,t)||{};if(null==c)return{};const d=bj(u),f={x:n,y:r},p=mj(o),h=hj(p),v=await s.getDimensions(c),m="y"===p,g=m?"top":"left",y=m?"bottom":"right",b=m?"clientHeight":"clientWidth",w=i.reference[h]+i.reference[p]-f[p]-i.floating[h],x=f[p]-i.reference[p],k=await(null==s.getOffsetParent?void 0:s.getOffsetParent(c));let E=k?k[b]:0;E&&await(null==s.isElement?void 0:s.isElement(k))||(E=a.floating[b]||i.floating[h]);const S=w/2-x/2,C=E/2-v[h]/2-1,_=nj(d[g],C),A=nj(d[y],C),N=_,P=E-v[h]-A,$=E/2-v[h]/2+S,j=cj(N,$,P),O=!l.arrow&&null!=fj(o)&&$!==j&&i.reference[h]/2-($n&&(p=n)}if(c){var g,y;const e="y"===f?"width":"height",t=["top","left"].includes(dj(o)),n=i.reference[d]-i.floating[e]+(t&&(null==(g=s.offset)?void 0:g[d])||0)+(t?0:m.crossAxis),r=i.reference[d]+i.reference[e]+(t?0:(null==(y=s.offset)?void 0:y[d])||0)-(t?m.crossAxis:0);hr&&(h=r)}return{[f]:p,[d]:h}}}},hO=(e,t,n)=>{const r=new Map,o={platform:iO,...n},i={...o.platform,_c:r};return(async(e,t,n)=>{const{placement:r="bottom",strategy:o="absolute",middleware:i=[],platform:s}=n,a=i.filter(Boolean),l=await(null==s.isRTL?void 0:s.isRTL(t));let c=await s.getElementRects({reference:e,floating:t,strategy:o}),{x:u,y:d}=xj(c,r,l),f=r,p={},h=0;for(let v=0;v{t.current=e})),t}const wO=e=>({name:"arrow",options:e,fn(t){const{element:n,padding:r}="function"==typeof e?e(t):e;return n&&(o=n,{}.hasOwnProperty.call(o,"current"))?null!=n.current?fO({element:n.current,padding:r}).fn(t):{}:n?fO({element:n,padding:r}).fn(t):{};var o}}),xO=(e,t)=>({...aO(e),options:[e,t]}),kO=(e,t)=>({...lO(e),options:[e,t]}),EO=(e,t)=>({...pO(e),options:[e,t]}),SO=(e,t)=>({...cO(e),options:[e,t]}),CO=(e,t)=>({...uO(e),options:[e,t]}),_O=(e,t)=>({...dO(e),options:[e,t]}),AO=(e,t)=>({...wO(e),options:[e,t]});var NO=qe.forwardRef(((e,t)=>{const{children:n,width:r=10,height:o=5,...i}=e;return nt.jsx(_$.svg,{...i,ref:t,width:r,height:o,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:e.asChild?n:nt.jsx("polygon",{points:"0,0 30,0 15,10"})})}));NO.displayName="Arrow";var PO=NO;var $O="Popper",[jO,OO]=g$($O),[RO,MO]=jO($O),TO=e=>{const{__scopePopper:t,children:n}=e,[r,o]=qe.useState(null);return nt.jsx(RO,{scope:t,anchor:r,onAnchorChange:o,children:n})};TO.displayName=$O;var LO="PopperAnchor",IO=qe.forwardRef(((e,t)=>{const{__scopePopper:n,virtualRef:r,...o}=e,i=MO(LO,n),s=qe.useRef(null),a=m$(t,s);return qe.useEffect((()=>{i.onAnchorChange((null==r?void 0:r.current)||s.current)})),r?null:nt.jsx(_$.div,{...o,ref:a})}));IO.displayName=LO;var zO="PopperContent",[DO,BO]=jO(zO),UO=qe.forwardRef(((e,t)=>{var n,r,o,i,s,a;const{__scopePopper:l,side:c="bottom",sideOffset:u=0,align:d="center",alignOffset:f=0,arrowPadding:p=0,avoidCollisions:h=!0,collisionBoundary:v=[],collisionPadding:m=0,sticky:g="partial",hideWhenDetached:y=!1,updatePositionStrategy:b="optimized",onPlaced:w,...x}=e,k=MO(zO,l),[E,S]=qe.useState(null),C=m$(t,(e=>S(e))),[_,A]=qe.useState(null),N=function(e){const[t,n]=qe.useState(void 0);return J$((()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});const t=new ResizeObserver((t=>{if(!Array.isArray(t))return;if(!t.length)return;const r=t[0];let o,i;if("borderBoxSize"in r){const e=r.borderBoxSize,t=Array.isArray(e)?e[0]:e;o=t.inlineSize,i=t.blockSize}else o=e.offsetWidth,i=e.offsetHeight;n({width:o,height:i})}));return t.observe(e,{box:"border-box"}),()=>t.unobserve(e)}n(void 0)}),[e]),t}(_),P=(null==N?void 0:N.width)??0,$=(null==N?void 0:N.height)??0,j=c+("center"!==d?"-"+d:""),O="number"==typeof m?m:{top:0,right:0,bottom:0,left:0,...m},R=Array.isArray(v)?v:[v],M=R.length>0,T={padding:O,boundary:R.filter(WO),altBoundary:M},{refs:L,floatingStyles:I,placement:z,isPositioned:D,middlewareData:B}=function(e){void 0===e&&(e={});const{placement:t="bottom",strategy:n="absolute",middleware:r=[],platform:o,elements:{reference:i,floating:s}={},transform:a=!0,whileElementsMounted:l,open:c}=e,[u,d]=qe.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[f,p]=qe.useState(r);mO(f,r)||p(r);const[h,v]=qe.useState(null),[m,g]=qe.useState(null),y=qe.useCallback((e=>{e!==k.current&&(k.current=e,v(e))}),[]),b=qe.useCallback((e=>{e!==E.current&&(E.current=e,g(e))}),[]),w=i||h,x=s||m,k=qe.useRef(null),E=qe.useRef(null),S=qe.useRef(u),C=null!=l,_=bO(l),A=bO(o),N=bO(c),P=qe.useCallback((()=>{if(!k.current||!E.current)return;const e={placement:t,strategy:n,middleware:f};A.current&&(e.platform=A.current),hO(k.current,E.current,e).then((e=>{const t={...e,isPositioned:!1!==N.current};$.current&&!mO(S.current,t)&&(S.current=t,df.flushSync((()=>{d(t)})))}))}),[f,t,n,A,N]);vO((()=>{!1===c&&S.current.isPositioned&&(S.current.isPositioned=!1,d((e=>({...e,isPositioned:!1}))))}),[c]);const $=qe.useRef(!1);vO((()=>($.current=!0,()=>{$.current=!1})),[]),vO((()=>{if(w&&(k.current=w),x&&(E.current=x),w&&x){if(_.current)return _.current(w,x,P);P()}}),[w,x,P,_,C]);const j=qe.useMemo((()=>({reference:k,floating:E,setReference:y,setFloating:b})),[y,b]),O=qe.useMemo((()=>({reference:w,floating:x})),[w,x]),R=qe.useMemo((()=>{const e={position:n,left:0,top:0};if(!O.floating)return e;const t=yO(O.floating,u.x),r=yO(O.floating,u.y);return a?{...e,transform:"translate("+t+"px, "+r+"px)",...gO(O.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:t,top:r}}),[n,a,O.floating,u.x,u.y]);return qe.useMemo((()=>({...u,update:P,refs:j,elements:O,floatingStyles:R})),[u,P,j,O,R])}({strategy:"fixed",placement:j,whileElementsMounted:(...e)=>sO(...e,{animationFrame:"always"===b}),elements:{reference:k.anchor},middleware:[xO({mainAxis:u+$,alignmentAxis:f}),h&&kO({mainAxis:!0,crossAxis:!1,limiter:"partial"===g?EO():void 0,...T}),h&&SO({...T}),CO({...T,apply:({elements:e,rects:t,availableWidth:n,availableHeight:r})=>{const{width:o,height:i}=t.reference,s=e.floating.style;s.setProperty("--radix-popper-available-width",`${n}px`),s.setProperty("--radix-popper-available-height",`${r}px`),s.setProperty("--radix-popper-anchor-width",`${o}px`),s.setProperty("--radix-popper-anchor-height",`${i}px`)}}),_&&AO({element:_,padding:p}),qO({arrowWidth:P,arrowHeight:$}),y&&_O({strategy:"referenceHidden",...T})]}),[U,F]=KO(z),H=b$(w);J$((()=>{D&&(null==H||H())}),[D,H]);const V=null==(n=B.arrow)?void 0:n.x,W=null==(r=B.arrow)?void 0:r.y,q=0!==(null==(o=B.arrow)?void 0:o.centerOffset),[K,Q]=qe.useState();return J$((()=>{E&&Q(window.getComputedStyle(E).zIndex)}),[E]),nt.jsx("div",{ref:L.setFloating,"data-radix-popper-content-wrapper":"",style:{...I,transform:D?I.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:K,"--radix-popper-transform-origin":[null==(i=B.transformOrigin)?void 0:i.x,null==(s=B.transformOrigin)?void 0:s.y].join(" "),...(null==(a=B.hide)?void 0:a.referenceHidden)&&{visibility:"hidden",pointerEvents:"none"}},dir:e.dir,children:nt.jsx(DO,{scope:l,placedSide:U,onArrowChange:A,arrowX:V,arrowY:W,shouldHideArrow:q,children:nt.jsx(_$.div,{"data-side":U,"data-align":F,...x,ref:C,style:{...x.style,animation:D?void 0:"none"}})})})}));UO.displayName=zO;var FO="PopperArrow",HO={top:"bottom",right:"left",bottom:"top",left:"right"},VO=qe.forwardRef((function(e,t){const{__scopePopper:n,...r}=e,o=BO(FO,n),i=HO[o.placedSide];return nt.jsx("span",{ref:o.onArrowChange,style:{position:"absolute",left:o.arrowX,top:o.arrowY,[i]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[o.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[o.placedSide],visibility:o.shouldHideArrow?"hidden":void 0},children:nt.jsx(PO,{...r,ref:t,style:{...r.style,display:"block"}})})}));function WO(e){return null!==e}VO.displayName=FO;var qO=e=>({name:"transformOrigin",options:e,fn(t){var n,r,o;const{placement:i,rects:s,middlewareData:a}=t,l=0!==(null==(n=a.arrow)?void 0:n.centerOffset),c=l?0:e.arrowWidth,u=l?0:e.arrowHeight,[d,f]=KO(i),p={start:"0%",center:"50%",end:"100%"}[f],h=((null==(r=a.arrow)?void 0:r.x)??0)+c/2,v=((null==(o=a.arrow)?void 0:o.y)??0)+u/2;let m="",g="";return"bottom"===d?(m=l?p:`${h}px`,g=-u+"px"):"top"===d?(m=l?p:`${h}px`,g=`${s.floating.height+u}px`):"right"===d?(m=-u+"px",g=l?p:`${v}px`):"left"===d&&(m=`${s.floating.width+u}px`,g=l?p:`${v}px`),{data:{x:m,y:g}}}});function KO(e){const[t,n="center"]=e.split("-");return[t,n]}var QO=TO,YO=IO,GO=UO,JO=VO,XO=qe.forwardRef(((e,t)=>{var n;const{container:r,...o}=e,[i,s]=qe.useState(!1);J$((()=>s(!0)),[]);const a=r||i&&(null==(n=null==globalThis?void 0:globalThis.document)?void 0:n.body);return a?ff.createPortal(nt.jsx(_$.div,{...o,ref:t}),a):null}));XO.displayName="Portal";var ZO=e=>{const{present:t,children:n}=e,r=function(e){const[t,n]=qe.useState(),r=qe.useRef({}),o=qe.useRef(e),i=qe.useRef("none"),s=e?"mounted":"unmounted",[a,l]=function(e,t){return qe.useReducer(((e,n)=>t[e][n]??e),e)}(s,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return qe.useEffect((()=>{const e=eR(r.current);i.current="mounted"===a?e:"none"}),[a]),J$((()=>{const t=r.current,n=o.current;if(n!==e){const r=i.current,s=eR(t);if(e)l("MOUNT");else if("none"===s||"none"===(null==t?void 0:t.display))l("UNMOUNT");else{l(n&&r!==s?"ANIMATION_OUT":"UNMOUNT")}o.current=e}}),[e,l]),J$((()=>{if(t){let e;const n=t.ownerDocument.defaultView??window,s=i=>{const s=eR(r.current).includes(i.animationName);if(i.target===t&&s&&(l("ANIMATION_END"),!o.current)){const r=t.style.animationFillMode;t.style.animationFillMode="forwards",e=n.setTimeout((()=>{"forwards"===t.style.animationFillMode&&(t.style.animationFillMode=r)}))}},a=e=>{e.target===t&&(i.current=eR(r.current))};return t.addEventListener("animationstart",a),t.addEventListener("animationcancel",s),t.addEventListener("animationend",s),()=>{n.clearTimeout(e),t.removeEventListener("animationstart",a),t.removeEventListener("animationcancel",s),t.removeEventListener("animationend",s)}}l("ANIMATION_END")}),[t,l]),{isPresent:["mounted","unmountSuspended"].includes(a),ref:qe.useCallback((e=>{e&&(r.current=getComputedStyle(e)),n(e)}),[])}}(t),o="function"==typeof n?n({present:r.isPresent}):qe.Children.only(n),i=m$(r.ref,function(e){var t,n;let r=null==(t=Object.getOwnPropertyDescriptor(e.props,"ref"))?void 0:t.get,o=r&&"isReactWarning"in r&&r.isReactWarning;if(o)return e.ref;if(r=null==(n=Object.getOwnPropertyDescriptor(e,"ref"))?void 0:n.get,o=r&&"isReactWarning"in r&&r.isReactWarning,o)return e.props.ref;return e.props.ref||e.ref}(o));return"function"==typeof n||r.isPresent?qe.cloneElement(o,{ref:i}):null};function eR(e){return(null==e?void 0:e.animationName)||"none"}ZO.displayName="Presence";var tR="rovingFocusGroup.onEntryFocus",nR={bubbles:!1,cancelable:!0},rR="RovingFocusGroup",[oR,iR,sR]=N$(rR),[aR,lR]=g$(rR,[sR]),[cR,uR]=aR(rR),dR=qe.forwardRef(((e,t)=>nt.jsx(oR.Provider,{scope:e.__scopeRovingFocusGroup,children:nt.jsx(oR.Slot,{scope:e.__scopeRovingFocusGroup,children:nt.jsx(fR,{...e,ref:t})})})));dR.displayName=rR;var fR=qe.forwardRef(((e,t)=>{const{__scopeRovingFocusGroup:n,orientation:r,loop:o=!1,dir:i,currentTabStopId:s,defaultCurrentTabStopId:a,onCurrentTabStopIdChange:l,onEntryFocus:c,preventScrollOnEntryFocus:u=!1,...d}=e,f=qe.useRef(null),p=m$(t,f),h=$$(i),[v=null,m]=w$({prop:s,defaultProp:a,onChange:l}),[g,y]=qe.useState(!1),b=b$(c),w=iR(n),x=qe.useRef(!1),[k,E]=qe.useState(0);return qe.useEffect((()=>{const e=f.current;if(e)return e.addEventListener(tR,b),()=>e.removeEventListener(tR,b)}),[b]),nt.jsx(cR,{scope:n,orientation:r,dir:h,loop:o,currentTabStopId:v,onItemFocus:qe.useCallback((e=>m(e)),[m]),onItemShiftTab:qe.useCallback((()=>y(!0)),[]),onFocusableItemAdd:qe.useCallback((()=>E((e=>e+1))),[]),onFocusableItemRemove:qe.useCallback((()=>E((e=>e-1))),[]),children:nt.jsx(_$.div,{tabIndex:g||0===k?-1:0,"data-orientation":r,...d,ref:p,style:{outline:"none",...e.style},onMouseDown:p$(e.onMouseDown,(()=>{x.current=!0})),onFocus:p$(e.onFocus,(e=>{const t=!x.current;if(e.target===e.currentTarget&&t&&!g){const t=new CustomEvent(tR,nR);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){const e=w().filter((e=>e.focusable));mR([e.find((e=>e.active)),e.find((e=>e.id===v)),...e].filter(Boolean).map((e=>e.ref.current)),u)}}x.current=!1})),onBlur:p$(e.onBlur,(()=>y(!1)))})})})),pR="RovingFocusGroupItem",hR=qe.forwardRef(((e,t)=>{const{__scopeRovingFocusGroup:n,focusable:r=!0,active:o=!1,tabStopId:i,...s}=e,a=ej(),l=i||a,c=uR(pR,n),u=c.currentTabStopId===l,d=iR(n),{onFocusableItemAdd:f,onFocusableItemRemove:p}=c;return qe.useEffect((()=>{if(r)return f(),()=>p()}),[r,f,p]),nt.jsx(oR.ItemSlot,{scope:n,id:l,focusable:r,active:o,children:nt.jsx(_$.span,{tabIndex:u?0:-1,"data-orientation":c.orientation,...s,ref:t,onMouseDown:p$(e.onMouseDown,(e=>{r?c.onItemFocus(l):e.preventDefault()})),onFocus:p$(e.onFocus,(()=>c.onItemFocus(l))),onKeyDown:p$(e.onKeyDown,(e=>{if("Tab"===e.key&&e.shiftKey)return void c.onItemShiftTab();if(e.target!==e.currentTarget)return;const t=function(e,t,n){const r=function(e,t){return"rtl"!==t?e:"ArrowLeft"===e?"ArrowRight":"ArrowRight"===e?"ArrowLeft":e}(e.key,n);return"vertical"===t&&["ArrowLeft","ArrowRight"].includes(r)||"horizontal"===t&&["ArrowUp","ArrowDown"].includes(r)?void 0:vR[r]}(e,c.orientation,c.dir);if(void 0!==t){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let o=d().filter((e=>e.focusable)).map((e=>e.ref.current));if("last"===t)o.reverse();else if("prev"===t||"next"===t){"prev"===t&&o.reverse();const i=o.indexOf(e.currentTarget);o=c.loop?(r=i+1,(n=o).map(((e,t)=>n[(r+t)%n.length]))):o.slice(i+1)}setTimeout((()=>mR(o)))}var n,r}))})})}));hR.displayName=pR;var vR={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function mR(e,t=!1){const n=document.activeElement;for(const r of e){if(r===n)return;if(r.focus({preventScroll:t}),document.activeElement!==n)return}}var gR=dR,yR=hR,bR=new WeakMap,wR=new WeakMap,xR={},kR=0,ER=function(e){return e&&(e.host||ER(e.parentNode))},SR=function(e,t,n,r){var o=function(e,t){return t.map((function(t){if(e.contains(t))return t;var n=ER(t);return n&&e.contains(n)?n:(console.error("aria-hidden",t,"in not contained inside",e,". Doing nothing"),null)})).filter((function(e){return Boolean(e)}))}(t,Array.isArray(e)?e:[e]);xR[n]||(xR[n]=new WeakMap);var i=xR[n],s=[],a=new Set,l=new Set(o),c=function(e){e&&!a.has(e)&&(a.add(e),c(e.parentNode))};o.forEach(c);var u=function(e){e&&!l.has(e)&&Array.prototype.forEach.call(e.children,(function(e){if(a.has(e))u(e);else try{var t=e.getAttribute(r),o=null!==t&&"false"!==t,l=(bR.get(e)||0)+1,c=(i.get(e)||0)+1;bR.set(e,l),i.set(e,c),s.push(e),1===l&&o&&wR.set(e,!0),1===c&&e.setAttribute(n,"true"),o||e.setAttribute(r,"true")}catch(d){console.error("aria-hidden: cannot operate on ",e,d)}}))};return u(t),a.clear(),kR++,function(){s.forEach((function(e){var t=bR.get(e)-1,o=i.get(e)-1;bR.set(e,t),i.set(e,o),t||(wR.has(e)||e.removeAttribute(r),wR.delete(e)),o||e.removeAttribute(n)})),--kR||(bR=new WeakMap,bR=new WeakMap,wR=new WeakMap,xR={})}},CR=function(e,t,n){void 0===n&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=function(e){return"undefined"==typeof document?null:(Array.isArray(e)?e[0]:e).ownerDocument.body}(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live]"))),SR(r,o,n,"aria-hidden")):function(){return null}},_R="right-scroll-bar-position",AR="width-before-scroll-bar";function NR(e,t){return"function"==typeof e?e(t):e&&(e.current=t),e}var PR="undefined"!=typeof window?qe.useLayoutEffect:qe.useEffect,$R=new WeakMap;function jR(e,t){var n,r,o,i=(n=null,r=function(t){return e.forEach((function(e){return NR(e,t)}))},(o=qe.useState((function(){return{value:n,callback:r,facade:{get current(){return o.value},set current(e){var t=o.value;t!==e&&(o.value=e,o.callback(e,t))}}}}))[0]).callback=r,o.facade);return PR((function(){var t=$R.get(i);if(t){var n=new Set(t),r=new Set(e),o=i.current;n.forEach((function(e){r.has(e)||NR(e,null)})),r.forEach((function(e){n.has(e)||NR(e,o)}))}$R.set(i,e)}),[e]),i}function OR(e){return e}var RR=function(e){var t=e.sideCar,n=Qy(e,["sideCar"]);if(!t)throw new Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw new Error("Sidecar medium not found");return qe.createElement(r,Ky({},n))};RR.isSideCarExport=!0;var MR=function(e){void 0===e&&(e={});var t=function(e,t){void 0===t&&(t=OR);var n=[],r=!1;return{read:function(){if(r)throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return n.length?n[n.length-1]:e},useMedium:function(e){var o=t(e,r);return n.push(o),function(){n=n.filter((function(e){return e!==o}))}},assignSyncMedium:function(e){for(r=!0;n.length;){var t=n;n=[],t.forEach(e)}n={push:function(t){return e(t)},filter:function(){return n}}},assignMedium:function(e){r=!0;var t=[];if(n.length){var o=n;n=[],o.forEach(e),t=n}var i=function(){var n=t;t=[],n.forEach(e)},s=function(){return Promise.resolve().then(i)};s(),n={push:function(e){t.push(e),s()},filter:function(e){return t=t.filter(e),n}}}}}(null);return t.options=Ky({async:!0,ssr:!1},e),t}(),TR=function(){},LR=qe.forwardRef((function(e,t){var n=qe.useRef(null),r=qe.useState({onScrollCapture:TR,onWheelCapture:TR,onTouchMoveCapture:TR}),o=r[0],i=r[1],s=e.forwardProps,a=e.children,l=e.className,c=e.removeScrollBar,u=e.enabled,d=e.shards,f=e.sideCar,p=e.noIsolation,h=e.inert,v=e.allowPinchZoom,m=e.as,g=void 0===m?"div":m,y=e.gapMode,b=Qy(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noIsolation","inert","allowPinchZoom","as","gapMode"]),w=f,x=jR([n,t]),k=Ky(Ky({},b),o);return qe.createElement(qe.Fragment,null,u&&qe.createElement(w,{sideCar:MR,removeScrollBar:c,shards:d,noIsolation:p,inert:h,setCallbacks:i,allowPinchZoom:!!v,lockRef:n,gapMode:y}),s?qe.cloneElement(qe.Children.only(a),Ky(Ky({},k),{ref:x})):qe.createElement(g,Ky({},k,{className:l,ref:x}),a))}));LR.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},LR.classNames={fullWidth:AR,zeroRight:_R};function IR(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=function(){if("undefined"!=typeof __webpack_nonce__)return __webpack_nonce__}();return t&&e.setAttribute("nonce",t),e}var zR=function(){var e=0,t=null;return{add:function(n){var r,o;0==e&&(t=IR())&&(o=n,(r=t).styleSheet?r.styleSheet.cssText=o:r.appendChild(document.createTextNode(o)),function(e){(document.head||document.getElementsByTagName("head")[0]).appendChild(e)}(t)),e++},remove:function(){! --e&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},DR=function(){var e,t=(e=zR(),function(t,n){qe.useEffect((function(){return e.add(t),function(){e.remove()}}),[t&&n])});return function(e){var n=e.styles,r=e.dynamic;return t(n,r),null}},BR={left:0,top:0,right:0,gap:0},UR=function(e){return parseInt(e||"",10)||0},FR=function(e){if(void 0===e&&(e="margin"),"undefined"==typeof window)return BR;var t=function(e){var t=window.getComputedStyle(document.body),n=t["padding"===e?"paddingLeft":"marginLeft"],r=t["padding"===e?"paddingTop":"marginTop"],o=t["padding"===e?"paddingRight":"marginRight"];return[UR(n),UR(r),UR(o)]}(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},HR=DR(),VR="data-scroll-locked",WR=function(e,t,n,r){var o=e.left,i=e.top,s=e.right,a=e.gap;return void 0===n&&(n="margin"),"\n .".concat("with-scroll-bars-hidden"," {\n overflow: hidden ").concat(r,";\n padding-right: ").concat(a,"px ").concat(r,";\n }\n body[").concat(VR,"] {\n overflow: hidden ").concat(r,";\n overscroll-behavior: contain;\n ").concat([t&&"position: relative ".concat(r,";"),"margin"===n&&"\n padding-left: ".concat(o,"px;\n padding-top: ").concat(i,"px;\n padding-right: ").concat(s,"px;\n margin-left:0;\n margin-top:0;\n margin-right: ").concat(a,"px ").concat(r,";\n "),"padding"===n&&"padding-right: ".concat(a,"px ").concat(r,";")].filter(Boolean).join(""),"\n }\n \n .").concat(_R," {\n right: ").concat(a,"px ").concat(r,";\n }\n \n .").concat(AR," {\n margin-right: ").concat(a,"px ").concat(r,";\n }\n \n .").concat(_R," .").concat(_R," {\n right: 0 ").concat(r,";\n }\n \n .").concat(AR," .").concat(AR," {\n margin-right: 0 ").concat(r,";\n }\n \n body[").concat(VR,"] {\n ").concat("--removed-body-scroll-bar-size",": ").concat(a,"px;\n }\n")},qR=function(){var e=parseInt(document.body.getAttribute(VR)||"0",10);return isFinite(e)?e:0},KR=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,o=void 0===r?"margin":r;qe.useEffect((function(){return document.body.setAttribute(VR,(qR()+1).toString()),function(){var e=qR()-1;e<=0?document.body.removeAttribute(VR):document.body.setAttribute(VR,e.toString())}}),[]);var i=qe.useMemo((function(){return FR(o)}),[o]);return qe.createElement(HR,{styles:WR(i,!t,o,n?"":"!important")})},QR=!1;if("undefined"!=typeof window)try{var YR=Object.defineProperty({},"passive",{get:function(){return QR=!0,!0}});window.addEventListener("test",YR,YR),window.removeEventListener("test",YR,YR)}catch(aI){QR=!1}var GR=!!QR&&{passive:!1},JR=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return"hidden"!==n[t]&&!(n.overflowY===n.overflowX&&!function(e){return"TEXTAREA"===e.tagName}(e)&&"visible"===n[t])},XR=function(e,t){var n=t.ownerDocument,r=t;do{if("undefined"!=typeof ShadowRoot&&r instanceof ShadowRoot&&(r=r.host),ZR(e,r)){var o=eM(e,r);if(o[1]>o[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},ZR=function(e,t){return"v"===e?function(e){return JR(e,"overflowY")}(t):function(e){return JR(e,"overflowX")}(t)},eM=function(e,t){return"v"===e?[(n=t).scrollTop,n.scrollHeight,n.clientHeight]:function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]}(t);var n},tM=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},nM=function(e){return[e.deltaX,e.deltaY]},rM=function(e){return e&&"current"in e?e.current:e},oM=function(e){return"\n .block-interactivity-".concat(e," {pointer-events: none;}\n .allow-interactivity-").concat(e," {pointer-events: all;}\n")},iM=0,sM=[];function aM(e){for(var t=null;null!==e;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const lM=(cM=function(e){var t=qe.useRef([]),n=qe.useRef([0,0]),r=qe.useRef(),o=qe.useState(iM++)[0],i=qe.useState(DR)[0],s=qe.useRef(e);qe.useEffect((function(){s.current=e}),[e]),qe.useEffect((function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var t=Zy([e.lockRef.current],(e.shards||[]).map(rM),!0).filter(Boolean);return t.forEach((function(e){return e.classList.add("allow-interactivity-".concat(o))})),function(){document.body.classList.remove("block-interactivity-".concat(o)),t.forEach((function(e){return e.classList.remove("allow-interactivity-".concat(o))}))}}}),[e.inert,e.lockRef.current,e.shards]);var a=qe.useCallback((function(e,t){if("touches"in e&&2===e.touches.length||"wheel"===e.type&&e.ctrlKey)return!s.current.allowPinchZoom;var o,i=tM(e),a=n.current,l="deltaX"in e?e.deltaX:a[0]-i[0],c="deltaY"in e?e.deltaY:a[1]-i[1],u=e.target,d=Math.abs(l)>Math.abs(c)?"h":"v";if("touches"in e&&"h"===d&&"range"===u.type)return!1;var f=XR(d,u);if(!f)return!0;if(f?o=d:(o="v"===d?"h":"v",f=XR(d,u)),!f)return!1;if(!r.current&&"changedTouches"in e&&(l||c)&&(r.current=o),!o)return!0;var p=r.current||o;return function(e,t,n,r,o){var i=function(e,t){return"h"===e&&"rtl"===t?-1:1}(e,window.getComputedStyle(t).direction),s=i*r,a=n.target,l=t.contains(a),c=!1,u=s>0,d=0,f=0;do{var p=eM(e,a),h=p[0],v=p[1]-p[2]-i*h;(h||v)&&ZR(e,a)&&(d+=v,f+=h),a=a instanceof ShadowRoot?a.host:a.parentNode}while(!l&&a!==document.body||l&&(t.contains(a)||t===a));return u&&(Math.abs(d)<1||!o)?c=!0:u||!(Math.abs(f)<1)&&o||(c=!0),c}(p,t,e,"h"===p?l:c,!0)}),[]),l=qe.useCallback((function(e){var n=e;if(sM.length&&sM[sM.length-1]===i){var r="deltaY"in n?nM(n):tM(n),o=t.current.filter((function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&(t=e.delta,o=r,t[0]===o[0]&&t[1]===o[1]);var t,o}))[0];if(o&&o.should)n.cancelable&&n.preventDefault();else if(!o){var l=(s.current.shards||[]).map(rM).filter(Boolean).filter((function(e){return e.contains(n.target)}));(l.length>0?a(n,l[0]):!s.current.noIsolation)&&n.cancelable&&n.preventDefault()}}}),[]),c=qe.useCallback((function(e,n,r,o){var i={name:e,delta:n,target:r,should:o,shadowParent:aM(r)};t.current.push(i),setTimeout((function(){t.current=t.current.filter((function(e){return e!==i}))}),1)}),[]),u=qe.useCallback((function(e){n.current=tM(e),r.current=void 0}),[]),d=qe.useCallback((function(t){c(t.type,nM(t),t.target,a(t,e.lockRef.current))}),[]),f=qe.useCallback((function(t){c(t.type,tM(t),t.target,a(t,e.lockRef.current))}),[]);qe.useEffect((function(){return sM.push(i),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener("wheel",l,GR),document.addEventListener("touchmove",l,GR),document.addEventListener("touchstart",u,GR),function(){sM=sM.filter((function(e){return e!==i})),document.removeEventListener("wheel",l,GR),document.removeEventListener("touchmove",l,GR),document.removeEventListener("touchstart",u,GR)}}),[]);var p=e.removeScrollBar,h=e.inert;return qe.createElement(qe.Fragment,null,h?qe.createElement(i,{styles:oM(o)}):null,p?qe.createElement(KR,{gapMode:e.gapMode}):null)},MR.useMedium(cM),RR);var cM,uM=qe.forwardRef((function(e,t){return qe.createElement(LR,Ky({},e,{ref:t,sideCar:lM}))}));uM.classNames=LR.classNames;var dM=["Enter"," "],fM=["ArrowUp","PageDown","End"],pM=["ArrowDown","PageUp","Home",...fM],hM={ltr:[...dM,"ArrowRight"],rtl:[...dM,"ArrowLeft"]},vM={ltr:["ArrowLeft"],rtl:["ArrowRight"]},mM="Menu",[gM,yM,bM]=N$(mM),[wM,xM]=g$(mM,[bM,OO,lR]),kM=OO(),EM=lR(),[SM,CM]=wM(mM),[_M,AM]=wM(mM),NM=e=>{const{__scopeMenu:t,open:n=!1,children:r,dir:o,onOpenChange:i,modal:s=!0}=e,a=kM(t),[l,c]=qe.useState(null),u=qe.useRef(!1),d=b$(i),f=$$(o);return qe.useEffect((()=>{const e=()=>{u.current=!0,document.addEventListener("pointerdown",t,{capture:!0,once:!0}),document.addEventListener("pointermove",t,{capture:!0,once:!0})},t=()=>u.current=!1;return document.addEventListener("keydown",e,{capture:!0}),()=>{document.removeEventListener("keydown",e,{capture:!0}),document.removeEventListener("pointerdown",t,{capture:!0}),document.removeEventListener("pointermove",t,{capture:!0})}}),[]),nt.jsx(QO,{...a,children:nt.jsx(SM,{scope:t,open:n,onOpenChange:d,content:l,onContentChange:c,children:nt.jsx(_M,{scope:t,onClose:qe.useCallback((()=>d(!1)),[d]),isUsingKeyboardRef:u,dir:f,modal:s,children:r})})})};NM.displayName=mM;var PM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e,o=kM(n);return nt.jsx(YO,{...o,...r,ref:t})}));PM.displayName="MenuAnchor";var $M="MenuPortal",[jM,OM]=wM($M,{forceMount:void 0}),RM=e=>{const{__scopeMenu:t,forceMount:n,children:r,container:o}=e,i=CM($M,t);return nt.jsx(jM,{scope:t,forceMount:n,children:nt.jsx(ZO,{present:n||i.open,children:nt.jsx(XO,{asChild:!0,container:o,children:r})})})};RM.displayName=$M;var MM="MenuContent",[TM,LM]=wM(MM),IM=qe.forwardRef(((e,t)=>{const n=OM(MM,e.__scopeMenu),{forceMount:r=n.forceMount,...o}=e,i=CM(MM,e.__scopeMenu),s=AM(MM,e.__scopeMenu);return nt.jsx(gM.Provider,{scope:e.__scopeMenu,children:nt.jsx(ZO,{present:r||i.open,children:nt.jsx(gM.Slot,{scope:e.__scopeMenu,children:s.modal?nt.jsx(zM,{...o,ref:t}):nt.jsx(DM,{...o,ref:t})})})})})),zM=qe.forwardRef(((e,t)=>{const n=CM(MM,e.__scopeMenu),r=qe.useRef(null),o=m$(t,r);return qe.useEffect((()=>{const e=r.current;if(e)return CR(e)}),[]),nt.jsx(BM,{...e,ref:o,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:p$(e.onFocusOutside,(e=>e.preventDefault()),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})})),DM=qe.forwardRef(((e,t)=>{const n=CM(MM,e.__scopeMenu);return nt.jsx(BM,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})})),BM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,loop:r=!1,trapFocus:o,onOpenAutoFocus:i,onCloseAutoFocus:s,disableOutsidePointerEvents:a,onEntryFocus:l,onEscapeKeyDown:c,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,disableOutsideScroll:h,...v}=e,m=CM(MM,n),g=AM(MM,n),y=kM(n),b=EM(n),w=yM(n),[x,k]=qe.useState(null),E=qe.useRef(null),S=m$(t,E,m.onContentChange),C=qe.useRef(0),_=qe.useRef(""),A=qe.useRef(0),N=qe.useRef(null),P=qe.useRef("right"),$=qe.useRef(0),j=h?uM:qe.Fragment,O=h?{as:x$,allowPinchZoom:!0}:void 0,R=e=>{var t,n;const r=_.current+e,o=w().filter((e=>!e.disabled)),i=document.activeElement,s=null==(t=o.find((e=>e.ref.current===i)))?void 0:t.textValue,a=function(e,t,n){const r=t.length>1&&Array.from(t).every((e=>e===t[0])),o=r?t[0]:t,i=n?e.indexOf(n):-1;let s=(a=e,l=Math.max(i,0),a.map(((e,t)=>a[(l+t)%a.length])));var a,l;1===o.length&&(s=s.filter((e=>e!==n)));const c=s.find((e=>e.toLowerCase().startsWith(o.toLowerCase())));return c!==n?c:void 0}(o.map((e=>e.textValue)),r,s),l=null==(n=o.find((e=>e.textValue===a)))?void 0:n.ref.current;!function e(t){_.current=t,window.clearTimeout(C.current),""!==t&&(C.current=window.setTimeout((()=>e("")),1e3))}(r),l&&setTimeout((()=>l.focus()))};qe.useEffect((()=>()=>window.clearTimeout(C.current)),[]),qe.useEffect((()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??B$()),document.body.insertAdjacentElement("beforeend",e[1]??B$()),D$++,()=>{1===D$&&document.querySelectorAll("[data-radix-focus-guard]").forEach((e=>e.remove())),D$--}}),[]);const M=qe.useCallback((e=>{var t,n;return P.current===(null==(t=N.current)?void 0:t.side)&&function(e,t){if(!t)return!1;const n={x:e.clientX,y:e.clientY};return function(e,t){const{x:n,y:r}=e;let o=!1;for(let i=0,s=t.length-1;ir!=c>r&&n<(l-e)*(r-a)/(c-a)+e&&(o=!o)}return o}(n,t)}(e,null==(n=N.current)?void 0:n.area)}),[]);return nt.jsx(TM,{scope:n,searchRef:_,onItemEnter:qe.useCallback((e=>{M(e)&&e.preventDefault()}),[M]),onItemLeave:qe.useCallback((e=>{var t;M(e)||(null==(t=E.current)||t.focus(),k(null))}),[M]),onTriggerLeave:qe.useCallback((e=>{M(e)&&e.preventDefault()}),[M]),pointerGraceTimerRef:A,onPointerGraceIntentChange:qe.useCallback((e=>{N.current=e}),[]),children:nt.jsx(j,{...O,children:nt.jsx(V$,{asChild:!0,trapped:o,onMountAutoFocus:p$(i,(e=>{var t;e.preventDefault(),null==(t=E.current)||t.focus({preventScroll:!0})})),onUnmountAutoFocus:s,children:nt.jsx(L$,{asChild:!0,disableOutsidePointerEvents:a,onEscapeKeyDown:c,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,children:nt.jsx(gR,{asChild:!0,...b,dir:g.dir,orientation:"vertical",loop:r,currentTabStopId:x,onCurrentTabStopIdChange:k,onEntryFocus:p$(l,(e=>{g.isUsingKeyboardRef.current||e.preventDefault()})),preventScrollOnEntryFocus:!0,children:nt.jsx(GO,{role:"menu","aria-orientation":"vertical","data-state":fT(m.open),"data-radix-menu-content":"",dir:g.dir,...y,...v,ref:S,style:{outline:"none",...v.style},onKeyDown:p$(v.onKeyDown,(e=>{const t=e.target.closest("[data-radix-menu-content]")===e.currentTarget,n=e.ctrlKey||e.altKey||e.metaKey,r=1===e.key.length;t&&("Tab"===e.key&&e.preventDefault(),!n&&r&&R(e.key));const o=E.current;if(e.target!==o)return;if(!pM.includes(e.key))return;e.preventDefault();const i=w().filter((e=>!e.disabled)).map((e=>e.ref.current));fM.includes(e.key)&&i.reverse(),function(e){const t=document.activeElement;for(const n of e){if(n===t)return;if(n.focus(),document.activeElement!==t)return}}(i)})),onBlur:p$(e.onBlur,(e=>{e.currentTarget.contains(e.target)||(window.clearTimeout(C.current),_.current="")})),onPointerMove:p$(e.onPointerMove,vT((e=>{const t=e.target,n=$.current!==e.clientX;if(e.currentTarget.contains(t)&&n){const t=e.clientX>$.current?"right":"left";P.current=t,$.current=e.clientX}})))})})})})})})}));IM.displayName=MM;var UM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(_$.div,{role:"group",...r,ref:t})}));UM.displayName="MenuGroup";var FM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(_$.div,{...r,ref:t})}));FM.displayName="MenuLabel";var HM="MenuItem",VM="menu.itemSelect",WM=qe.forwardRef(((e,t)=>{const{disabled:n=!1,onSelect:r,...o}=e,i=qe.useRef(null),s=AM(HM,e.__scopeMenu),a=LM(HM,e.__scopeMenu),l=m$(t,i),c=qe.useRef(!1);return nt.jsx(qM,{...o,ref:l,disabled:n,onClick:p$(e.onClick,(()=>{const e=i.current;if(!n&&e){const t=new CustomEvent(VM,{bubbles:!0,cancelable:!0});e.addEventListener(VM,(e=>null==r?void 0:r(e)),{once:!0}),A$(e,t),t.defaultPrevented?c.current=!1:s.onClose()}})),onPointerDown:t=>{var n;null==(n=e.onPointerDown)||n.call(e,t),c.current=!0},onPointerUp:p$(e.onPointerUp,(e=>{var t;c.current||null==(t=e.currentTarget)||t.click()})),onKeyDown:p$(e.onKeyDown,(e=>{const t=""!==a.searchRef.current;n||t&&" "===e.key||dM.includes(e.key)&&(e.currentTarget.click(),e.preventDefault())}))})}));WM.displayName=HM;var qM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,disabled:r=!1,textValue:o,...i}=e,s=LM(HM,n),a=EM(n),l=qe.useRef(null),c=m$(t,l),[u,d]=qe.useState(!1),[f,p]=qe.useState("");return qe.useEffect((()=>{const e=l.current;e&&p((e.textContent??"").trim())}),[i.children]),nt.jsx(gM.ItemSlot,{scope:n,disabled:r,textValue:o??f,children:nt.jsx(yR,{asChild:!0,...a,focusable:!r,children:nt.jsx(_$.div,{role:"menuitem","data-highlighted":u?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0,...i,ref:c,onPointerMove:p$(e.onPointerMove,vT((e=>{if(r)s.onItemLeave(e);else if(s.onItemEnter(e),!e.defaultPrevented){e.currentTarget.focus({preventScroll:!0})}}))),onPointerLeave:p$(e.onPointerLeave,vT((e=>s.onItemLeave(e)))),onFocus:p$(e.onFocus,(()=>d(!0))),onBlur:p$(e.onBlur,(()=>d(!1)))})})})})),KM=qe.forwardRef(((e,t)=>{const{checked:n=!1,onCheckedChange:r,...o}=e;return nt.jsx(tT,{scope:e.__scopeMenu,checked:n,children:nt.jsx(WM,{role:"menuitemcheckbox","aria-checked":pT(n)?"mixed":n,...o,ref:t,"data-state":hT(n),onSelect:p$(o.onSelect,(()=>null==r?void 0:r(!!pT(n)||!n)),{checkForDefaultPrevented:!1})})})}));KM.displayName="MenuCheckboxItem";var QM="MenuRadioGroup",[YM,GM]=wM(QM,{value:void 0,onValueChange:()=>{}}),JM=qe.forwardRef(((e,t)=>{const{value:n,onValueChange:r,...o}=e,i=b$(r);return nt.jsx(YM,{scope:e.__scopeMenu,value:n,onValueChange:i,children:nt.jsx(UM,{...o,ref:t})})}));JM.displayName=QM;var XM="MenuRadioItem",ZM=qe.forwardRef(((e,t)=>{const{value:n,...r}=e,o=GM(XM,e.__scopeMenu),i=n===o.value;return nt.jsx(tT,{scope:e.__scopeMenu,checked:i,children:nt.jsx(WM,{role:"menuitemradio","aria-checked":i,...r,ref:t,"data-state":hT(i),onSelect:p$(r.onSelect,(()=>{var e;return null==(e=o.onValueChange)?void 0:e.call(o,n)}),{checkForDefaultPrevented:!1})})})}));ZM.displayName=XM;var eT="MenuItemIndicator",[tT,nT]=wM(eT,{checked:!1}),rT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,forceMount:r,...o}=e,i=nT(eT,n);return nt.jsx(ZO,{present:r||pT(i.checked)||!0===i.checked,children:nt.jsx(_$.span,{...o,ref:t,"data-state":hT(i.checked)})})}));rT.displayName=eT;var oT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(_$.div,{role:"separator","aria-orientation":"horizontal",...r,ref:t})}));oT.displayName="MenuSeparator";var iT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e,o=kM(n);return nt.jsx(JO,{...o,...r,ref:t})}));iT.displayName="MenuArrow";var[sT,aT]=wM("MenuSub"),lT="MenuSubTrigger",cT=qe.forwardRef(((e,t)=>{const n=CM(lT,e.__scopeMenu),r=AM(lT,e.__scopeMenu),o=aT(lT,e.__scopeMenu),i=LM(lT,e.__scopeMenu),s=qe.useRef(null),{pointerGraceTimerRef:a,onPointerGraceIntentChange:l}=i,c={__scopeMenu:e.__scopeMenu},u=qe.useCallback((()=>{s.current&&window.clearTimeout(s.current),s.current=null}),[]);return qe.useEffect((()=>u),[u]),qe.useEffect((()=>{const e=a.current;return()=>{window.clearTimeout(e),l(null)}}),[a,l]),nt.jsx(PM,{asChild:!0,...c,children:nt.jsx(qM,{id:o.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":o.contentId,"data-state":fT(n.open),...e,ref:v$(t,o.onTriggerChange),onClick:t=>{var r;null==(r=e.onClick)||r.call(e,t),e.disabled||t.defaultPrevented||(t.currentTarget.focus(),n.open||n.onOpenChange(!0))},onPointerMove:p$(e.onPointerMove,vT((t=>{i.onItemEnter(t),t.defaultPrevented||e.disabled||n.open||s.current||(i.onPointerGraceIntentChange(null),s.current=window.setTimeout((()=>{n.onOpenChange(!0),u()}),100))}))),onPointerLeave:p$(e.onPointerLeave,vT((e=>{var t,r;u();const o=null==(t=n.content)?void 0:t.getBoundingClientRect();if(o){const t=null==(r=n.content)?void 0:r.dataset.side,s="right"===t,l=s?-5:5,c=o[s?"left":"right"],u=o[s?"right":"left"];i.onPointerGraceIntentChange({area:[{x:e.clientX+l,y:e.clientY},{x:c,y:o.top},{x:u,y:o.top},{x:u,y:o.bottom},{x:c,y:o.bottom}],side:t}),window.clearTimeout(a.current),a.current=window.setTimeout((()=>i.onPointerGraceIntentChange(null)),300)}else{if(i.onTriggerLeave(e),e.defaultPrevented)return;i.onPointerGraceIntentChange(null)}}))),onKeyDown:p$(e.onKeyDown,(t=>{var o;const s=""!==i.searchRef.current;e.disabled||s&&" "===t.key||hM[r.dir].includes(t.key)&&(n.onOpenChange(!0),null==(o=n.content)||o.focus(),t.preventDefault())}))})})}));cT.displayName=lT;var uT="MenuSubContent",dT=qe.forwardRef(((e,t)=>{const n=OM(MM,e.__scopeMenu),{forceMount:r=n.forceMount,...o}=e,i=CM(MM,e.__scopeMenu),s=AM(MM,e.__scopeMenu),a=aT(uT,e.__scopeMenu),l=qe.useRef(null),c=m$(t,l);return nt.jsx(gM.Provider,{scope:e.__scopeMenu,children:nt.jsx(ZO,{present:r||i.open,children:nt.jsx(gM.Slot,{scope:e.__scopeMenu,children:nt.jsx(BM,{id:a.contentId,"aria-labelledby":a.triggerId,...o,ref:c,align:"start",side:"rtl"===s.dir?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:e=>{var t;s.isUsingKeyboardRef.current&&(null==(t=l.current)||t.focus()),e.preventDefault()},onCloseAutoFocus:e=>e.preventDefault(),onFocusOutside:p$(e.onFocusOutside,(e=>{e.target!==a.trigger&&i.onOpenChange(!1)})),onEscapeKeyDown:p$(e.onEscapeKeyDown,(e=>{s.onClose(),e.preventDefault()})),onKeyDown:p$(e.onKeyDown,(e=>{var t;const n=e.currentTarget.contains(e.target),r=vM[s.dir].includes(e.key);n&&r&&(i.onOpenChange(!1),null==(t=a.trigger)||t.focus(),e.preventDefault())}))})})})})}));function fT(e){return e?"open":"closed"}function pT(e){return"indeterminate"===e}function hT(e){return pT(e)?"indeterminate":e?"checked":"unchecked"}function vT(e){return t=>"mouse"===t.pointerType?e(t):void 0}dT.displayName=uT;var mT=NM,gT=PM,yT=RM,bT=IM,wT=UM,xT=FM,kT=WM,ET=KM,ST=JM,CT=ZM,_T=rT,AT=oT,NT=iT,PT=cT,$T=dT,jT="DropdownMenu",[OT,RT]=g$(jT,[xM]),MT=xM(),[TT,LT]=OT(jT),IT=e=>{const{__scopeDropdownMenu:t,children:n,dir:r,open:o,defaultOpen:i,onOpenChange:s,modal:a=!0}=e,l=MT(t),c=qe.useRef(null),[u=!1,d]=w$({prop:o,defaultProp:i,onChange:s});return nt.jsx(TT,{scope:t,triggerId:ej(),triggerRef:c,contentId:ej(),open:u,onOpenChange:d,onOpenToggle:qe.useCallback((()=>d((e=>!e))),[d]),modal:a,children:nt.jsx(mT,{...l,open:u,onOpenChange:d,dir:r,modal:a,children:n})})};IT.displayName=jT;var zT="DropdownMenuTrigger",DT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,disabled:r=!1,...o}=e,i=LT(zT,n),s=MT(n);return nt.jsx(gT,{asChild:!0,...s,children:nt.jsx(_$.button,{type:"button",id:i.triggerId,"aria-haspopup":"menu","aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":i.open?"open":"closed","data-disabled":r?"":void 0,disabled:r,...o,ref:v$(t,i.triggerRef),onPointerDown:p$(e.onPointerDown,(e=>{r||0!==e.button||!1!==e.ctrlKey||(i.onOpenToggle(),i.open||e.preventDefault())})),onKeyDown:p$(e.onKeyDown,(e=>{r||(["Enter"," "].includes(e.key)&&i.onOpenToggle(),"ArrowDown"===e.key&&i.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(e.key)&&e.preventDefault())}))})})}));DT.displayName=zT;var BT=e=>{const{__scopeDropdownMenu:t,...n}=e,r=MT(t);return nt.jsx(yT,{...r,...n})};BT.displayName="DropdownMenuPortal";var UT="DropdownMenuContent",FT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(UT,n),i=MT(n),s=qe.useRef(!1);return nt.jsx(bT,{id:o.contentId,"aria-labelledby":o.triggerId,...i,...r,ref:t,onCloseAutoFocus:p$(e.onCloseAutoFocus,(e=>{var t;s.current||null==(t=o.triggerRef.current)||t.focus(),s.current=!1,e.preventDefault()})),onInteractOutside:p$(e.onInteractOutside,(e=>{const t=e.detail.originalEvent,n=0===t.button&&!0===t.ctrlKey,r=2===t.button||n;o.modal&&!r||(s.current=!0)})),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})}));FT.displayName=UT;qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(wT,{...o,...r,ref:t})})).displayName="DropdownMenuGroup";var HT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(xT,{...o,...r,ref:t})}));HT.displayName="DropdownMenuLabel";var VT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(kT,{...o,...r,ref:t})}));VT.displayName="DropdownMenuItem";var WT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(ET,{...o,...r,ref:t})}));WT.displayName="DropdownMenuCheckboxItem";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(ST,{...o,...r,ref:t})})).displayName="DropdownMenuRadioGroup";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(CT,{...o,...r,ref:t})})).displayName="DropdownMenuRadioItem";var qT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(_T,{...o,...r,ref:t})}));qT.displayName="DropdownMenuItemIndicator";var KT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(AT,{...o,...r,ref:t})}));KT.displayName="DropdownMenuSeparator";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(NT,{...o,...r,ref:t})})).displayName="DropdownMenuArrow";var QT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx(PT,{...o,...r,ref:t})}));QT.displayName="DropdownMenuSubTrigger";var YT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=MT(n);return nt.jsx($T,{...o,...r,ref:t,style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})}));YT.displayName="DropdownMenuSubContent";var GT=IT,JT=DT,XT=BT,ZT=FT,eL=HT,tL=VT,nL=WT,rL=qT,oL=KT,iL=QT,sL=YT;const aL=(...e)=>e.filter(((e,t,n)=>Boolean(e)&&""!==e.trim()&&n.indexOf(e)===t)).join(" ").trim();var lL={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const cL=qe.forwardRef((({color:e="currentColor",size:t=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:o="",children:i,iconNode:s,...a},l)=>qe.createElement("svg",{ref:l,...lL,width:t,height:t,stroke:e,strokeWidth:r?24*Number(n)/Number(t):n,className:aL("lucide",o),...a},[...s.map((([e,t])=>qe.createElement(e,t))),...Array.isArray(i)?i:[i]]))),uL=(e,t)=>{const n=qe.forwardRef((({className:n,...r},o)=>{return qe.createElement(cL,{ref:o,iconNode:t,className:aL(`lucide-${i=e,i.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}`,n),...r});var i}));return n.displayName=`${e}`,n},dL=uL("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),fL=uL("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]),pL=GT,hL=JT;qe.forwardRef((({className:e,inset:t,children:n,...r},o)=>nt.jsxs(iL,{ref:o,className:ap("flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",t&&"pl-8",e),...r,children:[n,nt.jsx(fL,{className:"ml-auto h-4 w-4"})]}))).displayName=iL.displayName;qe.forwardRef((({className:e,...t},n)=>nt.jsx(sL,{ref:n,className:ap("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...t}))).displayName=sL.displayName;const vL=qe.forwardRef((({className:e,sideOffset:t=4,...n},r)=>nt.jsx(XT,{children:nt.jsx(ZT,{ref:r,sideOffset:t,className:ap("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})})));vL.displayName=ZT.displayName;const mL=qe.forwardRef((({className:e,inset:t,...n},r)=>nt.jsx(tL,{ref:r,className:ap("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",t&&"pl-8",e),...n})));mL.displayName=tL.displayName;qe.forwardRef((({className:e,children:t,checked:n,...r},o)=>nt.jsxs(nL,{ref:o,className:ap("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),checked:n,...r,children:[nt.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:nt.jsx(rL,{children:nt.jsx(dL,{className:"h-4 w-4"})})}),t]}))).displayName=nL.displayName;qe.forwardRef((({className:e,inset:t,...n},r)=>nt.jsx(eL,{ref:r,className:ap("px-2 py-1.5 text-sm font-semibold",t&&"pl-8",e),...n}))).displayName=eL.displayName;const gL=qe.forwardRef((({className:e,...t},n)=>nt.jsx(oL,{ref:n,className:ap("-mx-1 my-1 h-px bg-muted",e),...t})));gL.displayName=oL.displayName;const yL=new Set(["children","localName","ref","style","className"]),bL=new WeakMap,wL=(e,t,n,r,o)=>{const i=null==o?void 0:o[t];void 0===i?(e[t]=n,null==n&&t in HTMLElement.prototype&&e.removeAttribute(t)):n!==r&&((e,t,n)=>{let r=bL.get(e);void 0===r&&bL.set(e,r=new Map);let o=r.get(t);void 0!==n?void 0===o?(r.set(t,o={handleEvent:n}),e.addEventListener(t,o)):o.handleEvent=n:void 0!==o&&(r.delete(t),e.removeEventListener(t,o))})(e,i,n)},xL=({react:e,tagName:t,elementClass:n,events:r,displayName:o})=>{const i=new Set(Object.keys(r??{})),s=e.forwardRef(((o,s)=>{const a=e.useRef(new Map),l=e.useRef(null),c={},u={};for(const[e,t]of Object.entries(o))yL.has(e)?c["className"===e?"class":e]=t:i.has(e)||e in n.prototype?u[e]=t:c[e]=t;return e.useLayoutEffect((()=>{if(null===l.current)return;const e=new Map;for(const t in u)wL(l.current,t,o[t],a.current.get(t),r),a.current.delete(t),e.set(t,o[t]);for(const[t,n]of a.current)wL(l.current,t,void 0,n,r);a.current=e})),e.useLayoutEffect((()=>{var e;null==(e=l.current)||e.removeAttribute("defer-hydration")}),[]),c.suppressHydrationWarning=!0,e.createElement(t,{...c,ref:e.useCallback((e=>{l.current=e,"function"==typeof s?s(e):null!==s&&(s.current=e)}),[s])})}));return s.displayName=o??n.name,s};xL({tagName:"dc-connection-button",elementClass:d$,react:Ke});const kL=xL({tagName:"dc-connection-dialog",elementClass:o$,react:Ke,events:{onClose:"close"}});function EL(...e){const t=e=>e,n=(e,t)=>n=>e(t(n));return{encode:e.map((e=>e.encode)).reduceRight(n,t),decode:e.map((e=>e.decode)).reduce(n,t)}}function SL(e){return{encode:t=>{if(!Array.isArray(t)||t.length&&"number"!=typeof t[0])throw new Error("alphabet.encode input should be an array of numbers");return t.map((t=>{if(t<0||t>=e.length)throw new Error(`Digit index outside alphabet: ${t} (alphabet: ${e.length})`);return e[t]}))},decode:t=>{if(!Array.isArray(t)||t.length&&"string"!=typeof t[0])throw new Error("alphabet.decode input should be array of strings");return t.map((t=>{if("string"!=typeof t)throw new Error(`alphabet.decode: not string element=${t}`);const n=e.indexOf(t);if(-1===n)throw new Error(`Unknown letter: "${t}". Allowed: ${e}`);return n}))}}}function CL(e=""){if("string"!=typeof e)throw new Error("join separator should be string");return{encode:t=>{if(!Array.isArray(t)||t.length&&"string"!=typeof t[0])throw new Error("join.encode input should be array of strings");for(let e of t)if("string"!=typeof e)throw new Error(`join.encode: non-string input=${e}`);return t.join(e)},decode:t=>{if("string"!=typeof t)throw new Error("join.decode input should be string");return t.split(e)}}}function _L(e,t,n){if(t<2)throw new Error(`convertRadix: wrong from=${t}, base cannot be less than 2`);if(n<2)throw new Error(`convertRadix: wrong to=${n}, base cannot be less than 2`);if(!Array.isArray(e))throw new Error("convertRadix: data should be array");if(!e.length)return[];let r=0;const o=[],i=Array.from(e);for(i.forEach((e=>{if(e<0||e>=t)throw new Error(`Wrong integer: ${e}`)}));;){let e=0,s=!0;for(let o=r;o{if(!function(e){return e instanceof Uint8Array||null!=e&&"object"==typeof e&&"Uint8Array"===e.constructor.name}(t))throw new Error("radix.encode input should be Uint8Array");return _L(Array.from(t),256,e)},decode:t=>{if(!Array.isArray(t)||t.length&&"number"!=typeof t[0])throw new Error("radix.decode input should be array of numbers");return Uint8Array.from(_L(t,e,256))}}}const NL=(e=>EL(AL(58),SL(e),CL("")))("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");function PL(e){if(!Number.isSafeInteger(e)||e<0)throw new Error(`positive integer expected, not ${e}`)}function $L(e,...t){if(!function(e){return e instanceof Uint8Array||null!=e&&"object"==typeof e&&"Uint8Array"===e.constructor.name}(e))throw new Error("Uint8Array expected");if(t.length>0&&!t.includes(e.length))throw new Error(`Uint8Array expected of length ${t}, not of length=${e.length}`)}function jL(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}const OL=e=>new Uint32Array(e.buffer,e.byteOffset,Math.floor(e.byteLength/4)),RL=68===new Uint8Array(new Uint32Array([287454020]).buffer)[0],ML=e=>e<<24&4278190080|e<<8&16711680|e>>>8&65280|e>>>24&255,TL=RL?e=>e:e=>ML(e);function LL(e){for(let t=0;te(n).update(IL(t)).digest(),n=e({});return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=t=>e(t),t}const BL=new Uint8Array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3,11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4,7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8,9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13,2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9,12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11,13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10,6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5,10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3]);class UL extends zL{constructor(e,t,n={},r,o,i){if(super(),this.blockLen=e,this.outputLen=t,this.length=0,this.pos=0,this.finished=!1,this.destroyed=!1,PL(e),PL(t),PL(r),t<0||t>r)throw new Error("outputLen bigger than keyLen");if(void 0!==n.key&&(n.key.length<1||n.key.length>r))throw new Error(`key must be up 1..${r} byte long or undefined`);if(void 0!==n.salt&&n.salt.length!==o)throw new Error(`salt must be ${o} byte long or undefined`);if(void 0!==n.personalization&&n.personalization.length!==i)throw new Error(`personalization must be ${i} byte long or undefined`);this.buffer32=OL(this.buffer=new Uint8Array(e))}update(e){jL(this);const{blockLen:t,buffer:n,buffer32:r}=this,o=(e=IL(e)).length,i=e.byteOffset,s=e.buffer;for(let a=0;ar[t]=TL(e)))}digest(){const{buffer:e,outputLen:t}=this;this.digestInto(e);const n=e.slice(0,t);return this.destroy(),n}_cloneInto(e){const{buffer:t,length:n,finished:r,destroyed:o,outputLen:i,pos:s}=this;return e||(e=new this.constructor({dkLen:i})),e.set(...this.get()),e.length=n,e.finished=r,e.destroyed=o,e.outputLen=i,e.buffer.set(t),e.pos=s,e}}const FL=BigInt(2**32-1),HL=BigInt(32);function VL(e,t=!1){return t?{h:Number(e&FL),l:Number(e>>HL&FL)}:{h:0|Number(e>>HL&FL),l:0|Number(e&FL)}}const WL={fromBig:VL,split:function(e,t=!1){let n=new Uint32Array(e.length),r=new Uint32Array(e.length);for(let o=0;oBigInt(e>>>0)<>>0),shrSH:(e,t,n)=>e>>>n,shrSL:(e,t,n)=>e<<32-n|t>>>n,rotrSH:(e,t,n)=>e>>>n|t<<32-n,rotrSL:(e,t,n)=>e<<32-n|t>>>n,rotrBH:(e,t,n)=>e<<64-n|t>>>n-32,rotrBL:(e,t,n)=>e>>>n-32|t<<64-n,rotr32H:(e,t)=>t,rotr32L:(e,t)=>e,rotlSH:(e,t,n)=>e<>>32-n,rotlSL:(e,t,n)=>t<>>32-n,rotlBH:(e,t,n)=>t<>>64-n,rotlBL:(e,t,n)=>e<>>64-n,add:function(e,t,n,r){const o=(t>>>0)+(r>>>0);return{h:e+n+(o/2**32|0)|0,l:0|o}},add3L:(e,t,n)=>(e>>>0)+(t>>>0)+(n>>>0),add3H:(e,t,n,r)=>t+n+r+(e/2**32|0)|0,add4L:(e,t,n,r)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0),add4H:(e,t,n,r,o)=>t+n+r+o+(e/2**32|0)|0,add5H:(e,t,n,r,o,i)=>t+n+r+o+i+(e/2**32|0)|0,add5L:(e,t,n,r,o)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0)+(o>>>0)},qL=new Uint32Array([4089235720,1779033703,2227873595,3144134277,4271175723,1013904242,1595750129,2773480762,2917565137,1359893119,725511199,2600822924,4215389547,528734635,327033209,1541459225]),KL=new Uint32Array(32);function QL(e,t,n,r,o,i){const s=o[i],a=o[i+1];let l=KL[2*e],c=KL[2*e+1],u=KL[2*t],d=KL[2*t+1],f=KL[2*n],p=KL[2*n+1],h=KL[2*r],v=KL[2*r+1],m=WL.add3L(l,u,s);c=WL.add3H(m,c,d,a),l=0|m,({Dh:v,Dl:h}={Dh:v^c,Dl:h^l}),({Dh:v,Dl:h}={Dh:WL.rotr32H(v,h),Dl:WL.rotr32L(v,h)}),({h:p,l:f}=WL.add(p,f,v,h)),({Bh:d,Bl:u}={Bh:d^p,Bl:u^f}),({Bh:d,Bl:u}={Bh:WL.rotrSH(d,u,24),Bl:WL.rotrSL(d,u,24)}),KL[2*e]=l,KL[2*e+1]=c,KL[2*t]=u,KL[2*t+1]=d,KL[2*n]=f,KL[2*n+1]=p,KL[2*r]=h,KL[2*r+1]=v}function YL(e,t,n,r,o,i){const s=o[i],a=o[i+1];let l=KL[2*e],c=KL[2*e+1],u=KL[2*t],d=KL[2*t+1],f=KL[2*n],p=KL[2*n+1],h=KL[2*r],v=KL[2*r+1],m=WL.add3L(l,u,s);c=WL.add3H(m,c,d,a),l=0|m,({Dh:v,Dl:h}={Dh:v^c,Dl:h^l}),({Dh:v,Dl:h}={Dh:WL.rotrSH(v,h,16),Dl:WL.rotrSL(v,h,16)}),({h:p,l:f}=WL.add(p,f,v,h)),({Bh:d,Bl:u}={Bh:d^p,Bl:u^f}),({Bh:d,Bl:u}={Bh:WL.rotrBH(d,u,63),Bl:WL.rotrBL(d,u,63)}),KL[2*e]=l,KL[2*e+1]=c,KL[2*t]=u,KL[2*t+1]=d,KL[2*n]=f,KL[2*n+1]=p,KL[2*r]=h,KL[2*r+1]=v}class GL extends UL{constructor(e={}){super(128,void 0===e.dkLen?64:e.dkLen,e,64,16,16),this.v0l=0|qL[0],this.v0h=0|qL[1],this.v1l=0|qL[2],this.v1h=0|qL[3],this.v2l=0|qL[4],this.v2h=0|qL[5],this.v3l=0|qL[6],this.v3h=0|qL[7],this.v4l=0|qL[8],this.v4h=0|qL[9],this.v5l=0|qL[10],this.v5h=0|qL[11],this.v6l=0|qL[12],this.v6h=0|qL[13],this.v7l=0|qL[14],this.v7h=0|qL[15];const t=e.key?e.key.length:0;if(this.v0l^=this.outputLen|t<<8|65536|1<<24,e.salt){const t=OL(IL(e.salt));this.v4l^=TL(t[0]),this.v4h^=TL(t[1]),this.v5l^=TL(t[2]),this.v5h^=TL(t[3])}if(e.personalization){const t=OL(IL(e.personalization));this.v6l^=TL(t[0]),this.v6h^=TL(t[1]),this.v7l^=TL(t[2]),this.v7h^=TL(t[3])}if(e.key){const t=new Uint8Array(this.blockLen);t.set(IL(e.key)),this.update(t)}}get(){let{v0l:e,v0h:t,v1l:n,v1h:r,v2l:o,v2h:i,v3l:s,v3h:a,v4l:l,v4h:c,v5l:u,v5h:d,v6l:f,v6h:p,v7l:h,v7h:v}=this;return[e,t,n,r,o,i,s,a,l,c,u,d,f,p,h,v]}set(e,t,n,r,o,i,s,a,l,c,u,d,f,p,h,v){this.v0l=0|e,this.v0h=0|t,this.v1l=0|n,this.v1h=0|r,this.v2l=0|o,this.v2h=0|i,this.v3l=0|s,this.v3h=0|a,this.v4l=0|l,this.v4h=0|c,this.v5l=0|u,this.v5h=0|d,this.v6l=0|f,this.v6h=0|p,this.v7l=0|h,this.v7h=0|v}compress(e,t,n){this.get().forEach(((e,t)=>KL[t]=e)),KL.set(qL,16);let{h:r,l:o}=WL.fromBig(BigInt(this.length));KL[24]=qL[8]^o,KL[25]=qL[9]^r,n&&(KL[28]=~KL[28],KL[29]=~KL[29]);let i=0;const s=BL;for(let a=0;a<12;a++)QL(0,4,8,12,e,t+2*s[i++]),YL(0,4,8,12,e,t+2*s[i++]),QL(1,5,9,13,e,t+2*s[i++]),YL(1,5,9,13,e,t+2*s[i++]),QL(2,6,10,14,e,t+2*s[i++]),YL(2,6,10,14,e,t+2*s[i++]),QL(3,7,11,15,e,t+2*s[i++]),YL(3,7,11,15,e,t+2*s[i++]),QL(0,5,10,15,e,t+2*s[i++]),YL(0,5,10,15,e,t+2*s[i++]),QL(1,6,11,12,e,t+2*s[i++]),YL(1,6,11,12,e,t+2*s[i++]),QL(2,7,8,13,e,t+2*s[i++]),YL(2,7,8,13,e,t+2*s[i++]),QL(3,4,9,14,e,t+2*s[i++]),YL(3,4,9,14,e,t+2*s[i++]);this.v0l^=KL[0]^KL[16],this.v0h^=KL[1]^KL[17],this.v1l^=KL[2]^KL[18],this.v1h^=KL[3]^KL[19],this.v2l^=KL[4]^KL[20],this.v2h^=KL[5]^KL[21],this.v3l^=KL[6]^KL[22],this.v3h^=KL[7]^KL[23],this.v4l^=KL[8]^KL[24],this.v4h^=KL[9]^KL[25],this.v5l^=KL[10]^KL[26],this.v5h^=KL[11]^KL[27],this.v6l^=KL[12]^KL[28],this.v6h^=KL[13]^KL[29],this.v7l^=KL[14]^KL[30],this.v7h^=KL[15]^KL[31],KL.fill(0)}destroy(){this.destroyed=!0,this.buffer32.fill(0),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}}const JL=DL((e=>new GL(e))),XL=(new TextEncoder).encode("SS58PRE"),ZL=e=>{try{const t=NL.decode(e),n=t.subarray(0,64&t[0]?2:1),r=t.subarray(n.length,t.length-2),o=t.subarray(n.length+r.length),i=JL(Uint8Array.of(...XL,...n,...r),{dkLen:64}).subarray(0,2);return o[0]===i[0]&&o[1]===i[1]?{isValid:!0,ss58Format:eI(n),publicKey:r.slice()}:{isValid:!1}}catch{return{isValid:!1}}},eI=e=>{const t=new DataView(e.buffer,e.byteOffset,e.byteLength);return 1===t.byteLength?t.getUint8(0):t.getUint16(0)},tI={target:{colors:[0,28,0,0,28,0,0,28,0,0,28,0,0,28,0,0,28,0,1],freq:1},cube:{colors:[0,1,3,2,4,3,0,1,3,2,4,3,0,1,3,2,4,3,5],freq:20},quazar:{colors:[1,2,3,1,2,4,5,5,4,1,2,3,1,2,4,5,5,4,0],freq:16},flower:{colors:[0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,3],freq:32},cyclic:{colors:[0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,6],freq:32},vmirror:{colors:[0,1,2,3,4,5,3,4,2,0,1,6,7,8,9,7,8,6,10],freq:128},hmirror:{colors:[0,1,2,3,4,5,6,7,8,9,10,8,6,7,5,3,4,2,11],freq:128}},nI=e=>{let t=0;const n=Object.values(tI).find((n=>(t+=n.freq,e{var l;const[c,u]=qe.useState(!1),[d,f]=qe.useState(i),[p,h]=qe.useState(),[v,m]=qe.useState(),[g,y]=qe.useState();qe.useEffect((()=>{if("string"==typeof e&&!e.includes("px")&&!e.includes("rem"))throw new Error("Providing a string for 'size' in Polkicon should be expressed either in 'px', 'rem' or 'em'");let t,n;if("string"==typeof e)switch(n=e.replace(/[0-9.]/g,""),n){case"px":t=parseFloat(e);break;case"rem":t=10*parseFloat(e)}else"number"==typeof e&&(t=e);var r,o;h(n?""+("px"===n?t+"px":t/10+"rem"):t),t<12&&(r=n||"number",o="px"===n?"12px":"rem"===n?"1.2rem":12,console.warn(`Polkicon: 'Size' expressed in '${r}' cannot be less than ${o}. Will be resized to minimum size.`)),t<32?(y("0rem 0.5rem"),m("0.5rem")):t>=32&&t<64?(y("1rem 0.5rem"),m("1rem")):t>=64&&t<100?(y("2rem 1rem"),m("1.5rem")):t>=100&&(y("3rem 1rem"),m("2rem"))}),[e]);const b=qe.useCallback((()=>{n&&(async e=>{try{await navigator.clipboard.writeText(e),u(!0),f(i)}catch{u(!0),f("Failed!")}})(t)}),[n,t,i]);qe.useEffect((()=>{n&&c&&setTimeout((()=>{u(!1)}),o)}),[n,c,o]);const{c:w,r:x,rroot3o2:k,ro2:E,rroot3o4:S,ro4:C,r3o4:_,z:A,rot:N,scheme:P,palette:$}=(e=>{const t=JL(new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])),n=ZL(e);let r=n.isValid?n.publicKey:e;if(!("object"==typeof r&&r&&r instanceof Uint8Array&&32==r.length))return{};r=Uint8Array.from(JL(r)).map(((e,n)=>(e+256-t[n])%256));const o=22.4,i=o*Math.sqrt(3)/2,s=o*Math.sqrt(3)/4,a=3*o/4,l=Object.keys(tI).map((e=>tI[e].freq)).reduce(((e,t)=>e+t)),c=Math.floor(70*r[29]/256+26)%80+30,u=Math.floor((r[30]+256*r[31])%l),d=nI(u),f=Array.from(r).map(((e,t)=>{const n=(e+t%28*58)%256;if(0==n)return"#444";if(255==n)return"transparent";const r=Math.floor(n%64*360/64),o=[53,15,35,75][Math.floor(n/64)];return`hsl(${r}, ${c}%, ${o}%)`}));return{c:32,r:o,rroot3o2:i,ro2:11.2,rroot3o4:s,ro4:5.6,r3o4:a,z:5,rot:r[28]%6*3,scheme:d,palette:f}})(t),j=ZL(t).isValid?null==(l=null==P?void 0:P.colors)?void 0:l.map(((e,t)=>$[null==P?void 0:P.colors[t<18?(t+N)%18:18]])):[];let O=0;return j?nt.jsxs("div",{onClick:n?b:void 0,style:n?{cursor:c?"none":"copy",position:"relative",display:"flex",justifyContent:"center",alignItems:"center"}:{display:"flex",justifyContent:"center",alignItems:"center"},children:[nt.jsxs("svg",{id:Math.random().toString(36).substring(2,9),className:a,style:s,width:p,height:p,viewBox:"0 0 64 64",children:[nt.jsx("circle",{cx:32,cy:32,r:32,fill:r}),nt.jsx("circle",{cx:w,cy:w-x,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w,cy:w-E,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-S,cy:w-_,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-k,cy:w-E,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-S,cy:w-C,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-k,cy:w,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-k,cy:w+E,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-S,cy:w+C,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w-S,cy:w+_,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w,cy:w+x,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w,cy:w+E,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+S,cy:w+_,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+k,cy:w+E,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+S,cy:w+C,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+k,cy:w,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+k,cy:w-E,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+S,cy:w-C,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w+S,cy:w-_,r:A,fill:j[O++]}),nt.jsx("circle",{cx:w,cy:w,r:A,fill:j[O++]})]}),n&&c&&nt.jsx("p",{style:{fontSize:v,fontWeight:"bold",padding:g,width:p,height:p,position:"absolute",borderRadius:"55rem",color:"white",background:"green",opacity:"80%",alignItems:"center",justifyContent:"center",display:"flex"},children:d})]}):null},oI=()=>{const{accounts:e,selectAccount:t,selectedAccount:n}=QS(),[,r]=DS(),[o,i]=qe.useState(!1);return qe.useEffect((()=>{!(null==n?void 0:n.address)&&e.length>0&&(t(e[0]),i(!1))}),[e,t,null==n?void 0:n.address]),nt.jsxs(nt.Fragment,{children:[nt.jsx("div",{className:"flex w-full justify-center",children:nt.jsxs("div",{className:"flex w-full",children:[!e.length&&nt.jsx(aC,{onClick:()=>i(!0),className:"w-full text-lg font-bold bg-pink-700 hover:bg-blue-600",children:"Connect Wallet"}),!!e.length&&nt.jsxs(pL,{children:[nt.jsx(hL,{asChild:!0,children:nt.jsxs(aC,{variant:"outline",size:"default",className:"cursor-pointer overflow-hidden w-full flex items-center justify-center gap-2",children:[nt.jsx(rI,{size:36,address:(null==n?void 0:n.address)||"",className:"mr-2",outerColor:"transparent"}),null==n?void 0:n.name,nt.jsx(cC,{className:"ml-2 h-4 w-4",isOpen:!1})]})}),nt.jsxs(vL,{className:"max-h-[calc(100vh-5rem)] overflow-auto",children:[e.map(((n,r)=>nt.jsxs(qe.Fragment,{children:[nt.jsxs(mL,{className:"cursor-pointer",onClick:()=>t(n),children:[nt.jsx(rI,{size:28,address:n.address||"",className:"mr-2",outerColor:"transparent"}),n.name]},n.address),r!==e.length-1&&nt.jsx(gL,{})]},n.address))),nt.jsx(mL,{className:"cursor-pointer",onClick:()=>{i(!0)},children:"Show wallets"},"show"),nt.jsx(mL,{className:"cursor-pointer",onClick:()=>{r(),t(void 0)},children:"Disconnect"},"logout")]})]})]})}),nt.jsx(kL,{open:o,onClose:()=>i(!1)})]})};function iI(){return nt.jsx("div",{className:"flex items-center justify-center mt-6",children:nt.jsxs(up,{className:"w-full max-w-xl",children:[nt.jsx(dp,{children:nt.jsxs(fp,{className:"text-4xl text-center flex items-center justify-center gap-2",children:[nt.jsx(sI,{}),nt.jsxs("span",{children:["CLI: ",nt.jsx("span",{className:"font-light",children:"Signing Portal"})]})]})}),nt.jsx(pp,{className:"grid gap-4",children:nt.jsx(PS,{config:f$,children:nt.jsx(KS,{children:nt.jsxs("div",{className:"overflow-auto break-words whitespace-pre-wrap",children:[nt.jsx(oI,{}),nt.jsx(yC,{})]})})})})]})})}const sI=()=>nt.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"100",height:"100",fill:"none",viewBox:"0 0 512 512",children:[nt.jsxs("g",{clipPath:"url(#clip0_873_174)",children:[nt.jsx("rect",{width:"512",height:"512",fill:"#1C0533",rx:"256"}),nt.jsx("rect",{width:"512",height:"512",fill:"url(#paint0_radial_873_174)",fillOpacity:"0.8",rx:"256"}),nt.jsx("rect",{width:"512",height:"512",fill:"url(#paint1_radial_873_174)",fillOpacity:"0.6",rx:"256"}),nt.jsx("mask",{id:"mask0_873_174",width:"1428",height:"1351",x:"-429",y:"-502",maskUnits:"userSpaceOnUse",style:{maskType:"alpha"},children:nt.jsx("path",{fill:"#D9D9D9",d:"m127.637-501.289 871.274 824.192-14.25 13.48-871.273-824.192zM88.96-464.701l871.272 824.192-14.249 13.48L74.71-451.221zM50.281-428.113l871.273 824.192-14.249 13.48L36.032-414.633zM11.603-391.525l871.273 824.192-14.249 13.48L-2.646-378.045zM-27.075-354.937l871.273 824.192-14.249 13.48-871.273-824.192zM-65.753-318.349 805.52 505.843l-14.249 13.48-871.273-824.192zM-104.431-281.761l871.273 824.192-14.249 13.48-871.273-824.192zM-143.109-245.173l871.273 824.192-14.249 13.48-871.273-824.192zM-181.787-208.585l871.273 824.192-14.249 13.48-871.273-824.192zM-220.465-171.997l871.273 824.192-14.249 13.48-871.273-824.192zM-259.143-135.409 612.13 688.783l-14.249 13.48-871.273-824.192zM-297.821-98.821 573.452 725.37l-14.249 13.48L-312.07-85.341zM-336.499-62.233l871.273 824.192-14.249 13.48-871.273-824.192zM-375.177-25.645l871.273 824.192-14.249 13.479-871.273-824.191zM-413.855 10.943l871.273 824.192-14.249 13.48-871.273-824.192z"})}),nt.jsx("g",{mask:"url(#mask0_873_174)",children:nt.jsx("path",{fill:"#E6007A",d:"M511.169 254.929C511.169 396.905 396.739 512 255.584 512 114.428 512-.001 396.905-.001 254.929S114.428-2.142 255.584-2.142c141.155 0 255.585 115.095 255.585 257.071"})}),nt.jsx("path",{fill:"#2B0532",d:"M183.804 160.44c-12.752-9.296-27.346-13.272-41.94-13.272h-89.63L5.63 367.567h117.053l9.399-44.802a86.4 86.4 0 0 0 28.32-8.371 104 104 0 0 0 4.133-2.155 97.7 97.7 0 0 0 14.779 24.342c8.258 9.967 18.342 17.785 29.802 23.435l.003.002.397.195.4.186c12.688 5.89 26.181 8.385 39.713 8.385 13.588 0 26.877-2.295 39.662-6.916a127 127 0 0 0 17.54-7.88l-2.871 13.579h117.052l9.4-44.802a86.4 86.4 0 0 0 28.32-8.371 101.4 101.4 0 0 0 28.939-21.348l.01-.011.159-.168.154-.167.003-.003c8.092-8.766 14.613-18.702 19.34-29.722 4.865-11.344 7.542-23.439 7.542-36.022 0-12.506-2.649-24.626-7.879-35.976-5.493-12.224-13.86-22.642-24.867-30.537-12.752-9.296-27.346-13.272-41.94-13.272h-89.631l-5.24 24.781c-7.026-6.908-15.096-12.504-24.009-16.758-12.764-6.233-26.404-8.834-40.034-8.834-13.589 0-26.878 2.295-39.662 6.916-12.145 4.39-23.444 10.507-33.804 18.268a133 133 0 0 0-7.099 5.68 74.6 74.6 0 0 0-16.911-16.781"}),nt.jsx("path",{fill:"#E6007A",fillRule:"evenodd",d:"m229.569 331.163.063.031.064.03c7.896 3.665 16.711 5.426 26.323 5.426 9.96 0 19.59-1.672 28.858-5.022a94.6 94.6 0 0 0 25.4-13.857c7.69-5.873 14.403-12.621 20.12-20.24l.016-.022.016-.022c5.708-7.75 10.156-16.036 13.319-24.849 3.326-9.027 5.005-18.25 5.005-27.639 0-8.458-1.303-16.601-3.928-24.399-2.514-8.002-6.373-15.177-11.585-21.468-5.277-6.369-11.756-11.363-19.365-14.972-7.797-3.835-16.58-5.669-26.205-5.669-9.961 0-19.591 1.673-28.858 5.022-9.165 3.313-17.687 7.924-25.551 13.819-7.719 5.752-14.51 12.512-20.362 20.265l-.01.014a97.4 97.4 0 0 0-13.544 24.867l-.015.04-.014.041c-3.181 9.016-4.784 18.219-4.784 27.583 0 8.482 1.311 16.702 3.94 24.634l.005.016.005.016c2.653 7.855 6.575 14.936 11.766 21.201 5.269 6.361 11.733 11.413 19.321 15.154m58.163-55.148.01-.015c2.998-4.343 5.197-8.955 6.622-13.852 1.467-5.04 2.176-9.872 2.176-14.513 0-4.335-.697-8.177-2.012-11.592l-.041-.109-.038-.11c-1.121-3.279-2.866-5.693-5.197-7.475-2.026-1.548-5.053-2.613-9.646-2.613-5.335 0-9.796 1.243-13.579 3.569l-.053.032-.053.031c-4.052 2.378-7.426 5.537-10.156 9.541a52.3 52.3 0 0 0-6.661 13.745c-1.454 5.016-2.157 9.826-2.157 14.447 0 4.358.638 8.321 1.862 11.932 1.246 3.306 3.081 5.852 5.484 7.803 2.047 1.543 5.008 2.569 9.387 2.569 5.495 0 10.017-1.255 13.78-3.568 4.107-2.524 7.515-5.779 10.262-9.808zM171.542 186.53c-6.663-4.89-14.525-7.228-23.288-7.228H84.491L51.478 335.434h51.685l9.273-44.204h15.658c8.698 0 16.935-1.893 24.636-5.69a69.4 69.4 0 0 0 19.864-14.652l.025-.027.025-.027c5.695-6.169 10.18-13.036 13.418-20.587 3.261-7.602 4.93-15.381 4.93-23.294 0-7.901-1.665-15.457-4.983-22.605-3.247-7.286-8.082-13.265-14.467-17.818m-35.967 55.601.023-.03c.765-1.001 1.462-2.391 1.969-4.328l.037-.142.044-.141c.574-1.827.929-4.221.929-7.293 0-2.333-.311-3.426-.466-3.769a4 4 0 0 0-.222-.434l-.027-.045-.013-.01h-11.5l-3.905 18.655h10.249c.063-.013.306-.078.757-.35.511-.309 1.235-.919 2.103-2.084zM469.872 186.53c-6.663-4.89-14.525-7.228-23.289-7.228h-63.762l-33.014 156.132h51.686l9.273-44.204h15.658c8.698 0 16.935-1.893 24.636-5.69a69.4 69.4 0 0 0 19.863-14.652l.026-.027.024-.027c5.696-6.169 10.18-13.036 13.419-20.587 3.26-7.602 4.93-15.381 4.93-23.294 0-7.901-1.665-15.457-4.983-22.605-3.247-7.286-8.082-13.265-14.467-17.818m-35.967 55.601.023-.03c.765-1.001 1.462-2.391 1.968-4.328l.038-.142.044-.141c.574-1.827.929-4.221.929-7.293 0-2.333-.311-3.426-.466-3.769a4 4 0 0 0-.222-.434l-.027-.045-.014-.01h-11.5l-3.904 18.655h10.249c.063-.013.306-.078.756-.35.511-.309 1.235-.919 2.104-2.084z",clipRule:"evenodd"}),nt.jsxs("mask",{id:"path-6-outside-1_873_174",width:"435",height:"156",x:"35.823",y:"166.788",fill:"#000",maskUnits:"userSpaceOnUse",children:[nt.jsx("path",{fill:"#fff",d:"M35.823 166.788h435v156h-435z"}),nt.jsx("path",{fillRule:"evenodd",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318m-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391m-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852m133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd"})]}),nt.jsx("path",{fill:"#fff",fillRule:"evenodd",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318m-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391m-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852m133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd"}),nt.jsx("path",{stroke:"#fff",strokeWidth:"0.3",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318Zm-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391Zm-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852Zm133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd",mask:"url(#path-6-outside-1_873_174)"})]}),nt.jsxs("defs",{children:[nt.jsxs("radialGradient",{id:"paint0_radial_873_174",cx:"0",cy:"0",r:"1",gradientTransform:"matrix(229.99965 647.20057 -371.3092 131.95444 -62.8 -86.4)",gradientUnits:"userSpaceOnUse",children:[nt.jsx("stop",{stopColor:"#6335EC"}),nt.jsx("stop",{offset:"1",stopOpacity:"0"})]}),nt.jsxs("radialGradient",{id:"paint1_radial_873_174",cx:"0",cy:"0",r:"1",gradientTransform:"rotate(-116.448 530.068 140.453)scale(1055.25 662.094)",gradientUnits:"userSpaceOnUse",children:[nt.jsx("stop",{stopColor:"#E6007A"}),nt.jsx("stop",{offset:"1",stopOpacity:"0"})]}),nt.jsx("clipPath",{id:"clip0_873_174",children:nt.jsx("rect",{width:"512",height:"512",fill:"#fff",rx:"256"})})]})]});rt.createRoot(document.getElementById("root")).render(nt.jsx(Ke.StrictMode,{children:nt.jsx(iI,{})})); From cbf722a7d3248a5a32cc7ca7abb4fa50cd57f559 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Thu, 19 Dec 2024 18:39:21 +0100 Subject: [PATCH 007/117] chore: release v0.6.0 (#394) * chore: bump version * chore: update changelog * chore: remove build deprecated options * chore: remove e2e test deprecated * chore: remove deprecated templates * fix: include latest PR in CHANGELOG * refactor: remove unnecesary macros and Options * chore: update CHANGELOG * refactor: rename BuildContractCommand and BuildParachainCommand * refactor: subcommand comment * refactor: nitpicks comment fixes --- CHANGELOG.md | 31 ++++ Cargo.lock | 165 ++++++++++-------- Cargo.toml | 2 +- crates/pop-cli/Cargo.toml | 8 +- crates/pop-cli/src/commands/build/contract.rs | 50 ++---- crates/pop-cli/src/commands/build/mod.rs | 32 +--- .../pop-cli/src/commands/build/parachain.rs | 87 ++++----- crates/pop-cli/src/commands/mod.rs | 4 - crates/pop-cli/src/commands/test/contract.rs | 17 -- crates/pop-contracts/Cargo.toml | 2 +- crates/pop-parachains/Cargo.toml | 2 +- crates/pop-parachains/src/new_parachain.rs | 7 +- crates/pop-parachains/src/templates.rs | 21 --- 13 files changed, 179 insertions(+), 249 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f3099bc8..099453ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ All notable changes to this project will be documented in this file. +## [0.6.0] - 2024-12-19 + +### 🚀 Features + +- Wallet integration (#371) +- Guide user to call a chain (#316) +- Guide user to call a contract (#306) +- Output events after calling a chain (#372) +- Add remark action (#387) + +### 🐛 Fixes + +- Build spec experience (#331) +- HRMP channels (#278) + +### 🚜 Refactor + +- Ensure short args consistency (#386) +- Bump fallback versions (#393) + +### ⚙️ Miscellaneous Tasks + +- Bump zombienet version to `v0.2.18` (#352) +- Set msrv (#385) +- Replace rococo to paseo name (#333) +- Replace codeowners (#388) + +### Build + +- *(deps)* Remove unused dependencies (#369) + ## [0.5.0] - 2024-11-08 ### 🚀 Features diff --git a/Cargo.lock b/Cargo.lock index 48e49894a..3e3981ecf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,7 +951,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "itoa", "matchit", @@ -1247,7 +1247,7 @@ dependencies = [ "hex", "http 1.2.0", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-named-pipe", "hyper-util", "hyperlocal", @@ -1258,7 +1258,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.6", + "thiserror 2.0.8", "tokio", "tokio-util", "tower-service", @@ -1720,7 +1720,7 @@ dependencies = [ "semver 1.0.24", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.8", ] [[package]] @@ -1878,12 +1878,12 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1913,15 +1913,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "unicode-width 0.1.14", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -2113,7 +2113,7 @@ dependencies = [ "serde", "serde_json", "strsim 0.11.1", - "thiserror 2.0.6", + "thiserror 2.0.8", "tracing", ] @@ -2275,9 +2275,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -2294,18 +2294,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -2727,9 +2727,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a32d755fe20281b46118ee4b507233311fb7a48a0cfd42f554b93640521a2f" +checksum = "4d44ff199ff93242c3afe480ab588d544dd08d72e92885e152ffebc670f076ad" dependencies = [ "cc", "cxxbridge-cmd", @@ -2741,9 +2741,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11645536ada5d1c8804312cbffc9ab950f2216154de431de930da47ca6955199" +checksum = "66fd8f17ad454fc1e4f4ab83abffcc88a532e90350d3ffddcb73030220fcbd52" dependencies = [ "cc", "codespan-reporting", @@ -2755,9 +2755,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcc9c78e3c7289665aab921a2b394eaffe8bdb369aa18d81ffc0f534fd49385" +checksum = "4717c9c806a9e07fdcb34c84965a414ea40fafe57667187052cf1eb7f5e8a8a9" dependencies = [ "clap", "codespan-reporting", @@ -2768,15 +2768,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a22a87bd9e78d7204d793261470a4c9d585154fddd251828d8aefbb5f74c3bf" +checksum = "2f6515329bf3d98f4073101c7866ff2bec4e635a13acb82e3f3753fff0bf43cb" [[package]] name = "cxxbridge-macro" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dfdb020ff8787c5daf6e0dca743005cc8782868faeadfbabb8824ede5cb1c72" +checksum = "fb93e6a7ce8ec985c02bbb758237a31598b340acbbc3c19c5a4fa6adaaac92ab" dependencies = [ "proc-macro2", "quote", @@ -3251,9 +3251,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -3465,6 +3465,17 @@ dependencies = [ "bytes", ] +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.0" @@ -4313,11 +4324,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4417,9 +4428,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -4441,9 +4452,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -4467,7 +4478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" dependencies = [ "hex", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "pin-project-lite", "tokio", @@ -4483,7 +4494,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -4493,13 +4504,13 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" dependencies = [ "futures-util", "http 1.2.0", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "rustls 0.23.20", "rustls-pki-types", @@ -4514,7 +4525,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.31", + "hyper 0.14.32", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -4527,7 +4538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.31", + "hyper 0.14.32", "native-tls", "tokio", "tokio-native-tls", @@ -4541,7 +4552,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "native-tls", "tokio", @@ -4560,7 +4571,7 @@ dependencies = [ "futures-util", "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.5.2", "pin-project-lite", "socket2", "tokio", @@ -4576,7 +4587,7 @@ checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" dependencies = [ "hex", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "pin-project-lite", "tokio", @@ -5318,7 +5329,7 @@ dependencies = [ "home", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", "hyper-timeout", "jsonpath-rust", @@ -5399,9 +5410,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libgit2-sys" @@ -5872,9 +5883,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -5904,7 +5915,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "log", "rand", @@ -8496,7 +8507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.6", + "thiserror 2.0.8", "ucd-trie", ] @@ -9335,7 +9346,7 @@ dependencies = [ [[package]] name = "pop-cli" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "assert_cmd", @@ -9371,7 +9382,7 @@ dependencies = [ [[package]] name = "pop-common" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "cargo_toml", @@ -9403,7 +9414,7 @@ dependencies = [ [[package]] name = "pop-contracts" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "contract-build", @@ -9430,7 +9441,7 @@ dependencies = [ [[package]] name = "pop-parachains" -version = "0.5.0" +version = "0.6.0" dependencies = [ "anyhow", "askama", @@ -9460,7 +9471,7 @@ dependencies = [ [[package]] name = "pop-telemetry" -version = "0.5.0" +version = "0.6.0" dependencies = [ "dirs", "env_logger 0.11.5", @@ -9926,7 +9937,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-tls 0.5.0", "ipnet", "js-sys", @@ -9967,8 +9978,8 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.5.2", + "hyper-rustls 0.27.4", "hyper-tls 0.6.0", "hyper-util", "ipnet", @@ -10084,16 +10095,18 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", "ark-ff 0.4.2", "bytes", - "fastrlp", + "fastrlp 0.3.1", + "fastrlp 0.4.0", "num-bigint", + "num-integer", "num-traits", "parity-scale-codec", "primitive-types 0.12.2", @@ -10891,9 +10904,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -13520,11 +13533,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.8", ] [[package]] @@ -13540,9 +13553,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" dependencies = [ "proc-macro2", "quote", @@ -15358,9 +15371,9 @@ dependencies = [ [[package]] name = "zip" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" dependencies = [ "arbitrary", "crc32fast", @@ -15368,7 +15381,7 @@ dependencies = [ "displaydoc", "indexmap 2.7.0", "memchr", - "thiserror 2.0.6", + "thiserror 2.0.8", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8b284b10a..07be639c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ documentation = "https://learn.onpop.io/" license = "GPL-3.0" repository = "https://github.com/r0gue-io/pop-cli" rust-version = "1.81.0" -version = "0.5.0" +version = "0.6.0" [workspace.dependencies] anyhow = "1.0" diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 6093569dc..538ef0d51 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -33,19 +33,19 @@ strum.workspace = true strum_macros.workspace = true # contracts -pop-contracts = { path = "../pop-contracts", version = "0.5.0", optional = true } +pop-contracts = { path = "../pop-contracts", version = "0.6.0", optional = true } sp-weights = { workspace = true, optional = true } # parachains -pop-parachains = { path = "../pop-parachains", version = "0.5.0", optional = true } +pop-parachains = { path = "../pop-parachains", version = "0.6.0", optional = true } dirs = { workspace = true, optional = true } git2.workspace = true # telemetry -pop-telemetry = { path = "../pop-telemetry", version = "0.5.0", optional = true } +pop-telemetry = { path = "../pop-telemetry", version = "0.6.0", optional = true } # common -pop-common = { path = "../pop-common", version = "0.5.0" } +pop-common = { path = "../pop-common", version = "0.6.0" } # wallet-integration axum.workspace = true diff --git a/crates/pop-cli/src/commands/build/contract.rs b/crates/pop-cli/src/commands/build/contract.rs index ca0cdd739..d8687aa0a 100644 --- a/crates/pop-cli/src/commands/build/contract.rs +++ b/crates/pop-cli/src/commands/build/contract.rs @@ -1,27 +1,18 @@ // SPDX-License-Identifier: GPL-3.0 use crate::cli; -use clap::Args; use pop_contracts::{build_smart_contract, Verbosity}; use std::path::PathBuf; -#[cfg(not(test))] -use std::{thread::sleep, time::Duration}; -#[derive(Args)] -pub struct BuildContractCommand { - /// Path for the contract project [default: current directory] - #[arg(long)] +/// Configuration for building a smart contract. +pub struct BuildContract { + /// Path of the contract project. pub(crate) path: Option, - /// The default compilation includes debug functionality, increasing contract size and gas - /// usage. For production, always build in release mode to exclude debug features. - #[clap(short, long)] + /// Build profile: `true` for release mode, `false` for debug mode. pub(crate) release: bool, - // Deprecation flag, used to specify whether the deprecation warning is shown. - #[clap(skip)] - pub(crate) valid: bool, } -impl BuildContractCommand { +impl BuildContract { /// Executes the command. pub(crate) fn execute(self) -> anyhow::Result<&'static str> { self.build(&mut cli::Cli) @@ -33,14 +24,6 @@ impl BuildContractCommand { /// * `cli` - The CLI implementation to be used. fn build(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<&'static str> { cli.intro("Building your contract")?; - - // Show warning if specified as deprecated. - if !self.valid { - cli.warning("NOTE: this command is deprecated. Please use `pop build` (or simply `pop b`) in future...")?; - #[cfg(not(test))] - sleep(Duration::from_secs(3)); - } - // Build contract. let build_result = build_smart_contract(self.path.as_deref(), self.release, Verbosity::Default)?; @@ -66,23 +49,16 @@ mod tests { create_smart_contract(name, &path.join(name), &Standard)?; for release in [false, true] { - for valid in [false, true] { - let mut cli = MockCli::new() - .expect_intro("Building your contract") - .expect_outro("Build completed successfully!"); - - if !valid { - cli = cli.expect_warning("NOTE: this command is deprecated. Please use `pop build` (or simply `pop b`) in future..."); - } + let mut cli = MockCli::new() + .expect_intro("Building your contract") + .expect_outro("Build completed successfully!"); - assert_eq!( - BuildContractCommand { path: Some(path.join(name)), release, valid } - .build(&mut cli)?, - "contract" - ); + assert_eq!( + BuildContract { path: Some(path.join(name)), release }.build(&mut cli)?, + "contract" + ); - cli.verify()?; - } + cli.verify()?; } Ok(()) diff --git a/crates/pop-cli/src/commands/build/mod.rs b/crates/pop-cli/src/commands/build/mod.rs index f1a69c9c2..d9ab9ba4d 100644 --- a/crates/pop-cli/src/commands/build/mod.rs +++ b/crates/pop-cli/src/commands/build/mod.rs @@ -3,12 +3,12 @@ use crate::cli::{self, Cli}; use clap::{Args, Subcommand}; #[cfg(feature = "contract")] -use contract::BuildContractCommand; +use contract::BuildContract; use duct::cmd; use pop_common::Profile; use std::path::PathBuf; #[cfg(feature = "parachain")] -use {parachain::BuildParachainCommand, spec::BuildSpecCommand}; +use {parachain::BuildParachain, spec::BuildSpecCommand}; #[cfg(feature = "contract")] pub(crate) mod contract; @@ -35,24 +35,11 @@ pub(crate) struct BuildArgs { /// Build profile [default: debug]. #[clap(long, value_enum)] pub(crate) profile: Option, - /// Parachain ID to be used when generating the chain spec files. - #[arg(short = 'i', long = "id")] - #[cfg(feature = "parachain")] - pub(crate) id: Option, } -/// Build a parachain, smart contract or Rust package. +/// Subcommand for building chain artifacts. #[derive(Subcommand)] pub(crate) enum Command { - /// [DEPRECATED] Build a parachain - #[cfg(feature = "parachain")] - #[clap(alias = "p")] - Parachain(BuildParachainCommand), - /// [DEPRECATED] Build a contract, generate metadata, bundle together in a `.contract` - /// file - #[cfg(feature = "contract")] - #[clap(alias = "c")] - Contract(BuildContractCommand), /// Build a chain specification and its genesis artifacts. #[cfg(feature = "parachain")] #[clap(alias = "s")] @@ -65,12 +52,11 @@ impl Command { // If only contract feature enabled, build as contract #[cfg(feature = "contract")] if pop_contracts::is_supported(args.path.as_deref())? { - // All commands originating from root command are valid let release = match args.profile { Some(profile) => profile.into(), None => args.release, }; - BuildContractCommand { path: args.path, release, valid: true }.execute()?; + BuildContract { path: args.path, release }.execute()?; return Ok("contract"); } @@ -81,13 +67,10 @@ impl Command { Some(profile) => profile, None => args.release.into(), }; - // All commands originating from root command are valid - BuildParachainCommand { - path: args.path, + BuildParachain { + path: args.path.unwrap_or_else(|| PathBuf::from("./")), package: args.package, - profile: Some(profile), - id: args.id, - valid: true, + profile, } .execute()?; return Ok("parachain"); @@ -160,7 +143,6 @@ mod tests { package: package.clone(), release, profile: Some(profile.clone()), - id: None, }, &mut cli, )?, diff --git a/crates/pop-cli/src/commands/build/parachain.rs b/crates/pop-cli/src/commands/build/parachain.rs index 13d8fe963..7091d426b 100644 --- a/crates/pop-cli/src/commands/build/parachain.rs +++ b/crates/pop-cli/src/commands/build/parachain.rs @@ -1,34 +1,24 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{cli, style::style}; -use clap::Args; use pop_common::Profile; use pop_parachains::build_parachain; use std::path::PathBuf; #[cfg(not(test))] use std::{thread::sleep, time::Duration}; -#[derive(Args)] -pub struct BuildParachainCommand { - /// Directory path for your project [default: current directory]. - #[arg(long)] - pub(crate) path: Option, +// Configuration for building a parachain. +pub struct BuildParachain { + /// Directory path for your project. + pub(crate) path: PathBuf, /// The package to be built. - #[arg(short, long)] pub(crate) package: Option, - /// Build profile [default: debug]. - #[clap(long, value_enum)] - pub(crate) profile: Option, - /// Parachain ID to be used when generating the chain spec files. - #[arg(short, long)] - pub(crate) id: Option, - // Deprecation flag, used to specify whether the deprecation warning is shown. - #[clap(skip)] - pub(crate) valid: bool, + /// Build profile. + pub(crate) profile: Profile, } -impl BuildParachainCommand { - /// Executes the command. +impl BuildParachain { + /// Executes the build process. pub(crate) fn execute(self) -> anyhow::Result<&'static str> { self.build(&mut cli::Cli) } @@ -41,13 +31,7 @@ impl BuildParachainCommand { let project = if self.package.is_some() { "package" } else { "parachain" }; cli.intro(format!("Building your {project}"))?; - let profile = self.profile.unwrap_or(Profile::Debug); - // Show warning if specified as deprecated. - if !self.valid { - cli.warning("NOTE: this command is deprecated. Please use `pop build` (or simply `pop b`) in future...")?; - #[cfg(not(test))] - sleep(Duration::from_secs(3)) - } else if profile == Profile::Debug { + if self.profile == Profile::Debug { cli.warning("NOTE: this command now defaults to DEBUG builds. Please use `--release` (or simply `-r`) for a release build...")?; #[cfg(not(test))] sleep(Duration::from_secs(3)) @@ -55,9 +39,8 @@ impl BuildParachainCommand { // Build parachain. cli.warning("NOTE: this may take some time...")?; - let project_path = self.path.unwrap_or_else(|| PathBuf::from("./")); - let binary = build_parachain(&project_path, self.package, &profile, None)?; - cli.info(format!("The {project} was built in {} mode.", profile))?; + let binary = build_parachain(&self.path, self.package, &self.profile, None)?; + cli.info(format!("The {project} was built in {} mode.", self.profile))?; cli.outro("Build completed successfully!")?; let generated_files = [format!("Binary generated at: {}", binary.display())]; let generated_files: Vec<_> = generated_files @@ -115,36 +98,28 @@ mod tests { for package in [None, Some(name.to_string())] { for profile in Profile::VARIANTS { - for valid in [false, true] { - let project = if package.is_some() { "package" } else { "parachain" }; - let mut cli = MockCli::new() - .expect_intro(format!("Building your {project}")) - .expect_warning("NOTE: this may take some time...") - .expect_info(format!("The {project} was built in {profile} mode.")) - .expect_outro("Build completed successfully!"); - - if !valid { - cli = cli.expect_warning("NOTE: this command is deprecated. Please use `pop build` (or simply `pop b`) in future..."); - } else { - if profile == &Profile::Debug { - cli = cli.expect_warning("NOTE: this command now defaults to DEBUG builds. Please use `--release` (or simply `-r`) for a release build..."); - } + let project = if package.is_some() { "package" } else { "parachain" }; + let mut cli = MockCli::new() + .expect_intro(format!("Building your {project}")) + .expect_warning("NOTE: this may take some time...") + .expect_info(format!("The {project} was built in {profile} mode.")) + .expect_outro("Build completed successfully!"); + + if profile == &Profile::Debug { + cli = cli.expect_warning("NOTE: this command now defaults to DEBUG builds. Please use `--release` (or simply `-r`) for a release build..."); + } + + assert_eq!( + BuildParachain { + path: project_path.clone(), + package: package.clone(), + profile: profile.clone(), } + .build(&mut cli)?, + project + ); - assert_eq!( - BuildParachainCommand { - path: Some(project_path.clone()), - package: package.clone(), - profile: Some(profile.clone()), - id: None, - valid, - } - .build(&mut cli)?, - project - ); - - cli.verify()?; - } + cli.verify()?; } } diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 9af86e3f4..064a34d0b 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -88,10 +88,6 @@ impl Command { Self::Build(args) => match args.command { None => build::Command::execute(args).map(|t| json!(t)), Some(cmd) => match cmd { - #[cfg(feature = "parachain")] - build::Command::Parachain(cmd) => cmd.execute().map(|_| Value::Null), - #[cfg(feature = "contract")] - build::Command::Contract(cmd) => cmd.execute().map(|_| Value::Null), #[cfg(feature = "parachain")] build::Command::Spec(cmd) => cmd.execute().await.map(|_| Value::Null), }, diff --git a/crates/pop-cli/src/commands/test/contract.rs b/crates/pop-cli/src/commands/test/contract.rs index 05e842eca..36d496163 100644 --- a/crates/pop-cli/src/commands/test/contract.rs +++ b/crates/pop-cli/src/commands/test/contract.rs @@ -8,16 +8,11 @@ use clap::Args; use cliclack::{clear_screen, log::warning, outro}; use pop_contracts::{test_e2e_smart_contract, test_smart_contract}; use std::path::PathBuf; -#[cfg(not(test))] -use {std::time::Duration, tokio::time::sleep}; #[derive(Args)] pub(crate) struct TestContractCommand { #[arg(short, long, help = "Path for the contract project [default: current directory]")] path: Option, - /// [DEPRECATED] Run e2e tests - #[arg(short, long, value_parser=["e2e-tests"])] - features: Option, /// Run end-to-end tests #[arg(short, long)] e2e: bool, @@ -33,21 +28,9 @@ impl TestContractCommand { pub(crate) async fn execute(mut self) -> anyhow::Result<&'static str> { clear_screen()?; - let mut show_deprecated = false; - if self.features.is_some() && self.features.clone().unwrap().contains("e2e-tests") { - show_deprecated = true; - self.e2e = true; - } - if self.e2e { Cli.intro("Starting end-to-end tests")?; - if show_deprecated { - warning("NOTE: --features e2e-tests is deprecated. Use --e2e instead.")?; - #[cfg(not(test))] - sleep(Duration::from_secs(3)).await; - } - self.node = match check_contracts_node_and_prompt( &mut Cli, &crate::cache()?, diff --git a/crates/pop-contracts/Cargo.toml b/crates/pop-contracts/Cargo.toml index cd880213c..581075f95 100644 --- a/crates/pop-contracts/Cargo.toml +++ b/crates/pop-contracts/Cargo.toml @@ -34,7 +34,7 @@ contract-extrinsics.workspace = true contract-transcode.workspace = true scale-info.workspace = true # pop -pop-common = { path = "../pop-common", version = "0.5.0" } +pop-common = { path = "../pop-common", version = "0.6.0" } [dev-dependencies] # Used in doc tests. diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index 4b2ee4111..619b3336e 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -35,7 +35,7 @@ walkdir.workspace = true zombienet-sdk.workspace = true # Pop -pop-common = { path = "../pop-common", version = "0.5.0" } +pop-common = { path = "../pop-common", version = "0.6.0" } [dev-dependencies] # Used in doc tests. diff --git a/crates/pop-parachains/src/new_parachain.rs b/crates/pop-parachains/src/new_parachain.rs index 8974cfede..f9e28e527 100644 --- a/crates/pop-parachains/src/new_parachain.rs +++ b/crates/pop-parachains/src/new_parachain.rs @@ -87,12 +87,7 @@ pub fn instantiate_openzeppelin_template( let source = temp_dir.path(); let tag = Git::clone_and_degit(template.repository_url()?, source, tag_version)?; - let mut template_name = template.template_name_without_provider(); - // Handle deprecated OpenZeppelin template - if matches!(template, Parachain::DeprecatedOpenZeppelinGeneric) { - template_name = Parachain::OpenZeppelinGeneric.template_name_without_provider(); - } - + let template_name = template.template_name_without_provider(); extract_template_files(template_name, temp_dir.path(), target, None)?; Ok(tag) } diff --git a/crates/pop-parachains/src/templates.rs b/crates/pop-parachains/src/templates.rs index 0f9ba5794..324f6f23f 100644 --- a/crates/pop-parachains/src/templates.rs +++ b/crates/pop-parachains/src/templates.rs @@ -177,23 +177,6 @@ pub enum Parachain { ) )] ParityFPT, - // OpenZeppelin - #[strum( - serialize = "polkadot-generic-runtime-template", - message = "Generic Runtime Template", - detailed_message = "A generic template for Substrate Runtime.", - props( - Provider = "OpenZeppelin", - Repository = "https://github.com/OpenZeppelin/polkadot-runtime-templates", - Network = "./zombienet-config/devnet.toml", - SupportedVersions = "v1.0.0,v2.0.1", - IsAudited = "true", - License = "GPL-3.0", - IsDeprecated = "true", - DeprecatedMessage = "This template is deprecated. Please use openzeppelin/generic-template in the future.", - ) - )] - DeprecatedOpenZeppelinGeneric, // templates for unit tests below #[cfg(test)] #[strum( @@ -273,7 +256,6 @@ mod tests { // openzeppelin ("openzeppelin/generic-template".to_string(), OpenZeppelinGeneric), ("openzeppelin/evm-template".to_string(), OpenZeppelinEVM), - ("polkadot-generic-runtime-template".to_string(), DeprecatedOpenZeppelinGeneric), ("cpt".to_string(), ParityContracts), ("fpt".to_string(), ParityFPT), ("test_01".to_string(), TestTemplate01), @@ -289,7 +271,6 @@ mod tests { (EVM, "evm".to_string()), (OpenZeppelinGeneric, "generic-template".to_string()), (OpenZeppelinEVM, "evm-template".to_string()), - (DeprecatedOpenZeppelinGeneric, "polkadot-generic-runtime-template".to_string()), (ParityContracts, "cpt".to_string()), (ParityFPT, "fpt".to_string()), (TestTemplate01, "test_01".to_string()), @@ -331,7 +312,6 @@ mod tests { (EVM, Some("./network.toml")), (OpenZeppelinGeneric, Some("./zombienet-config/devnet.toml")), (OpenZeppelinEVM, Some("./zombienet-config/devnet.toml")), - (DeprecatedOpenZeppelinGeneric, Some("./zombienet-config/devnet.toml")), (ParityContracts, Some("./zombienet.toml")), (ParityFPT, Some("./zombienet-config.toml")), (TestTemplate01, Some("")), @@ -348,7 +328,6 @@ mod tests { (EVM, Some("Unlicense")), (OpenZeppelinGeneric, Some("GPL-3.0")), (OpenZeppelinEVM, Some("GPL-3.0")), - (DeprecatedOpenZeppelinGeneric, Some("GPL-3.0")), (ParityContracts, Some("Unlicense")), (ParityFPT, Some("Unlicense")), (TestTemplate01, Some("Unlicense")), From ca1cd92a10b0f109e5e94b60804128725f7894e9 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez <11448715+al3mart@users.noreply.github.com> Date: Fri, 3 Jan 2025 23:16:20 +0100 Subject: [PATCH 008/117] fix: check_contracts_node handles skip_confirm (#396) * fix: check_contracts_node_handles_skip_confirm * style: remove unused import --- crates/pop-cli/src/common/contracts.rs | 28 +++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/common/contracts.rs b/crates/pop-cli/src/common/contracts.rs index a3b8f878c..09d3ee5b9 100644 --- a/crates/pop-cli/src/common/contracts.rs +++ b/crates/pop-cli/src/common/contracts.rs @@ -26,11 +26,14 @@ pub async fn check_contracts_node_and_prompt( let mut node_path = binary.path(); if !binary.exists() { cli.warning("⚠️ The substrate-contracts-node binary is not found.")?; - if cli - .confirm("📦 Would you like to source it automatically now?") - .initial_value(true) - .interact()? - { + let latest = if !skip_confirm { + cli.confirm("📦 Would you like to source it automatically now?") + .initial_value(true) + .interact()? + } else { + true + }; + if latest { let spinner = spinner(); spinner.start("📦 Sourcing substrate-contracts-node..."); @@ -174,6 +177,21 @@ mod tests { cli.verify() } + #[tokio::test] + async fn check_contracts_node_and_prompt_handles_skip_confirm() -> anyhow::Result<()> { + let cache_path = tempfile::tempdir().expect("Could create temp dir"); + let mut cli = + MockCli::new().expect_warning("⚠️ The substrate-contracts-node binary is not found."); + + let node_path = check_contracts_node_and_prompt(&mut cli, cache_path.path(), true).await?; + // Binary path is at least equal to the cache path + "substrate-contracts-node". + assert!(node_path + .to_str() + .unwrap() + .starts_with(&cache_path.path().join("substrate-contracts-node").to_str().unwrap())); + cli.verify() + } + #[tokio::test] async fn node_is_terminated() -> anyhow::Result<()> { let cache = tempfile::tempdir().expect("Could not create temp dir"); From a91ccfd803cf37d93462e43bf56bc6406b8a4344 Mon Sep 17 00:00:00 2001 From: Kazunobu Ndong <33208377+ndkazu@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:50:06 +0900 Subject: [PATCH 009/117] chore: support specify contract path input with or without -p flag (#361) * pop -p arg * pop build & pop up completed * re-starting from scratch * Cleaner version of first proposal * cargo +nightly fmt * pop build completed * pop up contract implementation * pop call contract implemented * Fixed some of the issues pointed in the reviews * cargo +nightly fmt * Review corrections implemented * cargo fmt * Fixing CI errors * License --- crates/pop-cli/src/commands/build/mod.rs | 23 +++++-- crates/pop-cli/src/commands/call/contract.rs | 63 ++++++++++++-------- crates/pop-cli/src/commands/up/contract.rs | 23 +++++-- crates/pop-cli/src/common/builds.rs | 12 ++++ crates/pop-cli/src/common/mod.rs | 1 + crates/pop-cli/tests/contract.rs | 13 +++- 6 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 crates/pop-cli/src/common/builds.rs diff --git a/crates/pop-cli/src/commands/build/mod.rs b/crates/pop-cli/src/commands/build/mod.rs index d9ab9ba4d..1f805f8df 100644 --- a/crates/pop-cli/src/commands/build/mod.rs +++ b/crates/pop-cli/src/commands/build/mod.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 -use crate::cli::{self, Cli}; +use crate::{ + cli::{self, Cli}, + common::builds::get_project_path, +}; use clap::{Args, Subcommand}; #[cfg(feature = "contract")] use contract::BuildContract; @@ -23,9 +26,12 @@ pub(crate) mod spec; pub(crate) struct BuildArgs { #[command(subcommand)] pub command: Option, - /// Directory path for your project [default: current directory] + /// Directory path with flag for your project [default: current directory] #[arg(long)] pub(crate) path: Option, + /// Directory path without flag for your project [default: current directory] + #[arg(value_name = "PATH", index = 1, conflicts_with = "path")] + pub(crate) path_pos: Option, /// The package to be built. #[arg(short = 'p', long)] pub(crate) package: Option, @@ -50,25 +56,29 @@ impl Command { /// Executes the command. pub(crate) fn execute(args: BuildArgs) -> anyhow::Result<&'static str> { // If only contract feature enabled, build as contract + let project_path = get_project_path(args.path.clone(), args.path_pos.clone()); + #[cfg(feature = "contract")] - if pop_contracts::is_supported(args.path.as_deref())? { + if pop_contracts::is_supported(project_path.as_deref().map(|v| v))? { + // All commands originating from root command are valid let release = match args.profile { Some(profile) => profile.into(), None => args.release, }; - BuildContract { path: args.path, release }.execute()?; + BuildContract { path: project_path, release }.execute()?; return Ok("contract"); } // If only parachain feature enabled, build as parachain #[cfg(feature = "parachain")] - if pop_parachains::is_supported(args.path.as_deref())? { + if pop_parachains::is_supported(project_path.as_deref().map(|v| v))? { let profile = match args.profile { Some(profile) => profile, None => args.release.into(), }; + let temp_path = PathBuf::from("./"); BuildParachain { - path: args.path.unwrap_or_else(|| PathBuf::from("./")), + path: project_path.unwrap_or_else(|| temp_path).to_path_buf(), package: args.package, profile, } @@ -140,6 +150,7 @@ mod tests { BuildArgs { command: None, path: Some(project_path.clone()), + path_pos: Some(project_path.clone()), package: package.clone(), release, profile: Some(profile.clone()), diff --git a/crates/pop-cli/src/commands/call/contract.rs b/crates/pop-cli/src/commands/call/contract.rs index ab7a826fd..629ca204b 100644 --- a/crates/pop-cli/src/commands/call/contract.rs +++ b/crates/pop-cli/src/commands/call/contract.rs @@ -3,6 +3,7 @@ use crate::{ cli::{self, traits::*}, common::{ + builds::get_project_path, contracts::has_contract_been_built, wallet::{prompt_to_use_wallet, request_signature}, }, @@ -17,7 +18,7 @@ use pop_contracts::{ parse_account, set_up_call, CallExec, CallOpts, DefaultEnvironment, Verbosity, }; use sp_weights::Weight; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; const DEFAULT_URL: &str = "ws://localhost:9944/"; const DEFAULT_URI: &str = "//Alice"; @@ -28,6 +29,9 @@ pub struct CallContractCommand { /// Path to the contract build directory or a contract artifact. #[arg(short, long)] path: Option, + /// Directory path without flag for your project [default: current directory] + #[arg(value_name = "PATH", index = 1, conflicts_with = "path")] + pub(crate) path_pos: Option, /// The address of the contract to call. #[arg(short, long, env = "CONTRACT")] contract: Option, @@ -109,9 +113,13 @@ impl CallContractCommand { fn display(&self) -> String { let mut full_message = "pop call contract".to_string(); + if let Some(path) = &self.path { full_message.push_str(&format!(" --path {}", path.display())); } + if let Some(path_pos) = &self.path_pos { + full_message.push_str(&format!(" --path {}", path_pos.display())); + } if let Some(contract) = &self.contract { full_message.push_str(&format!(" --contract {}", contract)); } @@ -148,11 +156,12 @@ impl CallContractCommand { /// If the contract has not been built, build it in release mode. async fn ensure_contract_built(&self, cli: &mut impl Cli) -> Result<()> { + let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); // Build the contract in release mode cli.warning("NOTE: contract has not yet been built.")?; let spinner = spinner(); spinner.start("Building contract in RELEASE mode..."); - let result = match build_smart_contract(self.path.as_deref(), true, Verbosity::Quiet) { + let result = match build_smart_contract(project_path.as_deref(), true, Verbosity::Quiet) { Ok(result) => result, Err(e) => { return Err(anyhow!(format!( @@ -182,7 +191,9 @@ impl CallContractCommand { /// Checks whether building the contract is required fn is_contract_build_required(&self) -> bool { - self.path + let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); + + project_path .as_ref() .map(|p| p.is_dir() && !has_contract_been_built(Some(p))) .unwrap_or_default() @@ -190,6 +201,8 @@ impl CallContractCommand { /// Configure the call based on command line arguments/call UI. async fn configure(&mut self, cli: &mut impl Cli, repeat: bool) -> Result<()> { + let mut project_path = get_project_path(self.path.clone(), self.path_pos.clone()); + // Show intro on first run. if !repeat { cli.intro("Call a contract")?; @@ -201,16 +214,15 @@ impl CallContractCommand { } // Resolve path. - if self.path.is_none() { + if project_path.is_none() { let input_path: String = cli .input("Where is your project or contract artifact located?") .placeholder("./") .default_input("./") .interact()?; - self.path = Some(PathBuf::from(input_path)); + project_path = Some(PathBuf::from(input_path)); } - let contract_path = self - .path + let contract_path = project_path .as_ref() .expect("path is guaranteed to be set as input as prompted when None; qed"); @@ -357,6 +369,8 @@ impl CallContractCommand { cli: &mut impl Cli, prompt_to_repeat_call: bool, ) -> Result<()> { + let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); + let message = match &self.message { Some(message) => message.to_string(), None => { @@ -364,8 +378,9 @@ impl CallContractCommand { }, }; // Disable wallet signing and display warning if the call is read-only. + let path = PathBuf::from("./"); let message_metadata = - get_message(self.path.as_deref().unwrap_or_else(|| Path::new("./")), &message)?; + get_message(project_path.as_deref().unwrap_or_else(|| &path), &message)?; if !message_metadata.mutates && self.use_wallet { cli.warning("NOTE: Signing is not required for this read-only call. The '--use-wallet' flag will be ignored.")?; self.use_wallet = false; @@ -378,7 +393,7 @@ impl CallContractCommand { }, }; let call_exec = match set_up_call(CallOpts { - path: self.path.clone(), + path: project_path, contract, message, args: self.args.clone(), @@ -564,6 +579,7 @@ mod tests { // Contract deployed on Pop Network testnet, test get CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("get".to_string()), args: vec![].to_vec(), @@ -600,6 +616,7 @@ mod tests { let mut call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("flip".to_string()), args: vec![].to_vec(), @@ -637,6 +654,7 @@ mod tests { // From .contract file let mut call_config = CallContractCommand { path: Some(current_dir.join("pop-contracts/tests/files/testing.contract")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("flip".to_string()), args: vec![].to_vec(), @@ -723,6 +741,7 @@ mod tests { // Contract deployed on Pop Network testnet, test get let mut call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: Some("get".to_string()), args: vec![].to_vec(), @@ -770,10 +789,6 @@ mod tests { Some(items), 1, // "get" message ) - .expect_input( - "Where is your project or contract artifact located?", - temp_dir.path().join("testing").display().to_string(), - ) .expect_input( "Where is your contract deployed?", "wss://rpc1.paseo.popnetwork.xyz".into(), @@ -789,6 +804,7 @@ mod tests { let mut call_config = CallContractCommand { path: None, + path_pos: Some(temp_dir.path().join("testing")), contract: None, message: None, args: vec![].to_vec(), @@ -818,7 +834,7 @@ mod tests { assert!(!call_config.dry_run); assert_eq!(call_config.display(), format!( "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message get --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice", - temp_dir.path().join("testing").display().to_string(), + temp_dir.path().join("testing").display().to_string() )); cli.verify() @@ -844,10 +860,6 @@ mod tests { ]; // The inputs are processed in reverse order. let mut cli = MockCli::new() - .expect_input( - "Where is your project or contract artifact located?", - temp_dir.path().join("testing").display().to_string(), - ) .expect_input( "Where is your contract deployed?", "wss://rpc1.paseo.popnetwork.xyz".into(), @@ -876,6 +888,7 @@ mod tests { let mut call_config = CallContractCommand { path: None, + path_pos: Some(temp_dir.path().join("testing")), contract: None, message: None, args: vec![].to_vec(), @@ -908,7 +921,7 @@ mod tests { assert!(!call_config.dry_run); assert_eq!(call_config.display(), format!( "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --use-wallet --execute", - temp_dir.path().join("testing").display().to_string(), + temp_dir.path().join("testing").display().to_string() )); cli.verify() @@ -941,10 +954,6 @@ mod tests { Some(items), 2, // "specific_flip" message ) - .expect_input( - "Where is your project or contract artifact located?", - temp_dir.path().join("testing").display().to_string(), - ) .expect_input( "Where is your contract deployed?", "wss://rpc1.paseo.popnetwork.xyz".into(), @@ -964,6 +973,7 @@ mod tests { let mut call_config = CallContractCommand { path: None, + path_pos: Some(temp_dir.path().join("testing")), contract: None, message: None, args: vec![].to_vec(), @@ -996,7 +1006,7 @@ mod tests { assert!(call_config.dev_mode); assert_eq!(call_config.display(), format!( "pop call contract --path {} --contract 15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm --message specific_flip --args \"true\", \"2\" --value 50 --url wss://rpc1.paseo.popnetwork.xyz/ --suri //Alice --execute", - temp_dir.path().join("testing").display().to_string(), + temp_dir.path().join("testing").display().to_string() )); cli.verify() @@ -1023,6 +1033,7 @@ mod tests { // Test the path is a folder with an invalid build. let mut command = CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: None, message: None, args: vec![].to_vec(), @@ -1073,6 +1084,7 @@ mod tests { assert!(matches!( CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: None, args: vec![].to_vec(), @@ -1092,6 +1104,7 @@ mod tests { assert!(matches!( CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: None, message: Some("get".to_string()), args: vec![].to_vec(), @@ -1116,6 +1129,7 @@ mod tests { let temp_dir = new_environment("testing")?; let call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: None, args: vec![].to_vec(), @@ -1147,6 +1161,7 @@ mod tests { let temp_dir = new_environment("testing")?; let call_config = CallContractCommand { path: Some(temp_dir.path().join("testing")), + path_pos: None, contract: Some("15XausWjFLBBFLDXUSBRfSfZk25warm4wZRV4ZxhZbfvjrJm".to_string()), message: None, args: vec![].to_vec(), diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index f46c0776f..c11e55e5c 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -3,6 +3,7 @@ use crate::{ cli::{traits::Cli as _, Cli}, common::{ + builds::get_project_path, contracts::{check_contracts_node_and_prompt, has_contract_been_built, terminate_node}, wallet::request_signature, }, @@ -34,6 +35,9 @@ pub struct UpContractCommand { /// Path to the contract build directory. #[arg(short, long)] path: Option, + /// Directory path without flag for your project [default: current directory] + #[arg(value_name = "PATH", index = 1, conflicts_with = "path")] + pub path_pos: Option, /// The name of the contract constructor to call. #[clap(short, long, default_value = "new")] constructor: String, @@ -92,13 +96,18 @@ impl UpContractCommand { pub(crate) async fn execute(mut self) -> anyhow::Result<()> { Cli.intro("Deploy a smart contract")?; + let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); // Check if build exists in the specified "Contract build directory" - if !has_contract_been_built(self.path.as_deref()) { + if !has_contract_been_built(project_path.as_deref()) { // Build the contract in release mode Cli.warning("NOTE: contract has not yet been built.")?; let spinner = spinner(); spinner.start("Building contract in RELEASE mode..."); - let result = match build_smart_contract(self.path.as_deref(), true, Verbosity::Quiet) { + let result = match build_smart_contract( + project_path.as_deref().map(|v| v), + true, + Verbosity::Quiet, + ) { Ok(result) => result, Err(e) => { Cli.outro_cancel(format!("🚫 An error occurred building your contract: {e}\nUse `pop build` to retry with build output."))?; @@ -360,7 +369,8 @@ impl UpContractCommand { // get the call data and contract code hash async fn get_contract_data(&self) -> anyhow::Result<(Vec, [u8; 32])> { - let contract_code = get_contract_code(self.path.as_ref())?; + let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); + let contract_code = get_contract_code(project_path.as_ref())?; let hash = contract_code.code_hash(); if self.upload_only { let call_data = get_upload_payload(contract_code, self.url.as_str()).await?; @@ -435,6 +445,7 @@ mod tests { fn default_up_contract_command() -> UpContractCommand { UpContractCommand { path: None, + path_pos: None, constructor: "new".to_string(), args: vec![], value: "0".to_string(), @@ -510,7 +521,8 @@ mod tests { let localhost_url = format!("ws://127.0.0.1:{}", port); let up_contract_opts = UpContractCommand { - path: Some(temp_dir), + path: Some(temp_dir.clone()), + path_pos: Some(temp_dir), constructor: "new".to_string(), args: vec![], value: "0".to_string(), @@ -561,7 +573,8 @@ mod tests { let localhost_url = format!("ws://127.0.0.1:{}", port); let up_contract_opts = UpContractCommand { - path: Some(temp_dir), + path: Some(temp_dir.clone()), + path_pos: Some(temp_dir), constructor: "new".to_string(), args: vec!["false".to_string()], value: "0".to_string(), diff --git a/crates/pop-cli/src/common/builds.rs b/crates/pop-cli/src/common/builds.rs new file mode 100644 index 000000000..1192ed0cc --- /dev/null +++ b/crates/pop-cli/src/common/builds.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +use std::path::PathBuf; + +/// This method is used to get the proper project path format (with or without cli flag) +pub fn get_project_path(path_flag: Option, path_pos: Option) -> Option { + let project_path = if let Some(ref path) = path_pos { + Some(path) // Use positional path if present + } else { + path_flag.as_ref() // Otherwise, use the named path + }; + project_path.cloned() +} diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index 4a89036e5..512d2a670 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 +pub mod builds; #[cfg(feature = "contract")] pub mod contracts; pub mod helpers; diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index 1af21b385..a095702a0 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -74,6 +74,7 @@ async fn contract_lifecycle() -> Result<()> { .args(&["build", "--path", "./test_contract", "--release"]) .assert() .success(); + // Verify that the directory target has been created assert!(temp_dir.join("test_contract/target").exists()); // Verify that all the artifacts has been generated @@ -88,11 +89,19 @@ async fn contract_lifecycle() -> Result<()> { sleep(Duration::from_secs(5)).await; // Only upload the contract - // pop up contract --upload-only + // pop up contract --path ./test_contract --upload-only Command::cargo_bin("pop") .unwrap() .current_dir(&temp_dir.join("test_contract")) - .args(&["up", "contract", "--upload-only", "--url", default_endpoint]) + .args(&[ + "up", + "contract", + "--path", + "./test_contract", + "--upload-only", + "--url", + default_endpoint, + ]) .assert() .success(); // Instantiate contract, only dry-run From 17d3bd6414a50e0544b290ab5a236bd53c9a525f Mon Sep 17 00:00:00 2001 From: Alejandro Martinez <11448715+al3mart@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:52:12 +0100 Subject: [PATCH 010/117] build(release): update upload-artifact to v4 (#398) --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 803cb31e0..f505dd001 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,7 @@ jobs: tar -czf ${{ env.package }} pop pop.sha256 - name: Upload binary - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: binaries path: ${{ env.path }}/${{ env.package }} @@ -81,4 +81,4 @@ jobs: uses: softprops/action-gh-release@v1 with: files: | - ${{ env.path }}/${{ env.package }} \ No newline at end of file + ${{ env.path }}/${{ env.package }} From 2e032339be1058e87460ac307a8c913a1728fbc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 10:14:16 +0100 Subject: [PATCH 011/117] build(deps): bump openssl from 0.10.68 to 0.10.70 (#402) Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.68 to 0.10.70. - [Release notes](https://github.com/sfackler/rust-openssl/releases) - [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.68...openssl-v0.10.70) --- updated-dependencies: - dependency-name: openssl dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e3981ecf..f3a264fc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6221,9 +6221,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -6262,9 +6262,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", From 84e9d0ba825ac8a2111e46184ecbc8c18855bafc Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Wed, 12 Feb 2025 16:38:31 +0700 Subject: [PATCH 012/117] chore: `profile` comment in `build_parachain` (#406) Minor fix for the parameter type of `build_parachain` --- crates/pop-parachains/src/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-parachains/src/build.rs b/crates/pop-parachains/src/build.rs index 1e369f371..96483ef43 100644 --- a/crates/pop-parachains/src/build.rs +++ b/crates/pop-parachains/src/build.rs @@ -17,7 +17,7 @@ use std::{ /// * `path` - The optional path to the parachain manifest, defaulting to the current directory if /// not specified. /// * `package` - The optional package to be built. -/// * `release` - Whether the parachain should be built without any debugging functionality. +/// * `profile` - Whether the parachain should be built without any debugging functionality. /// * `node_path` - An optional path to the node directory. Defaults to the `node` subdirectory of /// the project path if not provided. pub fn build_parachain( From 00680f8d17ecd29c376402dfd247390acf9b3e7a Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Thu, 13 Feb 2025 09:32:45 +0100 Subject: [PATCH 013/117] fix: increase `DefaultBodyLimit` to prevent large payload failures (#409) * fix: increase DefaultBodyLimit to prevent large payload failures * test: increase coverage testing with larger than MAX_PAYLOAD_SIZE --- crates/pop-cli/src/wallet_integration.rs | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/pop-cli/src/wallet_integration.rs b/crates/pop-cli/src/wallet_integration.rs index 92e3395ec..8304b7d59 100644 --- a/crates/pop-cli/src/wallet_integration.rs +++ b/crates/pop-cli/src/wallet_integration.rs @@ -1,4 +1,5 @@ use axum::{ + extract::DefaultBodyLimit, http::HeaderValue, response::Html, routing::{get, post}, @@ -13,6 +14,8 @@ use tokio::{ }; use tower_http::{cors::Any, services::ServeDir}; +const MAX_PAYLOAD_SIZE: usize = 15 * 1024 * 1024; + /// Make frontend sourcing more flexible by allowing a custom route to be defined. pub trait Frontend { /// Serves the content via a [Router]. @@ -116,7 +119,8 @@ impl WalletIntegrationManager { .route("/error", post(routes::error_handler).with_state(state.clone())) .route("/terminate", post(routes::terminate_handler).with_state(state.clone())) .merge(frontend.serve_content()) // Custom route for serving frontend. - .layer(cors); + .layer(cors) + .layer(DefaultBodyLimit::max(MAX_PAYLOAD_SIZE)); let url_owned = server_url.to_string(); @@ -546,6 +550,33 @@ mod tests { assert_eq!(actual_payload.chain_rpc, expected_payload.chain_rpc); assert_eq!(actual_payload.call_data, call_data_5mb); + let encoded_payload: String = call_data_5mb.iter().map(|b| format!("{:02x}", b)).collect(); + let client = reqwest::Client::new(); + let response = client + .post(&format!("{}/submit", addr)) + .json(&encoded_payload) + .send() + .await + .expect("Failed to send large payload"); + + assert!(response.status().is_success()); + let error = wim.take_error().await; + assert!(error.is_none()); + + let call_data_15mb = vec![99u8; MAX_PAYLOAD_SIZE + 1]; + let encoded_oversized_payload: String = + call_data_15mb.iter().map(|b| format!("{:02x}", b)).collect(); + let response = client + .post(&format!("{}/submit", addr)) + .json(&encoded_oversized_payload) + .send() + .await; + + assert!( + response.is_err() || + response.unwrap().status() == reqwest::StatusCode::PAYLOAD_TOO_LARGE + ); + wim.terminate().await.expect("Termination should not fail."); assert!(wim.task_handle.await.is_ok()); } From a2b9184612a7dc0797e45f8eb0f5feedfdb3404b Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Mon, 17 Feb 2025 08:40:56 +0100 Subject: [PATCH 014/117] fix: prevent recursion error with `pallet_collective` metadata (#412) * fix: bug parsing metadata with Council pallet * test: use a live network endpoint for the test --- .../pop-parachains/src/call/metadata/params.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/pop-parachains/src/call/metadata/params.rs b/crates/pop-parachains/src/call/metadata/params.rs index 9d8521f9c..162fc6d28 100644 --- a/crates/pop-parachains/src/call/metadata/params.rs +++ b/crates/pop-parachains/src/call/metadata/params.rs @@ -50,6 +50,10 @@ fn type_to_param(name: &str, registry: &PortableRegistry, type_id: u32) -> Resul let type_info = registry .resolve(type_id) .ok_or_else(|| Error::MetadataParsingError(name.to_string()))?; + // Check for unsupported `RuntimeCall` type + if type_info.path.segments.contains(&"RuntimeCall".to_string()) { + return Err(Error::FunctionNotSupported); + } for param in &type_info.type_params { if param.name.contains("RuntimeCall") { return Err(Error::FunctionNotSupported); @@ -232,6 +236,19 @@ mod tests { field_to_param(&metadata, &function.fields.first().unwrap()), Err(Error::FunctionNotSupported) )); + // TODO: Use the Pop Network endpoint once the mainnet (or an equivalent testnet with the + // same runtime) is available. + let client = set_up_client("wss://mythos.ibp.network").await?; + let metadata = client.metadata(); + let function = metadata + .pallet_by_name("Council") + .unwrap() + .call_variant_by_name("execute") + .unwrap(); + assert!(matches!( + field_to_param(&metadata, &function.fields.first().unwrap()), + Err(Error::FunctionNotSupported) + )); Ok(()) } From 68ec6d5213b19e8ef3442cd095551ccc04438902 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Mon, 17 Feb 2025 13:08:48 +0100 Subject: [PATCH 015/117] feat: enable pop up without project type specification (#403) * refactor: use network subcommand for launching local network * feat: enable pop up without project type specification * chore: warning message if command deprecated * refactor: remove set_project_path and fix tests * docs: deprecation comments * test: update integration tests * refactor: use HELP_HEADER only once * test: fix integration tests * refactor: docs and warning messages * test: detects_project_type_correctly * refactor: remove logic deploy parachain * fix: help header * docs: deprecation comments * refactor: unit test detects_project_type_correctly * refactor: improve deprecation warning * refactor: improve deprecation warning --- crates/pop-cli/src/commands/mod.rs | 36 ++++- crates/pop-cli/src/commands/up/contract.rs | 64 ++++----- crates/pop-cli/src/commands/up/mod.rs | 132 ++++++++++++++++-- .../commands/up/{parachain.rs => network.rs} | 12 +- crates/pop-cli/tests/contract.rs | 20 +-- crates/pop-cli/tests/parachain.rs | 4 +- 6 files changed, 201 insertions(+), 67 deletions(-) rename crates/pop-cli/src/commands/up/{parachain.rs => network.rs} (97%) diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 064a34d0b..bdf09c4ca 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 -use crate::{cache, cli::Cli}; +use crate::{cache, cli::Cli, common::builds::get_project_path}; use clap::Subcommand; use pop_common::templates::Template; use serde_json::{json, Value}; @@ -30,8 +30,7 @@ pub(crate) enum Command { #[clap(alias = "c")] #[cfg(any(feature = "parachain", feature = "contract"))] Call(call::CallArgs), - /// Launch a local network or deploy a smart contract. - #[clap(alias = "u")] + #[clap(alias = "u", about = about_up())] #[cfg(any(feature = "parachain", feature = "contract"))] Up(up::UpArgs), /// Test a smart contract. @@ -53,6 +52,16 @@ fn about_build() -> &'static str { return "Build a smart contract or Rust package."; } +/// Help message for the up command. +fn about_up() -> &'static str { + #[cfg(all(feature = "parachain", feature = "contract"))] + return "Deploy a smart contract or launch a local network."; + #[cfg(all(feature = "parachain", not(feature = "contract")))] + return "Launch a local network."; + #[cfg(all(feature = "contract", not(feature = "parachain")))] + return "Deploy a smart contract."; +} + impl Command { /// Executes the command. pub(crate) async fn execute(self) -> anyhow::Result { @@ -101,10 +110,23 @@ impl Command { }, #[cfg(any(feature = "parachain", feature = "contract"))] Self::Up(args) => match args.command { - #[cfg(feature = "parachain")] - up::Command::Parachain(cmd) => cmd.execute().await.map(|_| Value::Null), - #[cfg(feature = "contract")] - up::Command::Contract(cmd) => cmd.execute().await.map(|_| Value::Null), + None => up::Command::execute(args).await.map(|t| json!(t)), + Some(cmd) => match cmd { + #[cfg(feature = "parachain")] + up::Command::Network(mut cmd) => { + cmd.valid = true; + cmd.execute().await.map(|_| Value::Null) + }, + // TODO: Deprecated, will be removed in v0.8.0. + #[cfg(feature = "parachain")] + up::Command::Parachain(cmd) => cmd.execute().await.map(|_| Value::Null), + // TODO: Deprecated, will be removed in v0.8.0. + #[cfg(feature = "contract")] + up::Command::Contract(mut cmd) => { + cmd.path = get_project_path(args.path, args.path_pos); + cmd.execute().await.map(|_| Value::Null) + }, + }, }, #[cfg(feature = "contract")] Self::Test(args) => match args.command { diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index c11e55e5c..3cc3f146c 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -3,7 +3,6 @@ use crate::{ cli::{traits::Cli as _, Cli}, common::{ - builds::get_project_path, contracts::{check_contracts_node_and_prompt, has_contract_been_built, terminate_node}, wallet::request_signature, }, @@ -29,47 +28,46 @@ const COMPLETE: &str = "🚀 Deployment complete"; const DEFAULT_URL: &str = "ws://localhost:9944/"; const DEFAULT_PORT: u16 = 9944; const FAILED: &str = "🚫 Deployment failed."; +const HELP_HEADER: &str = "Smart contract deployment options"; #[derive(Args, Clone)] +#[clap(next_help_heading = HELP_HEADER)] pub struct UpContractCommand { /// Path to the contract build directory. - #[arg(short, long)] - path: Option, - /// Directory path without flag for your project [default: current directory] - #[arg(value_name = "PATH", index = 1, conflicts_with = "path")] - pub path_pos: Option, + #[clap(skip)] + pub(crate) path: Option, /// The name of the contract constructor to call. #[clap(short, long, default_value = "new")] - constructor: String, + pub(crate) constructor: String, /// The constructor arguments, encoded as strings. #[clap(short, long, num_args = 0..,)] - args: Vec, + pub(crate) args: Vec, /// Transfers an initial balance to the instantiated contract. #[clap(short, long, default_value = "0")] - value: String, + pub(crate) value: String, /// Maximum amount of gas to be used for this command. /// If not specified it will perform a dry-run to estimate the gas consumed for the /// instantiation. #[clap(name = "gas", short, long)] - gas_limit: Option, + pub(crate) gas_limit: Option, /// Maximum proof size for the instantiation. /// If not specified it will perform a dry-run to estimate the proof size required. #[clap(short = 'P', long)] - proof_size: Option, + pub(crate) proof_size: Option, /// A salt used in the address derivation of the new contract. Use to create multiple /// instances of the same contract code from the same account. #[clap(short = 'S', long, value_parser = parse_hex_bytes)] - salt: Option, + pub(crate) salt: Option, /// Websocket endpoint of a chain. #[clap(short, long, value_parser, default_value = DEFAULT_URL)] - url: Url, + pub(crate) url: Url, /// Secret key URI for the account deploying the contract. /// /// e.g. /// - for a dev account "//Alice" /// - with a password "//Alice///SECRET_PASSWORD" #[clap(short, long, default_value = "//Alice")] - suri: String, + pub(crate) suri: String, /// Use a browser extension wallet to sign the extrinsic. #[clap( name = "use-wallet", @@ -78,36 +76,37 @@ pub struct UpContractCommand { short('w'), conflicts_with = "suri" )] - use_wallet: bool, + pub(crate) use_wallet: bool, /// Perform a dry-run via RPC to estimate the gas usage. This does not submit a transaction. #[clap(short = 'D', long)] - dry_run: bool, + pub(crate) dry_run: bool, /// Uploads the contract only, without instantiation. #[clap(short = 'U', long)] - upload_only: bool, + pub(crate) upload_only: bool, /// Automatically source or update the needed binary required without prompting for /// confirmation. #[clap(short = 'y', long)] - skip_confirm: bool, + pub(crate) skip_confirm: bool, + // Deprecation flag, used to specify whether the deprecation warning is shown. + #[clap(skip)] + pub(crate) valid: bool, } impl UpContractCommand { /// Executes the command. pub(crate) async fn execute(mut self) -> anyhow::Result<()> { Cli.intro("Deploy a smart contract")?; - - let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); + // Show warning if specified as deprecated. + if !self.valid { + Cli.warning("DEPRECATION: Please use `pop up` (or simply `pop u`) in future...")?; + } // Check if build exists in the specified "Contract build directory" - if !has_contract_been_built(project_path.as_deref()) { + if !has_contract_been_built(self.path.as_deref()) { // Build the contract in release mode Cli.warning("NOTE: contract has not yet been built.")?; let spinner = spinner(); spinner.start("Building contract in RELEASE mode..."); - let result = match build_smart_contract( - project_path.as_deref().map(|v| v), - true, - Verbosity::Quiet, - ) { + let result = match build_smart_contract(self.path.as_deref(), true, Verbosity::Quiet) { Ok(result) => result, Err(e) => { Cli.outro_cancel(format!("🚫 An error occurred building your contract: {e}\nUse `pop build` to retry with build output."))?; @@ -369,8 +368,7 @@ impl UpContractCommand { // get the call data and contract code hash async fn get_contract_data(&self) -> anyhow::Result<(Vec, [u8; 32])> { - let project_path = get_project_path(self.path.clone(), self.path_pos.clone()); - let contract_code = get_contract_code(project_path.as_ref())?; + let contract_code = get_contract_code(self.path.as_ref())?; let hash = contract_code.code_hash(); if self.upload_only { let call_data = get_upload_payload(contract_code, self.url.as_str()).await?; @@ -445,7 +443,6 @@ mod tests { fn default_up_contract_command() -> UpContractCommand { UpContractCommand { path: None, - path_pos: None, constructor: "new".to_string(), args: vec![], value: "0".to_string(), @@ -458,6 +455,7 @@ mod tests { upload_only: false, skip_confirm: false, use_wallet: false, + valid: true, } } @@ -521,8 +519,7 @@ mod tests { let localhost_url = format!("ws://127.0.0.1:{}", port); let up_contract_opts = UpContractCommand { - path: Some(temp_dir.clone()), - path_pos: Some(temp_dir), + path: Some(temp_dir), constructor: "new".to_string(), args: vec![], value: "0".to_string(), @@ -535,6 +532,7 @@ mod tests { upload_only: true, skip_confirm: true, use_wallet: true, + valid: true, }; let rpc_client = subxt::backend::rpc::RpcClient::from_url(&up_contract_opts.url).await?; @@ -573,8 +571,7 @@ mod tests { let localhost_url = format!("ws://127.0.0.1:{}", port); let up_contract_opts = UpContractCommand { - path: Some(temp_dir.clone()), - path_pos: Some(temp_dir), + path: Some(temp_dir), constructor: "new".to_string(), args: vec!["false".to_string()], value: "0".to_string(), @@ -587,6 +584,7 @@ mod tests { upload_only: false, skip_confirm: true, use_wallet: true, + valid: true, }; // Retrieve call data based on the above command options. diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 500e2e206..877618ba1 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -1,29 +1,143 @@ // SPDX-License-Identifier: GPL-3.0 +use crate::{ + cli::{self, Cli}, + common::builds::get_project_path, +}; use clap::{Args, Subcommand}; +use std::path::PathBuf; #[cfg(feature = "contract")] mod contract; #[cfg(feature = "parachain")] -mod parachain; +mod network; -/// Arguments for launching or deploying. -#[derive(Args)] +/// Arguments for launching or deploying a project. +#[derive(Args, Clone)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct UpArgs { + /// Path to the project directory. + // TODO: Introduce the short option in v0.8.0 once deprecated parachain command is removed. + #[arg(long, global = true)] + pub path: Option, + + /// Directory path without flag for your project [default: current directory] + #[arg(value_name = "PATH", index = 1, global = true, conflicts_with = "path")] + pub path_pos: Option, + + #[command(flatten)] + #[cfg(feature = "contract")] + pub(crate) contract: contract::UpContractCommand, + #[command(subcommand)] - pub(crate) command: Command, + pub(crate) command: Option, } /// Launch a local network or deploy a smart contract. -#[derive(Subcommand)] +#[derive(Subcommand, Clone)] pub(crate) enum Command { #[cfg(feature = "parachain")] /// Launch a local network. - #[clap(alias = "p")] - Parachain(parachain::ZombienetCommand), + #[clap(alias = "n")] + Network(network::ZombienetCommand), + #[cfg(feature = "parachain")] + /// [DEPRECATED] Launch a local network (will be removed in v0.8.0). + #[clap(alias = "p", hide = true)] + Parachain(network::ZombienetCommand), #[cfg(feature = "contract")] - /// Deploy a smart contract. - #[clap(alias = "c")] + /// [DEPRECATED] Deploy a smart contract (will be removed in v0.8.0). + #[clap(alias = "c", hide = true)] Contract(contract::UpContractCommand), } + +impl Command { + /// Executes the command. + pub(crate) async fn execute(args: UpArgs) -> anyhow::Result<&'static str> { + Self::execute_project_deployment(args, &mut Cli).await + } + + /// Identifies the project type and executes the appropriate deployment process. + async fn execute_project_deployment( + args: UpArgs, + cli: &mut impl cli::traits::Cli, + ) -> anyhow::Result<&'static str> { + let project_path = get_project_path(args.path.clone(), args.path_pos.clone()); + // If only contract feature enabled, deploy a contract + #[cfg(feature = "contract")] + if pop_contracts::is_supported(project_path.as_deref())? { + let mut cmd = args.contract; + cmd.path = project_path; + cmd.valid = true; // To handle deprecated command, remove in v0.8.0. + cmd.execute().await?; + return Ok("contract"); + } + cli.warning("No contract detected. Ensure you are in a valid project directory.")?; + Ok("") + } +} + +#[cfg(test)] +mod tests { + use super::{contract::UpContractCommand, *}; + + use cli::MockCli; + use duct::cmd; + use pop_contracts::{mock_build_process, new_environment}; + use std::env; + use url::Url; + + fn create_up_args(project_path: PathBuf) -> anyhow::Result { + Ok(UpArgs { + path: Some(project_path), + path_pos: None, + contract: UpContractCommand { + path: None, + constructor: "new".to_string(), + args: vec!["false".to_string()], + value: "0".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + url: Url::parse("wss://rpc2.paseo.popnetwork.xyz")?, + suri: "//Alice".to_string(), + use_wallet: false, + dry_run: true, + upload_only: true, + skip_confirm: false, + valid: false, + }, + command: None, + }) + } + + #[tokio::test] + async fn detects_contract_correctly() -> anyhow::Result<()> { + let temp_dir = new_environment("testing")?; + let mut current_dir = env::current_dir().expect("Failed to get current directory"); + current_dir.pop(); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("pop-contracts/tests/files/testing.contract"), + current_dir.join("pop-contracts/tests/files/testing.json"), + )?; + let args = create_up_args(temp_dir.path().join("testing"))?; + let mut cli = MockCli::new(); + assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "contract"); + cli.verify() + } + + #[tokio::test] + async fn detects_rust_project_correctly() -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let name = "hello_world"; + let path = temp_dir.path(); + let project_path = path.join(name); + let args = create_up_args(project_path)?; + + cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; + let mut cli = MockCli::new() + .expect_warning("No contract detected. Ensure you are in a valid project directory."); + assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, ""); + cli.verify() + } +} diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/network.rs similarity index 97% rename from crates/pop-cli/src/commands/up/parachain.rs rename to crates/pop-cli/src/commands/up/network.rs index 30fc6fce0..8d29c0a12 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/network.rs @@ -13,7 +13,7 @@ use pop_parachains::{clear_dmpq, Error, IndexSet, NetworkNode, RelayChain, Zombi use std::{path::Path, time::Duration}; use tokio::time::sleep; -#[derive(Args)] +#[derive(Args, Clone)] pub(crate) struct ZombienetCommand { /// The Zombienet network configuration file to be used. #[arg(short, long)] @@ -48,6 +48,9 @@ pub(crate) struct ZombienetCommand { /// Automatically source all needed binaries required without prompting for confirmation. #[clap(short = 'y', long)] skip_confirm: bool, + // Deprecation flag, used to specify whether the deprecation warning is shown. + #[clap(skip)] + pub(crate) valid: bool, } impl ZombienetCommand { @@ -57,6 +60,13 @@ impl ZombienetCommand { intro(format!("{}: Launch a local network", style(" Pop CLI ").black().on_magenta()))?; set_theme(Theme); + // Show warning if specified as deprecated. + if !self.valid { + log::warning( + "DEPRECATION: Please use `pop up network` (or simply `pop up n`) in future...", + )?; + } + // Parse arguments let cache = crate::cache()?; let mut zombienet = match Zombienet::new( diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index a095702a0..535b0502e 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -89,19 +89,11 @@ async fn contract_lifecycle() -> Result<()> { sleep(Duration::from_secs(5)).await; // Only upload the contract - // pop up contract --path ./test_contract --upload-only + // pop up --path ./test_contract --upload-only Command::cargo_bin("pop") .unwrap() - .current_dir(&temp_dir.join("test_contract")) - .args(&[ - "up", - "contract", - "--path", - "./test_contract", - "--upload-only", - "--url", - default_endpoint, - ]) + .current_dir(&temp_dir) + .args(&["up", "--path", "./test_contract", "--upload-only", "--url", default_endpoint]) .assert() .success(); // Instantiate contract, only dry-run @@ -110,7 +102,6 @@ async fn contract_lifecycle() -> Result<()> { .current_dir(&temp_dir.join("test_contract")) .args(&[ "up", - "contract", "--constructor", "new", "--args", @@ -180,7 +171,7 @@ async fn contract_lifecycle() -> Result<()> { .assert() .success(); - // pop up contract --upload-only --use-wallet + // pop up --upload-only --use-wallet // Will run http server for wallet integration. // Using `cargo run --` as means for the CI to pass. // Possibly there's room for improvement here. @@ -189,12 +180,11 @@ async fn contract_lifecycle() -> Result<()> { "run", "--", "up", - "contract", "--upload-only", "--use-wallet", "--skip-confirm", "--dry-run", - "-p", + "--path", temp_dir.join("test_contract").to_str().expect("to_str"), "--url", default_endpoint, diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 31ad6bded..d8d3b5b56 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -123,10 +123,10 @@ name = "collator-01" ), )?; - // `pop up parachain -f ./network.toml --skip-confirm` + // `pop up network -f ./network.toml --skip-confirm` let mut cmd = Cmd::new(cargo_bin("pop")) .current_dir(&temp_parachain_dir) - .args(&["up", "parachain", "-f", "./network.toml", "--skip-confirm"]) + .args(&["up", "network", "-f", "./network.toml", "--skip-confirm"]) .spawn() .unwrap(); From 11f2d86bfbe4461e98eca50f4d57fc430c48f3a1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 9 Feb 2025 11:50:01 +0100 Subject: [PATCH 016/117] test: detects_project_type_correctly --- crates/pop-cli/src/commands/up/mod.rs | 33 ++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 877618ba1..d16bd52e3 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -71,7 +71,13 @@ impl Command { cmd.execute().await?; return Ok("contract"); } - cli.warning("No contract detected. Ensure you are in a valid project directory.")?; + if pop_parachains::is_supported(project_path.as_deref())? { + cli.warning("Parachain deployment is currently not implemented.")?; + return Ok("parachain"); + } + cli.warning( + "No contract or parachain detected. Ensure you are in a valid project directory.", + )?; Ok("") } } @@ -83,6 +89,7 @@ mod tests { use cli::MockCli; use duct::cmd; use pop_contracts::{mock_build_process, new_environment}; + use pop_parachains::{instantiate_template_dir, Config, Parachain}; use std::env; use url::Url; @@ -126,6 +133,25 @@ mod tests { cli.verify() } + #[tokio::test] + async fn detects_parachain_correctly() -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let name = "parachain"; + let project_path = temp_dir.path().join(name); + let config = Config { + symbol: "DOT".to_string(), + decimals: 18, + initial_endowment: "1000000".to_string(), + }; + instantiate_template_dir(&Parachain::Standard, &project_path, None, config)?; + + let args = create_up_args(project_path)?; + let mut cli = + MockCli::new().expect_warning("Parachain deployment is currently not implemented."); + assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); + cli.verify() + } + #[tokio::test] async fn detects_rust_project_correctly() -> anyhow::Result<()> { let temp_dir = tempfile::tempdir()?; @@ -135,8 +161,9 @@ mod tests { let args = create_up_args(project_path)?; cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; - let mut cli = MockCli::new() - .expect_warning("No contract detected. Ensure you are in a valid project directory."); + let mut cli = MockCli::new().expect_warning( + "No contract or parachain detected. Ensure you are in a valid project directory.", + ); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, ""); cli.verify() } From 50a207dabf2914eb88e26c5165ca0deef55f8172 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 9 Feb 2025 12:14:28 +0100 Subject: [PATCH 017/117] feat: parachain deployment in pop up --- crates/pop-cli/src/commands/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index bdf09c4ca..0ce460f55 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -55,9 +55,9 @@ fn about_build() -> &'static str { /// Help message for the up command. fn about_up() -> &'static str { #[cfg(all(feature = "parachain", feature = "contract"))] - return "Deploy a smart contract or launch a local network."; + return "Deploy a parachain, deploy a smart contract or launch a local network."; #[cfg(all(feature = "parachain", not(feature = "contract")))] - return "Launch a local network."; + return "Deploy a parachain or launch a local network."; #[cfg(all(feature = "contract", not(feature = "parachain")))] return "Deploy a smart contract."; } From 0a64db891b6b096bcd139785addcdd915fa60ff7 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 9 Feb 2025 19:21:13 +0100 Subject: [PATCH 018/117] fix: parachain feature --- crates/pop-cli/src/commands/up/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index d16bd52e3..5d409065a 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -71,6 +71,7 @@ impl Command { cmd.execute().await?; return Ok("contract"); } + #[cfg(feature = "parachain")] if pop_parachains::is_supported(project_path.as_deref())? { cli.warning("Parachain deployment is currently not implemented.")?; return Ok("parachain"); From 89d4c23a01858429552ca9820cc17f9f515d3a76 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 17:40:48 +0100 Subject: [PATCH 019/117] docs: improve comments --- crates/pop-cli/src/commands/mod.rs | 2 +- crates/pop-cli/src/commands/up/contract.rs | 5 +++-- crates/pop-cli/src/commands/up/network.rs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 0ce460f55..0f597183c 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -52,7 +52,7 @@ fn about_build() -> &'static str { return "Build a smart contract or Rust package."; } -/// Help message for the up command. +/// Help message for the `up` command. fn about_up() -> &'static str { #[cfg(all(feature = "parachain", feature = "contract"))] return "Deploy a parachain, deploy a smart contract or launch a local network."; diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 3cc3f146c..d4807404e 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -87,7 +87,8 @@ pub struct UpContractCommand { /// confirmation. #[clap(short = 'y', long)] pub(crate) skip_confirm: bool, - // Deprecation flag, used to specify whether the deprecation warning is shown. + // Deprecation flag, used to specify whether the deprecation warning is shown (will be removed + // in v0.8.0). #[clap(skip)] pub(crate) valid: bool, } @@ -98,7 +99,7 @@ impl UpContractCommand { Cli.intro("Deploy a smart contract")?; // Show warning if specified as deprecated. if !self.valid { - Cli.warning("DEPRECATION: Please use `pop up` (or simply `pop u`) in future...")?; + Cli.warning("DEPRECATION: Please use `pop up` (or simply `pop u`) in the future...")?; } // Check if build exists in the specified "Contract build directory" if !has_contract_been_built(self.path.as_deref()) { diff --git a/crates/pop-cli/src/commands/up/network.rs b/crates/pop-cli/src/commands/up/network.rs index 8d29c0a12..f45d5f45c 100644 --- a/crates/pop-cli/src/commands/up/network.rs +++ b/crates/pop-cli/src/commands/up/network.rs @@ -48,7 +48,8 @@ pub(crate) struct ZombienetCommand { /// Automatically source all needed binaries required without prompting for confirmation. #[clap(short = 'y', long)] skip_confirm: bool, - // Deprecation flag, used to specify whether the deprecation warning is shown. + // Deprecation flag, used to specify whether the deprecation warning is shown (will be removed + // in v0.8.0). #[clap(skip)] pub(crate) valid: bool, } @@ -63,7 +64,7 @@ impl ZombienetCommand { // Show warning if specified as deprecated. if !self.valid { log::warning( - "DEPRECATION: Please use `pop up network` (or simply `pop up n`) in future...", + "DEPRECATION: Please use `pop up network` (or simply `pop up n`) in the future...", )?; } From 77309e2fa26198011a17d20c3732bf1061237319 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 10 Feb 2025 11:23:52 +0100 Subject: [PATCH 020/117] feat: deploy a parachain commands --- crates/pop-cli/src/commands/up/mod.rs | 15 +++++++++++--- crates/pop-cli/src/commands/up/parachain.rs | 23 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 crates/pop-cli/src/commands/up/parachain.rs diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 5d409065a..362de9a9a 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -11,6 +11,8 @@ use std::path::PathBuf; mod contract; #[cfg(feature = "parachain")] mod network; +#[cfg(feature = "parachain")] +mod parachain; /// Arguments for launching or deploying a project. #[derive(Args, Clone)] @@ -29,6 +31,10 @@ pub(crate) struct UpArgs { #[cfg(feature = "contract")] pub(crate) contract: contract::UpContractCommand, + #[command(flatten)] + #[cfg(feature = "parachain")] + pub(crate) parachain: parachain::UpParachainCommand, + #[command(subcommand)] pub(crate) command: Option, } @@ -73,7 +79,9 @@ impl Command { } #[cfg(feature = "parachain")] if pop_parachains::is_supported(project_path.as_deref())? { - cli.warning("Parachain deployment is currently not implemented.")?; + let mut cmd = args.parachain; + cmd.path = project_path; + cmd.execute(cli).await?; return Ok("parachain"); } cli.warning( @@ -85,7 +93,7 @@ impl Command { #[cfg(test)] mod tests { - use super::{contract::UpContractCommand, *}; + use super::{contract::UpContractCommand, parachain::UpParachainCommand, *}; use cli::MockCli; use duct::cmd; @@ -114,6 +122,7 @@ mod tests { skip_confirm: false, valid: false, }, + parachain: UpParachainCommand::default(), command: None, }) } @@ -148,7 +157,7 @@ mod tests { let args = create_up_args(project_path)?; let mut cli = - MockCli::new().expect_warning("Parachain deployment is currently not implemented."); + MockCli::new().expect_warning("Deploy a parachain"); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); cli.verify() } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs new file mode 100644 index 000000000..beeca6709 --- /dev/null +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::cli; +use clap::Args; +use std::path::PathBuf; + +const HELP_HEADER: &str = "Parachain deployment options"; + +#[derive(Args, Clone, Default)] +#[clap(next_help_heading = HELP_HEADER)] +pub struct UpParachainCommand { + /// Path to the contract build directory. + #[clap(skip)] + pub(crate) path: Option, +} + +impl UpParachainCommand { + /// Executes the command. + pub(crate) async fn execute(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<()> { + cli.intro("Deploy a parachain")?; + Ok(()) + } +} From f9dffc6c9ab6319534947c4e192cc032c0ea09d6 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 10 Feb 2025 17:31:20 +0100 Subject: [PATCH 021/117] feat: build specs in pop up --- crates/pop-cli/src/commands/build/spec.rs | 89 ++++++++++++++------- crates/pop-cli/src/commands/up/mod.rs | 8 +- crates/pop-cli/src/commands/up/parachain.rs | 35 +++++++- 3 files changed, 100 insertions(+), 32 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index 7e9ba052b..c09220bf7 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -9,7 +9,7 @@ use crate::{ style::style, }; use clap::{Args, ValueEnum}; -use cliclack::spinner; +use cliclack::{spinner, ProgressBar}; use pop_common::Profile; use pop_parachains::{ binary_path, build_parachain, export_wasm_file, generate_genesis_state_file, @@ -193,7 +193,7 @@ impl BuildSpecCommand { /// /// # Arguments /// * `cli` - The cli. - async fn configure_build_spec( + pub async fn configure_build_spec( self, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result { @@ -439,7 +439,7 @@ impl BuildSpecCommand { // Represents the configuration for building a chain specification. #[derive(Debug)] -struct BuildSpec { +pub struct BuildSpec { output_file: PathBuf, profile: Profile, id: u32, @@ -461,39 +461,18 @@ impl BuildSpec { fn build(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<&'static str> { cli.intro("Building your chain spec")?; let mut generated_files = vec![]; - let BuildSpec { - ref output_file, - ref profile, - id, - default_bootnode, - ref chain, - genesis_state, - genesis_code, - .. - } = self; + let BuildSpec { ref output_file, ref profile, id, genesis_code, genesis_state, .. } = self; // Ensure binary is built. let binary_path = ensure_binary_exists(cli, profile)?; let spinner = spinner(); - spinner.start("Generating chain specification..."); - // Generate chain spec. - generate_plain_chain_spec(&binary_path, output_file, default_bootnode, chain)?; - // Customize spec based on input. - self.customize()?; + let raw_chain_spec = self.generate_chain_spec(&binary_path, &spinner)?; + generated_files.push(format!( "Plain text chain specification file generated at: {}", &output_file.display() )); - // Generate raw spec. - spinner.set_message("Generating raw chain specification..."); - let spec_name = &output_file - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or(DEFAULT_SPEC_NAME) - .trim_end_matches(".json"); - let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; generated_files.push(format!( "Raw chain specification file generated at: {}", raw_chain_spec.display() @@ -529,6 +508,62 @@ impl BuildSpec { Ok("spec") } + /// Generates chain specification files and returns the paths of the genesis code and genesis + /// state. + /// + /// # Arguments + /// * `cli` - The cli. + pub fn generate_genesis_artifacts( + self, + cli: &mut impl cli::traits::Cli, + ) -> anyhow::Result<(PathBuf, PathBuf)> { + // Ensure binary is built once. + let binary_path = ensure_binary_exists(cli, &self.profile)?; + let spinner = spinner(); + spinner.start("Generating files..."); + + let raw_chain_spec = self.generate_chain_spec(&binary_path, &spinner)?; + + spinner.set_message("Generating genesis code..."); + let wasm_file_name = format!("para-{}.wasm", self.id); + let genesis_code = export_wasm_file(&binary_path, &raw_chain_spec, &wasm_file_name)?; + + spinner.set_message("Generating genesis state..."); + let genesis_file_name = format!("para-{}-genesis-state", self.id); + let genesis_state = + generate_genesis_state_file(&binary_path, &raw_chain_spec, &genesis_file_name)?; + + spinner.stop("Genesis artifacts generated successfully."); + Ok((genesis_code, genesis_state)) + } + + /// Generates plain and raw chain specification files. + fn generate_chain_spec( + &self, + binary_path: &PathBuf, + spinner: &ProgressBar, + ) -> anyhow::Result { + let BuildSpec { output_file, chain, .. } = self; + spinner.start("Generating chain specification..."); + + // Generate plain chain spec. + generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; + // Customize spec based on input. + self.customize()?; + + // Generate raw spec. + spinner.set_message("Generating raw chain specification..."); + let spec_name = &output_file + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(DEFAULT_SPEC_NAME) + .trim_end_matches(".json"); + let raw_spec_name = format!("{spec_name}-raw.json"); + let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; + + Ok(raw_chain_spec) + } + // Customize a chain specification. fn customize(&self) -> anyhow::Result<()> { let mut chain_spec = ChainSpec::from(&self.output_file)?; diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 362de9a9a..e2ee19d07 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -27,14 +27,14 @@ pub(crate) struct UpArgs { #[arg(value_name = "PATH", index = 1, global = true, conflicts_with = "path")] pub path_pos: Option, - #[command(flatten)] - #[cfg(feature = "contract")] - pub(crate) contract: contract::UpContractCommand, - #[command(flatten)] #[cfg(feature = "parachain")] pub(crate) parachain: parachain::UpParachainCommand, + #[command(flatten)] + #[cfg(feature = "contract")] + pub(crate) contract: contract::UpContractCommand, + #[command(subcommand)] pub(crate) command: Option, } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index beeca6709..14172ec38 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 -use crate::cli; +use crate::{ + build::spec::BuildSpecCommand, + cli::{self}, +}; use clap::Args; use std::path::PathBuf; @@ -12,12 +15,42 @@ pub struct UpParachainCommand { /// Path to the contract build directory. #[clap(skip)] pub(crate) path: Option, + /// Parachain ID to be used when generating the chain spec files. + #[arg(short, long)] + pub(crate) id: Option, + /// Path to the genesis state file. + #[arg(short = 'G', long = "genesis-state")] + pub(crate) genesis_state: Option, + /// Path to the genesis code file. + #[arg(short = 'C', long = "genesis-code")] + pub(crate) genesis_code: Option, } impl UpParachainCommand { /// Executes the command. pub(crate) async fn execute(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<()> { cli.intro("Deploy a parachain")?; + let para_id = self.id.unwrap_or(reserve_para_id(cli)?); + let (genesis_state, genesis_code) = + match (self.genesis_state.clone(), self.genesis_code.clone()) { + (Some(state), Some(code)) => (state, code), + _ => generate_spec_files(para_id, cli).await?, + }; + cli.outro("Parachain deployment complete.")?; Ok(()) } } + +fn reserve_para_id(cli: &mut impl cli::traits::Cli) -> anyhow::Result { + Ok(2000) +} + +async fn generate_spec_files( + id: u32, + cli: &mut impl cli::traits::Cli, +) -> anyhow::Result<(PathBuf, PathBuf)> { + let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } + .configure_build_spec(cli) + .await?; + build_spec.generate_genesis_artifacts(cli) +} From 80162728af32ded215df6acb1de8102acb1b8ea1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 10:49:16 +0100 Subject: [PATCH 022/117] feat: logic to interact with a chain --- crates/pop-cli/src/commands/call/chain.rs | 8 +- crates/pop-cli/src/commands/up/parachain.rs | 84 ++++++++++++++++++++- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 18f61fe77..45a9a334e 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -358,13 +358,13 @@ impl CallChainCommand { } // Represents a chain, including its URL, client connection, and available pallets. -struct Chain { +pub struct Chain { // Websocket endpoint of the node. - url: Url, + pub(crate) url: Url, // The client used to interact with the chain. - client: OnlineClient, + pub(crate) client: OnlineClient, // A list of pallets available on the chain. - pallets: Vec, + pub(crate) pallets: Vec, } /// Represents a configured dispatchable function call, including the pallet, function, arguments, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 14172ec38..187b2e700 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,11 +2,21 @@ use crate::{ build::spec::BuildSpecCommand, - cli::{self}, + call::chain::Chain, + cli::{self, traits::*}, + common::wallet::request_signature, }; +use anyhow::{anyhow, Result}; use clap::Args; +use pop_parachains::{ + construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, + submit_signed_extrinsic, Action, OnlineClient, Payload, SubstrateConfig, +}; + use std::path::PathBuf; +use url::Url; +const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; const HELP_HEADER: &str = "Parachain deployment options"; #[derive(Args, Clone, Default)] @@ -24,13 +34,17 @@ pub struct UpParachainCommand { /// Path to the genesis code file. #[arg(short = 'C', long = "genesis-code")] pub(crate) genesis_code: Option, + /// Websocket endpoint of the relay chain. + #[arg(long)] + pub(crate) relay_url: Option, } impl UpParachainCommand { /// Executes the command. - pub(crate) async fn execute(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<()> { + pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a parachain")?; - let para_id = self.id.unwrap_or(reserve_para_id(cli)?); + let chain = self.configure_chain(cli).await?; + let para_id = self.id.unwrap_or(reserve_para_id(chain, cli).await?); let (genesis_state, genesis_code) = match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), @@ -39,9 +53,44 @@ impl UpParachainCommand { cli.outro("Parachain deployment complete.")?; Ok(()) } + + // Configures the chain by resolving the URL and fetching its metadata. + async fn configure_chain(&self, cli: &mut impl Cli) -> Result { + // Resolve url. + let url = match &self.relay_url { + Some(url) => url.clone(), + None => { + // Prompt for url. + let url: String = cli + .input("Enter the relay chain node URL to deploy your parachain:") + .default_input(DEFAULT_URL) + .interact()?; + Url::parse(&url)? + }, + }; + + // Parse metadata from chain url. + let client = set_up_client(url.as_str()).await?; + let mut pallets = parse_chain_metadata(&client).map_err(|e| { + anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) + })?; + // Sort by name for display. + pallets.sort_by(|a, b| a.name.cmp(&b.name)); + pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); + Ok(Chain { url, client, pallets }) + } } -fn reserve_para_id(cli: &mut impl cli::traits::Cli) -> anyhow::Result { +async fn reserve_para_id(chain: Chain, cli: &mut impl Cli) -> Result { + cli.info("Reserving a parachain ID for your parachain...")?; + let ex = find_dispatchable_by_name( + &chain.pallets, + Action::Reserve.pallet_name(), + Action::Reserve.function_name(), + )?; + let xt = construct_extrinsic(ex, Vec::new())?; + let call_data = xt.encode_call_data(&chain.client.metadata())?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; Ok(2000) } @@ -49,8 +98,35 @@ async fn generate_spec_files( id: u32, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { + cli.info("Generating the chain spec for your parachain—some extra information is needed:")?; let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } .configure_build_spec(cli) .await?; build_spec.generate_genesis_artifacts(cli) } + +// Sign and submit an extrinsic using wallet integration. +pub async fn submit_extrinsic_with_wallet( + client: &OnlineClient, + url: &Url, + call_data: Vec, + cli: &mut impl Cli, +) -> Result<()> { + let maybe_payload = request_signature(call_data, url.to_string()).await?; + if let Some(payload) = maybe_payload { + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner.start( + "Submitting the extrinsic and then waiting for finalization, please be patient...", + ); + + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); + } else { + cli.outro_cancel("No signed payload received.")?; + } + Ok(()) +} From 11869aad99dc69b60e12d927abd24994c3eefc8c Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 16:05:53 +0100 Subject: [PATCH 023/117] feat: register parachain --- crates/pop-cli/src/commands/call/chain.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 68 ++++++++++----------- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 45a9a334e..019ba3996 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -474,7 +474,7 @@ impl Call { } // Sign and submit an extrinsic using wallet integration. -async fn submit_extrinsic_with_wallet( +pub async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, call_data: Vec, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 187b2e700..e65c954f4 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,15 +2,14 @@ use crate::{ build::spec::BuildSpecCommand, - call::chain::Chain, + call::chain::{submit_extrinsic_with_wallet, Chain}, cli::{self, traits::*}, - common::wallet::request_signature, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, - submit_signed_extrinsic, Action, OnlineClient, Payload, SubstrateConfig, + Action, Payload, }; use std::path::PathBuf; @@ -22,7 +21,7 @@ const HELP_HEADER: &str = "Parachain deployment options"; #[derive(Args, Clone, Default)] #[clap(next_help_heading = HELP_HEADER)] pub struct UpParachainCommand { - /// Path to the contract build directory. + /// Path to the chain directory. #[clap(skip)] pub(crate) path: Option, /// Parachain ID to be used when generating the chain spec files. @@ -44,12 +43,17 @@ impl UpParachainCommand { pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a parachain")?; let chain = self.configure_chain(cli).await?; - let para_id = self.id.unwrap_or(reserve_para_id(chain, cli).await?); + let para_id = if let Some(id) = self.id { + id + } else { + reserve_para_id(&chain, cli).await? + }; let (genesis_state, genesis_code) = match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), - _ => generate_spec_files(para_id, cli).await?, + _ => generate_spec_files(para_id, self.path, cli).await?, }; + register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await?; cli.outro("Parachain deployment complete.")?; Ok(()) } @@ -71,17 +75,14 @@ impl UpParachainCommand { // Parse metadata from chain url. let client = set_up_client(url.as_str()).await?; - let mut pallets = parse_chain_metadata(&client).map_err(|e| { + let pallets = parse_chain_metadata(&client).map_err(|e| { anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) })?; - // Sort by name for display. - pallets.sort_by(|a, b| a.name.cmp(&b.name)); - pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); Ok(Chain { url, client, pallets }) } } -async fn reserve_para_id(chain: Chain, cli: &mut impl Cli) -> Result { +async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { cli.info("Reserving a parachain ID for your parachain...")?; let ex = find_dispatchable_by_name( &chain.pallets, @@ -96,37 +97,32 @@ async fn reserve_para_id(chain: Chain, cli: &mut impl Cli) -> Result { async fn generate_spec_files( id: u32, + path: Option, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { - cli.info("Generating the chain spec for your parachain—some extra information is needed:")?; + cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; + if let Some(path) = &path { + std::env::set_current_dir(path).map_err(|err| { + anyhow!("Failed to change working directory to {}: {}", path.display(), err) + })?; + } let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } .configure_build_spec(cli) .await?; build_spec.generate_genesis_artifacts(cli) } -// Sign and submit an extrinsic using wallet integration. -pub async fn submit_extrinsic_with_wallet( - client: &OnlineClient, - url: &Url, - call_data: Vec, - cli: &mut impl Cli, -) -> Result<()> { - let maybe_payload = request_signature(call_data, url.to_string()).await?; - if let Some(payload) = maybe_payload { - cli.success("Signed payload received.")?; - let spinner = cliclack::spinner(); - spinner.start( - "Submitting the extrinsic and then waiting for finalization, please be patient...", - ); - - let result = submit_signed_extrinsic(client.clone(), payload) - .await - .map_err(|err| anyhow!("{}", format!("{err:?}")))?; - - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); - } else { - cli.outro_cancel("No signed payload received.")?; - } +async fn register_parachain(chain: &Chain, id: u32, genesis_state: PathBuf, genesis_code: PathBuf, cli: &mut impl Cli) -> Result<()> { + cli.info("Registering a parachain ID")?; + let ex = find_dispatchable_by_name( + &chain.pallets, + Action::Register.pallet_name(), + Action::Register.function_name(), + )?; + let state = std::fs::read_to_string(genesis_state).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let code = std::fs::read_to_string(genesis_code).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + let call_data = xt.encode_call_data(&chain.client.metadata())?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; Ok(()) -} +} \ No newline at end of file From d30c135d0a8a08455f83d06f4773e002289c539a Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 17:54:33 +0100 Subject: [PATCH 024/117] refactor: clean code and improve docs --- crates/pop-cli/src/commands/build/spec.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 133 ++++++++++++++------ 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index c09220bf7..2b580f198 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -534,7 +534,7 @@ impl BuildSpec { generate_genesis_state_file(&binary_path, &raw_chain_spec, &genesis_file_name)?; spinner.stop("Genesis artifacts generated successfully."); - Ok((genesis_code, genesis_state)) + Ok((genesis_state, genesis_code)) } /// Generates plain and raw chain specification files. diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index e65c954f4..1b17bbbff 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,15 +1,15 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, + build::spec::{BuildSpec, BuildSpecCommand}, call::chain::{submit_extrinsic_with_wallet, Chain}, cli::{self, traits::*}, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, - Action, Payload, + construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, + Payload, }; use std::path::PathBuf; @@ -24,7 +24,7 @@ pub struct UpParachainCommand { /// Path to the chain directory. #[clap(skip)] pub(crate) path: Option, - /// Parachain ID to be used when generating the chain spec files. + /// Parachain ID to use. If not specified, a new ID will be reserved. #[arg(short, long)] pub(crate) id: Option, /// Path to the genesis state file. @@ -43,17 +43,40 @@ impl UpParachainCommand { pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a parachain")?; let chain = self.configure_chain(cli).await?; - let para_id = if let Some(id) = self.id { - id - } else { - reserve_para_id(&chain, cli).await? - }; + let para_id = match self.id { + Some(id) => id, + None => { + cli.info("Reserving a parachain ID...")?; + match reserve_para_id(&chain, cli).await { + Ok(id) => id, + Err(e) => { + cli.outro_cancel(&format!("Failed to reserve parachain ID: {}", e))?; + return Err(e); + }, + } + }, + }; let (genesis_state, genesis_code) = match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), - _ => generate_spec_files(para_id, self.path, cli).await?, + _ => { + cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; + match generate_spec_files(para_id, self.path, cli).await { + Ok(files) => files, + Err(e) => { + cli.outro_cancel(&format!("Failed to generate spec files: {}", e))?; + return Err(e); + }, + } + }, }; - register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await?; + cli.info("Registering a parachain ID")?; + if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await + { + cli.outro_cancel(&format!("Failed to register parachain: {}", e))?; + return Err(e); + } + cli.outro("Parachain deployment complete.")?; Ok(()) } @@ -82,47 +105,85 @@ impl UpParachainCommand { } } +/// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { - cli.info("Reserving a parachain ID for your parachain...")?; - let ex = find_dispatchable_by_name( + let call_data = prepare_reserve_para_id_extrinsic(chain)?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; + Ok(2000) +} + +/// Constructs an extrinsic for reserving a parachain ID. +fn prepare_reserve_para_id_extrinsic(chain: &Chain) -> Result> { + let function = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), Action::Reserve.function_name(), )?; - let xt = construct_extrinsic(ex, Vec::new())?; - let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(2000) + let xt = construct_extrinsic(function, Vec::new())?; + Ok(xt.encode_call_data(&chain.client.metadata())?) } +/// Generates chain spec files for the parachain. async fn generate_spec_files( id: u32, - path: Option, - cli: &mut impl cli::traits::Cli, + path: Option, + cli: &mut impl Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { - cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; - if let Some(path) = &path { - std::env::set_current_dir(path).map_err(|err| { - anyhow!("Failed to change working directory to {}: {}", path.display(), err) - })?; - } - let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } - .configure_build_spec(cli) - .await?; + change_working_directory(&path)?; + let build_spec = configure_build_spec(id, cli).await?; build_spec.generate_genesis_artifacts(cli) } -async fn register_parachain(chain: &Chain, id: u32, genesis_state: PathBuf, genesis_code: PathBuf, cli: &mut impl Cli) -> Result<()> { - cli.info("Registering a parachain ID")?; +/// Changes the working directory if a path is provided, ensuring the build spec process runs in the +/// correct context. +fn change_working_directory(path: &Option) -> Result<()> { + if let Some(path) = path { + std::env::set_current_dir(path)?; + } + Ok(()) +} + +/// Configures the chain specification requirements. +async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result { + Ok(BuildSpecCommand { + id: Some(id), + genesis_code: true, + genesis_state: true, + ..Default::default() + } + .configure_build_spec(cli) + .await?) +} + +/// Registers a parachain by submitting an extrinsic. +async fn register_parachain( + chain: &Chain, + id: u32, + genesis_state: PathBuf, + genesis_code: PathBuf, + cli: &mut impl Cli, +) -> Result<()> { + let call_data = prepare_register_parachain_extrinsic(chain, id, genesis_state, genesis_code)?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; + Ok(()) +} + +/// Constructs an extrinsic for registering a parachain. +fn prepare_register_parachain_extrinsic( + chain: &Chain, + id: u32, + genesis_state: PathBuf, + genesis_code: PathBuf, +) -> Result> { let ex = find_dispatchable_by_name( &chain.pallets, Action::Register.pallet_name(), Action::Register.function_name(), )?; - let state = std::fs::read_to_string(genesis_state).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; - let code = std::fs::read_to_string(genesis_code).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let state = std::fs::read_to_string(genesis_state) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let code = std::fs::read_to_string(genesis_code) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; - let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(()) -} \ No newline at end of file + Ok(xt.encode_call_data(&chain.client.metadata())?) +} From 9bd7229a85381f651bca8032848c6e29b8cb2e69 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 20:30:26 +0100 Subject: [PATCH 025/117] test: unit tests for pop up methods --- crates/pop-cli/src/commands/up/parachain.rs | 153 +++++++++++++++++- .../pop-parachains/src/call/metadata/mod.rs | 5 +- 2 files changed, 155 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 1b17bbbff..32ce91007 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -3,7 +3,7 @@ use crate::{ build::spec::{BuildSpec, BuildSpecCommand}, call::chain::{submit_extrinsic_with_wallet, Chain}, - cli::{self, traits::*}, + cli::traits::*, }; use anyhow::{anyhow, Result}; use clap::Args; @@ -89,7 +89,7 @@ impl UpParachainCommand { None => { // Prompt for url. let url: String = cli - .input("Enter the relay chain node URL to deploy your parachain:") + .input("Enter the relay chain node URL to deploy your parachain") .default_input(DEFAULT_URL) .interact()?; Url::parse(&url)? @@ -187,3 +187,152 @@ fn prepare_register_parachain_extrinsic( let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; Ok(xt.encode_call_data(&chain.client.metadata())?) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + build::spec::{ChainType, RelayChain}, + cli::MockCli, + }; + use pop_common::Profile; + use pop_parachains::decode_call_data; + use std::{env, fs}; + use strum::{EnumMessage, VariantArray}; + use tempfile::tempdir; + use url::Url; + + const POLKADOT_NETWORK_URL: &str = "wss://polkadot-rpc.publicnode.com"; + + #[tokio::test] + async fn configure_chain_works() -> Result<()> { + let mut cli = MockCli::new().expect_input( + "Enter the relay chain node URL to deploy your parachain", + POLKADOT_NETWORK_URL.into(), + ); + let chain = UpParachainCommand::default().configure_chain(&mut cli).await?; + assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); + cli.verify() + } + + #[tokio::test] + async fn prepare_reserve_para_id_extrinsic_works() -> Result<()> { + let mut cli = MockCli::new(); + let chain = UpParachainCommand { + relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), + ..Default::default() + } + .configure_chain(&mut cli) + .await?; + let call_data = prepare_reserve_para_id_extrinsic(&chain)?; + assert_eq!(call_data, decode_call_data("0x4605")?); + Ok(()) + } + + #[tokio::test] + async fn change_working_directory_works() -> Result<()> { + let temp_dir = tempdir()?; + let my_parachain_path = Some(temp_dir.path().to_path_buf()); + change_working_directory(&my_parachain_path)?; + assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + + change_working_directory(&None)?; + assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + Ok(()) + } + + #[tokio::test] + async fn prepare_register_parachain_extrinsic_works() -> Result<()> { + let mut cli = MockCli::new(); + let chain = UpParachainCommand { + relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), + ..Default::default() + } + .configure_chain(&mut cli) + .await?; + // Create a temporary files to act as genesis_state and genesis_code files. + let temp_dir = tempdir()?; + let genesis_state_path = temp_dir.path().join("genesis_state"); + let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); + std::fs::write(&genesis_state_path, "0x1234")?; + std::fs::write(&genesis_code_path, "0x1234")?; + + let call_data = prepare_register_parachain_extrinsic( + &chain, + 2000, + genesis_state_path, + genesis_code_path, + )?; + assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); + Ok(()) + } + + #[tokio::test] + async fn configure_build_spec_works() -> Result<()> { + let mut cli = MockCli::new().expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) + .expect_input( + "Name or path for the plain chain spec file:", "output_file".to_string()) + .expect_input( + "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) + .expect_select( + "Choose the chain type: ", + Some(false), + true, + Some(chain_types()), + ChainType::Development as usize, + ).expect_select( + "Choose the relay your chain will be connecting to: ", + Some(false), + true, + Some(relays()), + RelayChain::PaseoLocal as usize, + ).expect_select( + "Choose the build profile of the binary that should be used: ", + Some(false), + true, + Some(profiles()), + Profile::Release as usize, + ); + + configure_build_spec(2000, &mut cli).await?; + cli.verify()?; + Ok(()) + } + + + fn relays() -> Vec<(String, String)> { + RelayChain::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn chain_types() -> Vec<(String, String)> { + ChainType::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn profiles() -> Vec<(String, String)> { + Profile::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } +} diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 5a9a9a2cb..2d371424c 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -188,7 +188,10 @@ pub fn parse_dispatchable_arguments( .add_custom_parser(custom_parsers::parse_ss58) .parse(&processed_param) .0 - .map_err(|_| Error::ParamProcessingError) + .map_err(|_| { + eprintln!("Failed to parse parameter: {}", processed_param); + Error::ParamProcessingError + }) }) .collect() } From 59525803a1f07b7ec08d1f1ec3e233d5381fc9dc Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 12 Feb 2025 15:39:25 +0100 Subject: [PATCH 026/117] refactor: small fixes with visibility and removing logs --- crates/pop-cli/src/commands/build/spec.rs | 10 +++++----- crates/pop-cli/src/commands/call/chain.rs | 4 ++-- crates/pop-cli/src/commands/up/parachain.rs | 1 - crates/pop-parachains/src/call/metadata/mod.rs | 5 +---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index 2b580f198..b1b84a8ed 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -193,7 +193,7 @@ impl BuildSpecCommand { /// /// # Arguments /// * `cli` - The cli. - pub async fn configure_build_spec( + pub(crate) async fn configure_build_spec( self, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result { @@ -439,7 +439,7 @@ impl BuildSpecCommand { // Represents the configuration for building a chain specification. #[derive(Debug)] -pub struct BuildSpec { +pub(crate) struct BuildSpec { output_file: PathBuf, profile: Profile, id: u32, @@ -508,12 +508,12 @@ impl BuildSpec { Ok("spec") } - /// Generates chain specification files and returns the paths of the genesis code and genesis - /// state. + /// Generates chain specification files and returns the file paths for the generated genesis + /// code and genesis state files. /// /// # Arguments /// * `cli` - The cli. - pub fn generate_genesis_artifacts( + pub(crate) fn generate_genesis_artifacts( self, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 019ba3996..8698dc317 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -358,7 +358,7 @@ impl CallChainCommand { } // Represents a chain, including its URL, client connection, and available pallets. -pub struct Chain { +pub(crate) struct Chain { // Websocket endpoint of the node. pub(crate) url: Url, // The client used to interact with the chain. @@ -474,7 +474,7 @@ impl Call { } // Sign and submit an extrinsic using wallet integration. -pub async fn submit_extrinsic_with_wallet( +pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, call_data: Vec, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 32ce91007..24424d129 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -299,7 +299,6 @@ mod tests { Ok(()) } - fn relays() -> Vec<(String, String)> { RelayChain::VARIANTS .iter() diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 2d371424c..5a9a9a2cb 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -188,10 +188,7 @@ pub fn parse_dispatchable_arguments( .add_custom_parser(custom_parsers::parse_ss58) .parse(&processed_param) .0 - .map_err(|_| { - eprintln!("Failed to parse parameter: {}", processed_param); - Error::ParamProcessingError - }) + .map_err(|_| Error::ParamProcessingError) }) .collect() } From d5bd2987d19088f0afdc19ebb98dffdfd55e89b9 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 13 Feb 2025 09:28:07 +0100 Subject: [PATCH 027/117] feat: return events in submit_signed_extrinsic --- crates/pop-cli/src/commands/call/chain.rs | 17 ++++++++++------- crates/pop-cli/src/commands/up/parachain.rs | 2 +- crates/pop-parachains/src/call/mod.rs | 8 ++++---- crates/pop-parachains/src/lib.rs | 1 + 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 8698dc317..2c636fe02 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -12,7 +12,8 @@ use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, sign_and_submit_extrinsic, submit_signed_extrinsic, supported_actions, Action, CallData, - DynamicPayload, Function, OnlineClient, Pallet, Param, Payload, SubstrateConfig, + DynamicPayload, ExtrinsicEvents, Function, OnlineClient, Pallet, Param, Payload, + SubstrateConfig, }; use url::Url; @@ -109,7 +110,9 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli).await + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + .await?; + Ok(()) } else { call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await }; @@ -473,13 +476,13 @@ impl Call { } } -// Sign and submit an extrinsic using wallet integration. +// Sign and submit an extrinsic using wallet integration, then returns the resulting events. pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, call_data: Vec, cli: &mut impl Cli, -) -> Result<()> { +) -> Result> { let maybe_payload = request_signature(call_data, url.to_string()).await?; if let Some(payload) = maybe_payload { cli.success("Signed payload received.")?; @@ -492,11 +495,11 @@ pub(crate) async fn submit_extrinsic_with_wallet( .await .map_err(|err| anyhow!("{}", format!("{err:?}")))?; - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); + Ok(result) } else { - display_message("No signed payload received.", false, cli)?; + return Err(anyhow!("No signed payload received.")); } - Ok(()) } // Displays a message to the user, with formatting based on the success status. diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 24424d129..1d94cb075 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -108,7 +108,7 @@ impl UpParachainCommand { /// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_para_id_extrinsic(chain)?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; + let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; Ok(2000) } diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 4569687ec..95a339d05 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -7,6 +7,7 @@ use pop_common::{ }; use sp_core::bytes::{from_hex, to_hex}; use subxt::{ + blocks::ExtrinsicEvents, dynamic::Value, tx::{DynamicPayload, Payload, SubmittableExtrinsic}, OnlineClient, SubstrateConfig, @@ -90,18 +91,17 @@ pub async fn sign_and_submit_extrinsic( pub async fn submit_signed_extrinsic( client: OnlineClient, payload: String, -) -> Result { +) -> Result, Error> { let hex_encoded = from_hex(&payload).map_err(|e| Error::CallDataDecodingError(e.to_string()))?; let extrinsic = SubmittableExtrinsic::from_bytes(client, hex_encoded); - let result = extrinsic + Ok(extrinsic .submit_and_watch() .await .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))? .wait_for_finalized_success() .await - .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?; - Ok(format!("{:?}", result.extrinsic_hash())) + .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?) } /// Encodes the call data for a given extrinsic into a hexadecimal string. diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 50bb6af5b..fce911856 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -34,6 +34,7 @@ pub use new_parachain::instantiate_template_dir; pub use relay::{clear_dmpq, RelayChain}; // External export from subxt. pub use subxt::{ + blocks::ExtrinsicEvents, tx::{DynamicPayload, Payload}, OnlineClient, SubstrateConfig, }; From 6abeba842b051e09f15f78298fc06d40851b99fe Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 14 Feb 2025 12:41:07 +0100 Subject: [PATCH 028/117] feat: get para_id from event --- Cargo.lock | 1 + Cargo.toml | 1 + crates/pop-cli/src/commands/up/parachain.rs | 24 ++++++++----- crates/pop-parachains/Cargo.toml | 1 + .../src/call/metadata/events.rs | 35 +++++++++++++++++++ .../pop-parachains/src/call/metadata/mod.rs | 1 + crates/pop-parachains/src/errors.rs | 3 ++ crates/pop-parachains/src/lib.rs | 1 + 8 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 crates/pop-parachains/src/call/metadata/events.rs diff --git a/Cargo.lock b/Cargo.lock index f3a264fc3..ffffe857b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9449,6 +9449,7 @@ dependencies = [ "duct", "glob", "indexmap 2.7.0", + "parity-scale-codec", "pop-common", "scale-info", "scale-value", diff --git a/Cargo.toml b/Cargo.toml index 07be639c7..37b62a14f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ subxt = "0.38.0" ink_env = "5.0.0" sp-core = "32.0.0" sp-weights = "31.0.0" +scale = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } scale-info = { version = "2.11.4", default-features = false, features = ["derive"] } scale-value = { version = "0.17.0", default-features = false, features = ["from-string", "parser-ss58"] } contract-build = "5.0.2" diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 1d94cb075..4c67f743e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -8,8 +8,8 @@ use crate::{ use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, - Payload, + construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, + parse_chain_metadata, set_up_client, Action, Payload, }; use std::path::PathBuf; @@ -50,8 +50,8 @@ impl UpParachainCommand { match reserve_para_id(&chain, cli).await { Ok(id) => id, Err(e) => { - cli.outro_cancel(&format!("Failed to reserve parachain ID: {}", e))?; - return Err(e); + cli.outro_cancel(&format!("{}", e))?; + return Ok(()); }, } }, @@ -60,12 +60,12 @@ impl UpParachainCommand { match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), _ => { - cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; + cli.info("Generating the chain spec for your parachain.")?; match generate_spec_files(para_id, self.path, cli).await { Ok(files) => files, Err(e) => { cli.outro_cancel(&format!("Failed to generate spec files: {}", e))?; - return Err(e); + return Ok(()); }, } }, @@ -74,7 +74,7 @@ impl UpParachainCommand { if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await { cli.outro_cancel(&format!("Failed to register parachain: {}", e))?; - return Err(e); + return Ok(()); } cli.outro("Parachain deployment complete.")?; @@ -108,8 +108,14 @@ impl UpParachainCommand { /// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_para_id_extrinsic(chain)?; - let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(2000) + let events = submit_extrinsic_with_wallet(&chain.client.clone(), &chain.url, call_data, cli) + .await + .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; + let para_id = extract_para_id_from_event(&events).await.map_err(|_| { + anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with --id.") + })?; + cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; + Ok(para_id) } /// Constructs an extrinsic for reserving a parachain ID. diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index 619b3336e..a7bd5b4d8 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -25,6 +25,7 @@ url.workspace = true askama.workspace = true indexmap.workspace = true +scale.workspace = true scale-info.workspace = true scale-value.workspace = true sp-core.workspace = true diff --git a/crates/pop-parachains/src/call/metadata/events.rs b/crates/pop-parachains/src/call/metadata/events.rs new file mode 100644 index 000000000..f15957bcd --- /dev/null +++ b/crates/pop-parachains/src/call/metadata/events.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0 + +use scale::{Decode, Encode}; +use subxt::{ + blocks::ExtrinsicEvents, + events::StaticEvent, + ext::{scale_decode::DecodeAsType, scale_encode::EncodeAsType}, + SubstrateConfig, +}; + +use crate::Error; + +#[derive(Debug, Encode, Decode, DecodeAsType, EncodeAsType)] +#[decode_as_type(crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub struct Reserved { + pub para_id: u32, +} +impl StaticEvent for Reserved { + const PALLET: &'static str = "Registrar"; + const EVENT: &'static str = "Reserved"; +} + +/// Extracts the `para_id` field from a `Reserved` event. +/// +/// # Arguments +/// * `events` - The extrinsic events from a transaction. +pub async fn extract_para_id_from_event( + events: &ExtrinsicEvents, +) -> Result { + let reserved_event = events.find_first::()?; + reserved_event + .map(|event| event.para_id) + .ok_or(Error::EventNotFound("Reserved".to_string())) +} diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 5a9a9a2cb..38f0bfaa7 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -7,6 +7,7 @@ use std::fmt::{Display, Formatter}; use subxt::{dynamic::Value, utils::to_hex, Metadata, OnlineClient, SubstrateConfig}; pub mod action; +pub mod events; pub mod params; /// Represents a pallet in the blockchain, including its dispatchable functions. diff --git a/crates/pop-parachains/src/errors.rs b/crates/pop-parachains/src/errors.rs index 2dd8805ac..0288c4415 100644 --- a/crates/pop-parachains/src/errors.rs +++ b/crates/pop-parachains/src/errors.rs @@ -27,6 +27,9 @@ pub enum Error { CurrentDirAccess, #[error("Failed to parse the endowment value")] EndowmentError, + /// The specified event was not found in the extrinsic events. + #[error("Event {0} not found.")] + EventNotFound(String), /// An error occurred during the submission of an extrinsic. #[error("Extrinsic submission error: {0}")] ExtrinsicSubmissionError(String), diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index fce911856..9e71dcbfe 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -21,6 +21,7 @@ pub use call::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, metadata::{ action::{supported_actions, Action}, + events::extract_para_id_from_event, find_dispatchable_by_name, find_pallet_by_name, params::Param, parse_chain_metadata, Function, Pallet, From d360dba4bde664fdcf9312d1d435dac0e448cb43 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 18:30:39 +0100 Subject: [PATCH 029/117] test: fix detects_parachain_correctly --- crates/pop-cli/src/commands/up/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index e2ee19d07..302b181b3 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -147,7 +147,8 @@ mod tests { async fn detects_parachain_correctly() -> anyhow::Result<()> { let temp_dir = tempfile::tempdir()?; let name = "parachain"; - let project_path = temp_dir.path().join(name); + let path = temp_dir.path(); + let project_path = path.join(name); let config = Config { symbol: "DOT".to_string(), decimals: 18, @@ -155,9 +156,12 @@ mod tests { }; instantiate_template_dir(&Parachain::Standard, &project_path, None, config)?; - let args = create_up_args(project_path)?; - let mut cli = - MockCli::new().expect_warning("Deploy a parachain"); + let mut args = create_up_args(project_path)?; + args.parachain.relay_url = Some(Url::parse("wss://polkadot-rpc.publicnode.com")?); + args.parachain.id = Some(2000); + args.parachain.genesis_code = Some(PathBuf::from("path/to/genesis")); + args.parachain.genesis_state = Some(PathBuf::from("path/to/state")); + let mut cli = MockCli::new(); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); cli.verify() } From 7f9b92638d51172d7774af479d55cb4a5a28d0bc Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 22:53:29 +0100 Subject: [PATCH 030/117] refactor: improve docs and code --- crates/pop-cli/src/commands/build/spec.rs | 54 ++++++++++----------- crates/pop-cli/src/commands/call/chain.rs | 20 ++++++-- crates/pop-cli/src/commands/up/parachain.rs | 4 +- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index b1b84a8ed..269627715 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -508,6 +508,33 @@ impl BuildSpec { Ok("spec") } + /// Generates plain and raw chain specification files. + fn generate_chain_spec( + &self, + binary_path: &PathBuf, + spinner: &ProgressBar, + ) -> anyhow::Result { + let BuildSpec { output_file, chain, .. } = self; + spinner.start("Generating chain specification..."); + + // Generate plain chain spec. + generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; + // Customize spec based on input. + self.customize()?; + + // Generate raw spec. + spinner.set_message("Generating raw chain specification..."); + let spec_name = &output_file + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(DEFAULT_SPEC_NAME) + .trim_end_matches(".json"); + let raw_spec_name = format!("{spec_name}-raw.json"); + let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; + + Ok(raw_chain_spec) + } + /// Generates chain specification files and returns the file paths for the generated genesis /// code and genesis state files. /// @@ -537,33 +564,6 @@ impl BuildSpec { Ok((genesis_state, genesis_code)) } - /// Generates plain and raw chain specification files. - fn generate_chain_spec( - &self, - binary_path: &PathBuf, - spinner: &ProgressBar, - ) -> anyhow::Result { - let BuildSpec { output_file, chain, .. } = self; - spinner.start("Generating chain specification..."); - - // Generate plain chain spec. - generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; - // Customize spec based on input. - self.customize()?; - - // Generate raw spec. - spinner.set_message("Generating raw chain specification..."); - let spec_name = &output_file - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or(DEFAULT_SPEC_NAME) - .trim_end_matches(".json"); - let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; - - Ok(raw_chain_spec) - } - // Customize a chain specification. fn customize(&self) -> anyhow::Result<()> { let mut chain_spec = ChainSpec::from(&self.output_file)?; diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 2c636fe02..8739d84dc 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -110,9 +110,15 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) - .await?; - Ok(()) + match submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + .await + { + Ok(_) => Ok(()), + Err(e) => { + display_message(&e.to_string(), false, &mut cli)?; + break; + }, + } } else { call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await }; @@ -476,7 +482,13 @@ impl Call { } } -// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +/// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +/// +/// # Arguments +/// * `client` - The client used to interact with the chain. +/// * `url` - Endpoint of the node. +/// * `call_data` - The call data to be signed. +/// * `cli` - The CLI implementation to be used. pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 4c67f743e..bc282427d 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -27,10 +27,10 @@ pub struct UpParachainCommand { /// Parachain ID to use. If not specified, a new ID will be reserved. #[arg(short, long)] pub(crate) id: Option, - /// Path to the genesis state file. + /// Path to the genesis state file. If not specified, it will be generated. #[arg(short = 'G', long = "genesis-state")] pub(crate) genesis_state: Option, - /// Path to the genesis code file. + /// Path to the genesis code file. If not specified, it will be generated. #[arg(short = 'C', long = "genesis-code")] pub(crate) genesis_code: Option, /// Websocket endpoint of the relay chain. From ece55079353bae24503d15a50361f0e2bea46fcb Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 23:24:26 +0100 Subject: [PATCH 031/117] test: fix change_working_directory_works --- crates/pop-cli/src/commands/up/parachain.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index bc282427d..6df399891 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -237,6 +237,7 @@ mod tests { #[tokio::test] async fn change_working_directory_works() -> Result<()> { + let original_dir = std::env::current_dir()?; let temp_dir = tempdir()?; let my_parachain_path = Some(temp_dir.path().to_path_buf()); change_working_directory(&my_parachain_path)?; @@ -244,6 +245,9 @@ mod tests { change_working_directory(&None)?; assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + + // Reset working directory back to original + change_working_directory(&Some(original_dir))?; Ok(()) } From e4138717e2f66e86131c3092417b1d77adfcc329 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 18 Feb 2025 09:06:06 +0100 Subject: [PATCH 032/117] fix: clippy warnings --- crates/pop-cli/src/commands/build/spec.rs | 6 +++--- crates/pop-cli/src/commands/call/chain.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 17 ++++++----------- crates/pop-parachains/src/call/mod.rs | 4 ++-- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index 269627715..eed7eb396 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -511,14 +511,14 @@ impl BuildSpec { /// Generates plain and raw chain specification files. fn generate_chain_spec( &self, - binary_path: &PathBuf, + binary_path: &Path, spinner: &ProgressBar, ) -> anyhow::Result { let BuildSpec { output_file, chain, .. } = self; spinner.start("Generating chain specification..."); // Generate plain chain spec. - generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; + generate_plain_chain_spec(binary_path, output_file, self.default_bootnode, chain)?; // Customize spec based on input. self.customize()?; @@ -530,7 +530,7 @@ impl BuildSpec { .unwrap_or(DEFAULT_SPEC_NAME) .trim_end_matches(".json"); let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; + let raw_chain_spec = generate_raw_chain_spec(binary_path, output_file, &raw_spec_name)?; Ok(raw_chain_spec) } diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 8739d84dc..c6835f738 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -510,7 +510,7 @@ pub(crate) async fn submit_extrinsic_with_wallet( spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); Ok(result) } else { - return Err(anyhow!("No signed payload received.")); + Err(anyhow!("No signed payload received.")) } } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 6df399891..f3f061699 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -50,7 +50,7 @@ impl UpParachainCommand { match reserve_para_id(&chain, cli).await { Ok(id) => id, Err(e) => { - cli.outro_cancel(&format!("{}", e))?; + cli.outro_cancel(format!("{}", e))?; return Ok(()); }, } @@ -64,7 +64,7 @@ impl UpParachainCommand { match generate_spec_files(para_id, self.path, cli).await { Ok(files) => files, Err(e) => { - cli.outro_cancel(&format!("Failed to generate spec files: {}", e))?; + cli.outro_cancel(format!("Failed to generate spec files: {}", e))?; return Ok(()); }, } @@ -73,7 +73,7 @@ impl UpParachainCommand { cli.info("Registering a parachain ID")?; if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await { - cli.outro_cancel(&format!("Failed to register parachain: {}", e))?; + cli.outro_cancel(format!("Failed to register parachain: {}", e))?; return Ok(()); } @@ -151,14 +151,9 @@ fn change_working_directory(path: &Option) -> Result<()> { /// Configures the chain specification requirements. async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result { - Ok(BuildSpecCommand { - id: Some(id), - genesis_code: true, - genesis_state: true, - ..Default::default() - } - .configure_build_spec(cli) - .await?) + BuildSpecCommand { id: Some(id), genesis_code: true, genesis_state: true, ..Default::default() } + .configure_build_spec(cli) + .await } /// Registers a parachain by submitting an extrinsic. diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 95a339d05..7ad7bcc07 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -95,13 +95,13 @@ pub async fn submit_signed_extrinsic( let hex_encoded = from_hex(&payload).map_err(|e| Error::CallDataDecodingError(e.to_string()))?; let extrinsic = SubmittableExtrinsic::from_bytes(client, hex_encoded); - Ok(extrinsic + extrinsic .submit_and_watch() .await .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))? .wait_for_finalized_success() .await - .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?) + .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e))) } /// Encodes the call data for a given extrinsic into a hexadecimal string. From 61be242708ad536743d0f20a776b64eca260cdd3 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Tue, 18 Feb 2025 17:36:42 +0100 Subject: [PATCH 033/117] feat: update parachain templates (#297) * feat: include new parachain template and remove old one for Parity * fix: tests * fix: order in test * fix: export PATH * chore: bump zombienet-sdk version * fix: remove parity evm * fix: missing changes * fix: parse collator and parachain-template-node without path for spawn * chore: remove set PATH * refactor: functionality and test * refactor: prefix for external templates * fix: deprecate old command for generating parity contracts template * test: update configure_works test to test new functionality * test: fix unit tests templates * fix: show deprecation message and fixes * docs: improve comments * refactor: nitpicks in parachain description * fix: logic for substrate-contracts-node without path in config file * feat: support v3.0.0 of OpenZeppelin templates * docs: improve template docs and comments * test: improve comments for clarity * fix: support different profiles when path is not specified * refactor: code improvements * refactor: improve import --- crates/pop-cli/src/commands/new/parachain.rs | 11 +- crates/pop-contracts/src/node.rs | 3 +- crates/pop-parachains/src/templates.rs | 61 ++++---- crates/pop-parachains/src/up/mod.rs | 144 ++++++++++++++++++- 4 files changed, 183 insertions(+), 36 deletions(-) diff --git a/crates/pop-cli/src/commands/new/parachain.rs b/crates/pop-cli/src/commands/new/parachain.rs index e65035cfc..22d335a5c 100644 --- a/crates/pop-cli/src/commands/new/parachain.rs +++ b/crates/pop-cli/src/commands/new/parachain.rs @@ -127,10 +127,9 @@ async fn guide_user_to_generate_parachain(verify: bool) -> Result Result<&Parachain> { if i == 0 { prompt = prompt.initial_value(template); } - prompt = prompt.item(template, template.name(), template.description()); + prompt = prompt.item(template, template.name(), template.description().trim()); } Ok(prompt.interact()?) } @@ -471,11 +470,11 @@ mod tests { fn test_is_template_supported() -> Result<()> { is_template_supported(&Provider::Pop, &Parachain::Standard)?; assert!(is_template_supported(&Provider::Pop, &Parachain::ParityContracts).is_err()); - assert!(is_template_supported(&Provider::Pop, &Parachain::ParityFPT).is_err()); + assert!(is_template_supported(&Provider::Pop, &Parachain::ParityGeneric).is_err()); assert!(is_template_supported(&Provider::Parity, &Parachain::Standard).is_err()); is_template_supported(&Provider::Parity, &Parachain::ParityContracts)?; - is_template_supported(&Provider::Parity, &Parachain::ParityFPT) + is_template_supported(&Provider::Parity, &Parachain::ParityGeneric) } #[test] diff --git a/crates/pop-contracts/src/node.rs b/crates/pop-contracts/src/node.rs index 158865d40..53559c94a 100644 --- a/crates/pop-contracts/src/node.rs +++ b/crates/pop-contracts/src/node.rs @@ -48,7 +48,8 @@ pub async fn is_chain_alive(url: url::Url) -> Result { /// A supported chain. #[derive(Debug, EnumProperty, PartialEq, VariantArray)] pub(super) enum Chain { - /// Minimal Substrate node configured for smart contracts via pallet-contracts. + /// Minimal Substrate node configured for smart contracts via pallet-contracts and + /// pallet-revive. #[strum(props( Repository = "https://github.com/paritytech/substrate-contracts-node", Binary = "substrate-contracts-node", diff --git a/crates/pop-parachains/src/templates.rs b/crates/pop-parachains/src/templates.rs index 324f6f23f..676597e15 100644 --- a/crates/pop-parachains/src/templates.rs +++ b/crates/pop-parachains/src/templates.rs @@ -38,7 +38,7 @@ impl Type for Provider { match &self { Provider::Pop => Some(Parachain::Standard), Provider::OpenZeppelin => Some(Parachain::OpenZeppelinGeneric), - Provider::Parity => Some(Parachain::ParityContracts), + Provider::Parity => Some(Parachain::ParityGeneric), } } } @@ -130,7 +130,7 @@ pub enum Parachain { Provider = "OpenZeppelin", Repository = "https://github.com/OpenZeppelin/polkadot-runtime-templates", Network = "./zombienet-config/devnet.toml", - SupportedVersions = "v1.0.0,v2.0.1,v2.0.3", + SupportedVersions = "v1.0.0,v2.0.1,v2.0.3,v3.0.0", IsAudited = "true", License = "GPL-3.0" ) @@ -145,38 +145,39 @@ pub enum Parachain { Provider = "OpenZeppelin", Repository = "https://github.com/OpenZeppelin/polkadot-runtime-templates", Network = "./zombienet-config/devnet.toml", - SupportedVersions = "v2.0.3", + SupportedVersions = "v2.0.3,v3.0.0", IsAudited = "true", License = "GPL-3.0" ) )] OpenZeppelinEVM, - /// Minimal Substrate node configured for smart contracts via pallet-contracts. + /// The Parachain-Ready Template From Polkadot SDK. #[strum( - serialize = "cpt", - message = "Contracts", - detailed_message = "Minimal Substrate node configured for smart contracts via pallet-contracts.", + serialize = "paritytech/polkadot-sdk-parachain-template", + message = "Polkadot SDK's Parachain Template", + detailed_message = "The Parachain-Ready Template From Polkadot SDK.", props( Provider = "Parity", - Repository = "https://github.com/paritytech/substrate-contracts-node", + Repository = "https://github.com/paritytech/polkadot-sdk-parachain-template", Network = "./zombienet.toml", License = "Unlicense" ) )] - ParityContracts, - /// Template node for a Frontier (EVM) based parachain. + ParityGeneric, + /// Minimal Substrate node configured for smart contracts via pallet-contracts and + /// pallet-revive. #[strum( - serialize = "fpt", - message = "EVM", - detailed_message = "Template node for a Frontier (EVM) based parachain.", + serialize = "paritytech/substrate-contracts-node", + message = "Contracts", + detailed_message = "Minimal Substrate node configured for smart contracts via pallet-contracts and pallet-revive.", props( Provider = "Parity", - Repository = "https://github.com/paritytech/frontier-parachain-template", - Network = "./zombienet-config.toml", + Repository = "https://github.com/paritytech/substrate-contracts-node", + Network = "./zombienet.toml", License = "Unlicense" ) )] - ParityFPT, + ParityContracts, // templates for unit tests below #[cfg(test)] #[strum( @@ -256,8 +257,9 @@ mod tests { // openzeppelin ("openzeppelin/generic-template".to_string(), OpenZeppelinGeneric), ("openzeppelin/evm-template".to_string(), OpenZeppelinEVM), - ("cpt".to_string(), ParityContracts), - ("fpt".to_string(), ParityFPT), + // pàrity + ("paritytech/polkadot-sdk-parachain-template".to_string(), ParityGeneric), + ("paritytech/substrate-contracts-node".to_string(), ParityContracts), ("test_01".to_string(), TestTemplate01), ("test_02".to_string(), TestTemplate02), ]) @@ -271,8 +273,8 @@ mod tests { (EVM, "evm".to_string()), (OpenZeppelinGeneric, "generic-template".to_string()), (OpenZeppelinEVM, "evm-template".to_string()), - (ParityContracts, "cpt".to_string()), - (ParityFPT, "fpt".to_string()), + (ParityGeneric, "polkadot-sdk-parachain-template".to_string()), + (ParityContracts, "substrate-contracts-node".to_string()), (TestTemplate01, "test_01".to_string()), (TestTemplate02, "test_02".to_string()), ]) @@ -297,8 +299,15 @@ mod tests { "polkadot-generic-runtime-template".to_string(), "https://github.com/OpenZeppelin/polkadot-runtime-templates", ), + ( + "paritytech/polkadot-sdk-parachain-template".to_string(), + "https://github.com/paritytech/polkadot-sdk-parachain-template", + ), + ( + "paritytech/substrate-contracts-node".to_string(), + "https://github.com/paritytech/substrate-contracts-node", + ), ("cpt".to_string(), "https://github.com/paritytech/substrate-contracts-node"), - ("fpt".to_string(), "https://github.com/paritytech/frontier-parachain-template"), ("test_01".to_string(), ""), ("test_02".to_string(), ""), ]) @@ -312,8 +321,8 @@ mod tests { (EVM, Some("./network.toml")), (OpenZeppelinGeneric, Some("./zombienet-config/devnet.toml")), (OpenZeppelinEVM, Some("./zombienet-config/devnet.toml")), + (ParityGeneric, Some("./zombienet.toml")), (ParityContracts, Some("./zombienet.toml")), - (ParityFPT, Some("./zombienet-config.toml")), (TestTemplate01, Some("")), (TestTemplate02, Some("")), ] @@ -328,8 +337,8 @@ mod tests { (EVM, Some("Unlicense")), (OpenZeppelinGeneric, Some("GPL-3.0")), (OpenZeppelinEVM, Some("GPL-3.0")), + (ParityGeneric, Some("Unlicense")), (ParityContracts, Some("Unlicense")), - (ParityFPT, Some("Unlicense")), (TestTemplate01, Some("Unlicense")), (TestTemplate02, Some("GPL-3.0")), ] @@ -343,7 +352,7 @@ mod tests { assert_eq!(Provider::Pop.provides(&template), true); assert_eq!(Provider::Parity.provides(&template), false); } - if matches!(template, ParityContracts | ParityFPT) { + if matches!(template, ParityContracts | ParityGeneric) { assert_eq!(Provider::Pop.provides(&template), false); assert_eq!(Provider::Parity.provides(&template), true) } @@ -397,7 +406,7 @@ mod tests { let mut provider = Provider::Pop; assert_eq!(provider.default_template(), Some(Standard)); provider = Provider::Parity; - assert_eq!(provider.default_template(), Some(ParityContracts)); + assert_eq!(provider.default_template(), Some(ParityGeneric)); } #[test] @@ -405,7 +414,7 @@ mod tests { let mut provider = Provider::Pop; assert_eq!(provider.templates(), [&Standard, &Assets, &Contracts, &EVM]); provider = Provider::Parity; - assert_eq!(provider.templates(), [&ParityContracts, &ParityFPT]); + assert_eq!(provider.templates(), [&ParityGeneric, &ParityContracts]); } #[test] diff --git a/crates/pop-parachains/src/up/mod.rs b/crates/pop-parachains/src/up/mod.rs index abefa8051..94f0a5066 100644 --- a/crates/pop-parachains/src/up/mod.rs +++ b/crates/pop-parachains/src/up/mod.rs @@ -6,6 +6,7 @@ use indexmap::IndexMap; pub use pop_common::{ git::{GitHub, Repository}, sourcing::{Binary, GitHub::*, Source, Source::*}, + Profile, }; use std::{ fmt::Debug, @@ -13,6 +14,7 @@ use std::{ iter::once, path::{Path, PathBuf}, }; +use strum::VariantArray; use symlink::{remove_symlink_file, symlink_file}; use tempfile::{Builder, NamedTempFile}; use toml_edit::{value, ArrayOfTables, DocumentMut, Formatted, Item, Table, Value}; @@ -153,6 +155,14 @@ impl Zombienet { } } } + // Check if the collator defines a custom launch command + if let Some(collator) = table.get("collator").and_then(|i| i.as_table()) { + if let Some(command) = + NetworkConfiguration::command(collator).and_then(|i| i.as_str()) + { + return Some(Item::Value(Value::String(Formatted::new(command.into())))); + } + } // Otherwise default to polkadot-parachain Some(Item::Value(Value::String(Formatted::new("polkadot-parachain".into())))) @@ -206,6 +216,18 @@ impl Zombienet { continue; } + // Check if command references a parachain template binary without a specified path + // (e.g. Polkadot SDK parachain template) + if ["parachain-template-node", "substrate-contracts-node"].contains(&command.as_str()) { + for profile in Profile::VARIANTS { + let binary_path = profile.target_directory(Path::new("./")).join(&command); + if binary_path.exists() { + paras.insert(id, Parachain::from_local(id, binary_path, chain)?); + continue 'outer; + } + } + return Err(Error::MissingBinary(command)); + } return Err(Error::MissingBinary(command)); } Ok(paras) @@ -479,6 +501,12 @@ impl NetworkConfiguration { } } } + // Resolve individual collator command to binary + if let Some(collator) = table.get_mut("collator").and_then(|p| p.as_table_mut()) { + if let Some(command) = NetworkConfiguration::command_mut(collator) { + *command = value(&path) + } + } } } @@ -622,8 +650,8 @@ fn resolve_manifest(package: &str, path: &Path) -> Result, Error .get("package") .and_then(|i| i.as_table()) .and_then(|t| t.get("name")) - .and_then(|i| i.as_str()) - .map_or(false, |n| n == package) + .and_then(|i| i.as_str()) == + Some(package) }; let mut manifest = Some(path); @@ -676,7 +704,11 @@ fn resolve_manifest(package: &str, path: &Path) -> Result, Error mod tests { use super::*; use anyhow::Result; - use std::{env::current_dir, fs::File, io::Write}; + use std::{ + env::current_dir, + fs::{create_dir_all, remove_dir, remove_file, File}, + io::Write, + }; use tempfile::tempdir; pub(crate) const VERSION: &str = "stable2409"; @@ -1123,6 +1155,83 @@ default_command = "./target/release/parachain-template-node" Ok(()) } + #[tokio::test] + async fn new_with_local_parachain_without_path_works() -> Result<()> { + let temp_dir = tempdir()?; + let cache = PathBuf::from(temp_dir.path()); + let config = Builder::new().suffix(".toml").tempfile()?; + writeln!( + config.as_file(), + r#" +[relaychain] +chain = "rococo-local" + +[[parachains]] +id = 1000 + +[parachains.collator] +name = "collator" +command = "parachain-template-node" + +[[parachains]] +id = 2000 + +[parachains.collator] +name = "collator" +command = "substrate-contracts-node" +"# + )?; + // Expecting failure since no custom path is provided and binaries don't exist in the + // default build directory. + assert!(matches!( + Zombienet::new(&cache, config.path().to_str().unwrap(), None, None, None, None, None).await, + Err(Error::MissingBinary(command)) + if command == "parachain-template-node" + )); + // Create the binaries in the default build directory. + let parachain_template = PathBuf::from("target/release/parachain-template-node"); + create_dir_all(parachain_template.parent().unwrap())?; + File::create(¶chain_template)?; + // Ensure the the binary is detected in the debug profile too. + let parachain_contracts_template = + PathBuf::from("target/debug/substrate-contracts-node"); + create_dir_all(parachain_contracts_template.parent().unwrap())?; + File::create(¶chain_contracts_template)?; + + let zombienet = Zombienet::new( + &cache, + config.path().to_str().unwrap(), + None, + None, + None, + None, + None, + ) + .await?; + // Remove the binaries created above after Zombienet initialization, as they are no + // longer needed. + remove_file(¶chain_template)?; + remove_file(¶chain_contracts_template)?; + remove_dir(parachain_template.parent().unwrap())?; + remove_dir(parachain_contracts_template.parent().unwrap())?; + + assert_eq!(zombienet.parachains.len(), 2); + let parachain = &zombienet.parachains.get(&1000).unwrap().binary; + assert_eq!(parachain.name(), "parachain-template-node"); + assert_eq!(parachain.path(), Path::new("./target/release/parachain-template-node")); + assert_eq!(parachain.version(), None); + assert!(matches!(parachain, Binary::Local { .. })); + let contract_parachain = &zombienet.parachains.get(&2000).unwrap().binary; + assert_eq!(contract_parachain.name(), "substrate-contracts-node"); + assert_eq!( + contract_parachain.path(), + Path::new("./target/debug/substrate-contracts-node") + ); + assert_eq!(contract_parachain.version(), None); + assert!(matches!(contract_parachain, Binary::Local { .. })); + Ok(()) + } + #[tokio::test] async fn new_with_collator_command_works() -> Result<()> { let temp_dir = tempdir()?; @@ -1619,6 +1728,14 @@ default_command = "./target/release/parachain-template-node" [[parachains.collators]] name = "collator" command = "./target/release/parachain-template-node" + +[[parachains]] +id = 2002 +default_command = "./target/release/parachain-template-node" + +[parachains.collator] +name = "collator" +command = "./target/release/parachain-template-node" "# )?; let mut network_config = NetworkConfiguration::from(config.path())?; @@ -1688,6 +1805,19 @@ command = "./target/release/parachain-template-node" chain_spec_generator: None, }, ), + ( + 2002, + Parachain { + id: 2002, + binary: Binary::Local { + name: "parachain-template-node".to_string(), + path: parachain_template.to_path_buf(), + manifest: None, + }, + chain: None, + chain_spec_generator: None, + }, + ), ] .into(), )?; @@ -1733,6 +1863,14 @@ default_command = "{3}" name = "collator" command = "{3}" +[[parachains]] +id = 2002 +default_command = "{3}" + +[parachains.collator] +name = "collator" +command = "{3}" + [settings] timeout = 1000 node_spawn_timeout = 300 From 7d87f17191e66f7d9dbfa689df7749feefff47de Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 09:52:47 +0100 Subject: [PATCH 034/117] refactor: move submit_extrinsic_with_wallet in a common file --- crates/pop-cli/src/commands/call/chain.rs | 39 ++------------------- crates/pop-cli/src/commands/up/parachain.rs | 3 +- crates/pop-cli/src/common/wallet.rs | 29 +++++++++++++++ 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index c6835f738..f418b6a90 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -4,16 +4,15 @@ use std::path::Path; use crate::{ cli::{self, traits::*}, - common::wallet::{prompt_to_use_wallet, request_signature}, + common::wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, - sign_and_submit_extrinsic, submit_signed_extrinsic, supported_actions, Action, CallData, - DynamicPayload, ExtrinsicEvents, Function, OnlineClient, Pallet, Param, Payload, - SubstrateConfig, + sign_and_submit_extrinsic, supported_actions, Action, CallData, DynamicPayload, Function, + OnlineClient, Pallet, Param, Payload, SubstrateConfig, }; use url::Url; @@ -482,38 +481,6 @@ impl Call { } } -/// Sign and submit an extrinsic using wallet integration, then returns the resulting events. -/// -/// # Arguments -/// * `client` - The client used to interact with the chain. -/// * `url` - Endpoint of the node. -/// * `call_data` - The call data to be signed. -/// * `cli` - The CLI implementation to be used. -pub(crate) async fn submit_extrinsic_with_wallet( - client: &OnlineClient, - url: &Url, - call_data: Vec, - cli: &mut impl Cli, -) -> Result> { - let maybe_payload = request_signature(call_data, url.to_string()).await?; - if let Some(payload) = maybe_payload { - cli.success("Signed payload received.")?; - let spinner = cliclack::spinner(); - spinner.start( - "Submitting the extrinsic and then waiting for finalization, please be patient...", - ); - - let result = submit_signed_extrinsic(client.clone(), payload) - .await - .map_err(|err| anyhow!("{}", format!("{err:?}")))?; - - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); - Ok(result) - } else { - Err(anyhow!("No signed payload received.")) - } -} - // Displays a message to the user, with formatting based on the success status. fn display_message(message: &str, success: bool, cli: &mut impl Cli) -> Result<()> { if success { diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index f3f061699..5d45ff17a 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,8 +2,9 @@ use crate::{ build::spec::{BuildSpec, BuildSpecCommand}, - call::chain::{submit_extrinsic_with_wallet, Chain}, + call::chain::Chain, cli::traits::*, + common::wallet::submit_extrinsic_with_wallet, }; use anyhow::{anyhow, Result}; use clap::Args; diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 1aedd0294..5b8f5a83e 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -4,7 +4,10 @@ use crate::{ cli::traits::Cli, wallet_integration::{FrontendFromString, TransactionData, WalletIntegrationManager}, }; +use anyhow::{anyhow, Result}; use cliclack::{log, spinner}; +use pop_parachains::{submit_signed_extrinsic, ExtrinsicEvents, OnlineClient, SubstrateConfig}; +use url::Url; /// The prompt to ask the user if they want to use the wallet for signing. pub const USE_WALLET_PROMPT: &str = "Do you want to use your browser wallet to sign the extrinsic? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')"; @@ -65,3 +68,29 @@ pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { Ok(false) } } + +// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +pub(crate) async fn submit_extrinsic_with_wallet( + client: &OnlineClient, + url: &Url, + call_data: Vec, + cli: &mut impl Cli, +) -> Result> { + let maybe_payload = request_signature(call_data, url.to_string()).await?; + if let Some(payload) = maybe_payload { + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner.start( + "Submitting the extrinsic and then waiting for finalization, please be patient...", + ); + + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); + Ok(result) + } else { + Err(anyhow!("No signed payload received.")) + } +} From ae3d9a4af3fc48dc3168c2d216caf87c9f9a3ff6 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 11:57:21 +0100 Subject: [PATCH 035/117] refactor: remove unnecesary code --- crates/pop-cli/src/commands/build/spec.rs | 5 ++--- crates/pop-cli/src/commands/call/chain.rs | 10 ++-------- crates/pop-cli/src/commands/up/mod.rs | 3 +-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index eed7eb396..fda8379c7 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -530,9 +530,8 @@ impl BuildSpec { .unwrap_or(DEFAULT_SPEC_NAME) .trim_end_matches(".json"); let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(binary_path, output_file, &raw_spec_name)?; - - Ok(raw_chain_spec) + generate_raw_chain_spec(binary_path, output_file, &raw_spec_name) + .map_err(anyhow::Error::from) } /// Generates chain specification files and returns the file paths for the generated genesis diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index f418b6a90..fac36c325 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -109,15 +109,9 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - match submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) .await - { - Ok(_) => Ok(()), - Err(e) => { - display_message(&e.to_string(), false, &mut cli)?; - break; - }, - } + .map(|_| ()) // Mapping to `()` since we don't need events returned } else { call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await }; diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 302b181b3..5fad429e7 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -147,8 +147,7 @@ mod tests { async fn detects_parachain_correctly() -> anyhow::Result<()> { let temp_dir = tempfile::tempdir()?; let name = "parachain"; - let path = temp_dir.path(); - let project_path = path.join(name); + let project_path = temp_dir.path().join(name); let config = Config { symbol: "DOT".to_string(), decimals: 18, From 54a676e2e7c20e94841cb2c90dbfaa37fea3439f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 14:55:45 +0100 Subject: [PATCH 036/117] refactor: UpChainCommand structure --- crates/pop-cli/src/commands/up/mod.rs | 6 +- crates/pop-cli/src/commands/up/parachain.rs | 167 ++++++++++---------- 2 files changed, 90 insertions(+), 83 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 5fad429e7..b9bc4a5bf 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -29,7 +29,7 @@ pub(crate) struct UpArgs { #[command(flatten)] #[cfg(feature = "parachain")] - pub(crate) parachain: parachain::UpParachainCommand, + pub(crate) parachain: parachain::UpChainCommand, #[command(flatten)] #[cfg(feature = "contract")] @@ -93,7 +93,7 @@ impl Command { #[cfg(test)] mod tests { - use super::{contract::UpContractCommand, parachain::UpParachainCommand, *}; + use super::{contract::UpContractCommand, parachain::UpChainCommand, *}; use cli::MockCli; use duct::cmd; @@ -122,7 +122,7 @@ mod tests { skip_confirm: false, valid: false, }, - parachain: UpParachainCommand::default(), + parachain: UpChainCommand::default(), command: None, }) } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 5d45ff17a..83f02865d 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -12,16 +12,15 @@ use pop_parachains::{ construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, Payload, }; - use std::path::PathBuf; use url::Url; const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; -const HELP_HEADER: &str = "Parachain deployment options"; +const HELP_HEADER: &str = "Chain deployment options"; #[derive(Args, Clone, Default)] #[clap(next_help_heading = HELP_HEADER)] -pub struct UpParachainCommand { +pub struct UpChainCommand { /// Path to the chain directory. #[clap(skip)] pub(crate) path: Option, @@ -39,49 +38,32 @@ pub struct UpParachainCommand { pub(crate) relay_url: Option, } -impl UpParachainCommand { +impl UpChainCommand { /// Executes the command. pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { - cli.intro("Deploy a parachain")?; - let chain = self.configure_chain(cli).await?; - let para_id = match self.id { - Some(id) => id, - None => { - cli.info("Reserving a parachain ID...")?; - match reserve_para_id(&chain, cli).await { - Ok(id) => id, - Err(e) => { - cli.outro_cancel(format!("{}", e))?; - return Ok(()); - }, - } + cli.intro("Deploy a chain")?; + let chain_config = match self.prepare_chain_for_registration(cli).await { + Ok(chain) => chain, + Err(e) => { + cli.outro_cancel(format!("{}", e))?; + return Ok(()); }, }; - let (genesis_state, genesis_code) = - match (self.genesis_state.clone(), self.genesis_code.clone()) { - (Some(state), Some(code)) => (state, code), - _ => { - cli.info("Generating the chain spec for your parachain.")?; - match generate_spec_files(para_id, self.path, cli).await { - Ok(files) => files, - Err(e) => { - cli.outro_cancel(format!("Failed to generate spec files: {}", e))?; - return Ok(()); - }, - } - }, - }; - cli.info("Registering a parachain ID")?; - if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await - { - cli.outro_cancel(format!("Failed to register parachain: {}", e))?; - return Ok(()); + match chain_config.register_parachain(cli).await { + Ok(_) => cli.success("Chain deployed successfully")?, + Err(e) => cli.outro_cancel(format!("{}", e))?, } - - cli.outro("Parachain deployment complete.")?; Ok(()) } + // Prepares the chain for registration by setting up its configuration. + async fn prepare_chain_for_registration(self, cli: &mut impl Cli) -> Result { + let chain = self.configure_chain(cli).await?; + let para_id = self.resolve_parachain_id(&chain, cli).await?; + let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; + Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) + } + // Configures the chain by resolving the URL and fetching its metadata. async fn configure_chain(&self, cli: &mut impl Cli) -> Result { // Resolve url. @@ -104,6 +86,63 @@ impl UpParachainCommand { })?; Ok(Chain { url, client, pallets }) } + // Resolves the parachain ID, reserving a new one if necessary. + async fn resolve_parachain_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + match self.id { + Some(id) => Ok(id), + None => { + cli.info("Reserving a parachain ID...")?; + return reserve_para_id(&chain, cli).await; + }, + } + } + // Resolves the genesis state and code files, generating them if necessary. + async fn resolve_genesis_files( + &self, + para_id: u32, + cli: &mut impl Cli, + ) -> Result<(PathBuf, PathBuf)> { + match (self.genesis_state.clone(), self.genesis_code.clone()) { + (Some(state), Some(code)) => Ok((state, code)), + _ => { + cli.info("Generating the chain spec for your parachain.")?; + return generate_spec_files(para_id, self.path.clone(), cli).await; + }, + } + } +} + +// Represents the configuration for deploying a chain. +pub(crate) struct UpChain { + id: u32, + genesis_state: PathBuf, + genesis_code: PathBuf, + chain: Chain, +} +impl UpChain { + // Registers a parachain by submitting an extrinsic. + async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { + cli.info("Registering a parachain ID")?; + let call_data = self.prepare_register_parachain_extrinsic()?; + submit_extrinsic_with_wallet(&self.chain.client, &self.chain.url, call_data, cli).await?; + Ok(()) + } + + // Constructs an extrinsic for registering a parachain. + fn prepare_register_parachain_extrinsic(&self) -> Result> { + let UpChain { id, genesis_code, genesis_state, chain } = self; + let ex = find_dispatchable_by_name( + &chain.pallets, + Action::Register.pallet_name(), + Action::Register.function_name(), + )?; + let state = std::fs::read_to_string(genesis_state) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let code = std::fs::read_to_string(genesis_code) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + Ok(xt.encode_call_data(&chain.client.metadata())?) + } } /// Reserves a parachain ID by submitting an extrinsic. @@ -157,39 +196,6 @@ async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result .await } -/// Registers a parachain by submitting an extrinsic. -async fn register_parachain( - chain: &Chain, - id: u32, - genesis_state: PathBuf, - genesis_code: PathBuf, - cli: &mut impl Cli, -) -> Result<()> { - let call_data = prepare_register_parachain_extrinsic(chain, id, genesis_state, genesis_code)?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(()) -} - -/// Constructs an extrinsic for registering a parachain. -fn prepare_register_parachain_extrinsic( - chain: &Chain, - id: u32, - genesis_state: PathBuf, - genesis_code: PathBuf, -) -> Result> { - let ex = find_dispatchable_by_name( - &chain.pallets, - Action::Register.pallet_name(), - Action::Register.function_name(), - )?; - let state = std::fs::read_to_string(genesis_state) - .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; - let code = std::fs::read_to_string(genesis_code) - .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; - let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; - Ok(xt.encode_call_data(&chain.client.metadata())?) -} - #[cfg(test)] mod tests { use super::*; @@ -212,7 +218,7 @@ mod tests { "Enter the relay chain node URL to deploy your parachain", POLKADOT_NETWORK_URL.into(), ); - let chain = UpParachainCommand::default().configure_chain(&mut cli).await?; + let chain = UpChainCommand::default().configure_chain(&mut cli).await?; assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); cli.verify() } @@ -220,7 +226,7 @@ mod tests { #[tokio::test] async fn prepare_reserve_para_id_extrinsic_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpParachainCommand { + let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), ..Default::default() } @@ -250,7 +256,7 @@ mod tests { #[tokio::test] async fn prepare_register_parachain_extrinsic_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpParachainCommand { + let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), ..Default::default() } @@ -263,12 +269,13 @@ mod tests { std::fs::write(&genesis_state_path, "0x1234")?; std::fs::write(&genesis_code_path, "0x1234")?; - let call_data = prepare_register_parachain_extrinsic( - &chain, - 2000, - genesis_state_path, - genesis_code_path, - )?; + let call_data = UpChain { + id: 2000, + genesis_state: genesis_state_path, + genesis_code: genesis_code_path, + chain, + } + .prepare_register_parachain_extrinsic()?; assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); Ok(()) } From db6e30da21fd763d81174e6888c6b07767838426 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 15:16:29 +0100 Subject: [PATCH 037/117] test: adjust tests to refactored struct --- crates/pop-cli/src/commands/up/parachain.rs | 157 +++++++------------- 1 file changed, 54 insertions(+), 103 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 83f02865d..3ede2e410 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::{BuildSpec, BuildSpecCommand}, + build::spec::BuildSpecCommand, call::chain::Chain, cli::traits::*, common::wallet::submit_extrinsic_with_wallet, @@ -175,51 +175,54 @@ async fn generate_spec_files( path: Option, cli: &mut impl Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { - change_working_directory(&path)?; - let build_spec = configure_build_spec(id, cli).await?; - build_spec.generate_genesis_artifacts(cli) -} - -/// Changes the working directory if a path is provided, ensuring the build spec process runs in the -/// correct context. -fn change_working_directory(path: &Option) -> Result<()> { + // Changes the working directory if a path is provided to ensure the build spec process runs in + // the correct context. if let Some(path) = path { std::env::set_current_dir(path)?; } - Ok(()) -} - -/// Configures the chain specification requirements. -async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result { - BuildSpecCommand { id: Some(id), genesis_code: true, genesis_state: true, ..Default::default() } - .configure_build_spec(cli) - .await + let build_spec = BuildSpecCommand { + id: Some(id), + genesis_code: true, + genesis_state: true, + ..Default::default() + } + .configure_build_spec(cli) + .await?; + build_spec.generate_genesis_artifacts(cli) } #[cfg(test)] mod tests { use super::*; - use crate::{ - build::spec::{ChainType, RelayChain}, - cli::MockCli, - }; - use pop_common::Profile; + use crate::cli::MockCli; use pop_parachains::decode_call_data; - use std::{env, fs}; - use strum::{EnumMessage, VariantArray}; + use std::fs; use tempfile::tempdir; use url::Url; const POLKADOT_NETWORK_URL: &str = "wss://polkadot-rpc.publicnode.com"; + const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; #[tokio::test] - async fn configure_chain_works() -> Result<()> { + async fn prepare_chain_for_registration_works() -> Result<()> { let mut cli = MockCli::new().expect_input( "Enter the relay chain node URL to deploy your parachain", POLKADOT_NETWORK_URL.into(), ); - let chain = UpChainCommand::default().configure_chain(&mut cli).await?; - assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); + let (genesis_state, genesis_code) = create_temp_genesis_files()?; + let chain_config = UpChainCommand { + id: Some(2000), + genesis_state: Some(genesis_state.clone()), + genesis_code: Some(genesis_code.clone()), + ..Default::default() + } + .prepare_chain_for_registration(&mut cli) + .await?; + + assert_eq!(chain_config.id, 2000); + assert_eq!(chain_config.genesis_code, genesis_code); + assert_eq!(chain_config.genesis_state, genesis_state); + assert_eq!(chain_config.chain.url, Url::parse(POLKADOT_NETWORK_URL)?); cli.verify() } @@ -238,19 +241,23 @@ mod tests { } #[tokio::test] - async fn change_working_directory_works() -> Result<()> { - let original_dir = std::env::current_dir()?; - let temp_dir = tempdir()?; - let my_parachain_path = Some(temp_dir.path().to_path_buf()); - change_working_directory(&my_parachain_path)?; - assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); - - change_working_directory(&None)?; - assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + async fn register_parachain_fails_wrong_chain() -> Result<()> { + let mut cli = MockCli::new() + .expect_intro("Deploy a chain") + .expect_info("Registering a parachain ID") + .expect_outro_cancel("Failed to find the pallet Registrar"); + let (genesis_state, genesis_code) = create_temp_genesis_files()?; + UpChainCommand { + id: Some(2000), + genesis_state: Some(genesis_state.clone()), + genesis_code: Some(genesis_code.clone()), + relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + path: None, + } + .execute(&mut cli) + .await?; - // Reset working directory back to original - change_working_directory(&Some(original_dir))?; - Ok(()) + cli.verify() } #[tokio::test] @@ -280,71 +287,15 @@ mod tests { Ok(()) } - #[tokio::test] - async fn configure_build_spec_works() -> Result<()> { - let mut cli = MockCli::new().expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) - .expect_input( - "Name or path for the plain chain spec file:", "output_file".to_string()) - .expect_input( - "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) - .expect_select( - "Choose the chain type: ", - Some(false), - true, - Some(chain_types()), - ChainType::Development as usize, - ).expect_select( - "Choose the relay your chain will be connecting to: ", - Some(false), - true, - Some(relays()), - RelayChain::PaseoLocal as usize, - ).expect_select( - "Choose the build profile of the binary that should be used: ", - Some(false), - true, - Some(profiles()), - Profile::Release as usize, - ); - - configure_build_spec(2000, &mut cli).await?; - cli.verify()?; - Ok(()) - } - - fn relays() -> Vec<(String, String)> { - RelayChain::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } + // Creates temporary files to act as `genesis_state` and `genesis_code` files. + fn create_temp_genesis_files() -> Result<(PathBuf, PathBuf)> { + let temp_dir = tempdir()?; // Create a temporary directory + let genesis_state_path = temp_dir.path().join("genesis_state"); + let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); - fn chain_types() -> Vec<(String, String)> { - ChainType::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } + fs::write(&genesis_state_path, "0x1234")?; + fs::write(&genesis_code_path, "0x1234")?; - fn profiles() -> Vec<(String, String)> { - Profile::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() + Ok((genesis_state_path, genesis_code_path)) } } From 50983c12dd6bca590998240cf4d92766655f098e Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 15:33:32 +0100 Subject: [PATCH 038/117] refactor: renaming prepare_register_parachain_call_data and prepare_rerve_parachain_call_data --- crates/pop-cli/src/commands/build/spec.rs | 2 +- crates/pop-cli/src/commands/call/chain.rs | 6 ++--- crates/pop-cli/src/commands/up/parachain.rs | 30 ++++++++++----------- crates/pop-parachains/src/errors.rs | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index fda8379c7..c78a5c30c 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -508,7 +508,7 @@ impl BuildSpec { Ok("spec") } - /// Generates plain and raw chain specification files. + /// Generates plain and raw chain specification files, and returns the path to the latter. fn generate_chain_spec( &self, binary_path: &Path, diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index fac36c325..c90ea66f2 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -371,8 +371,8 @@ pub(crate) struct Chain { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. -#[derive(Clone)] -struct Call { +#[derive(Clone, Default)] +pub(crate) struct Call { /// The dispatchable function to execute. function: Function, /// The dispatchable function arguments, encoded as strings. @@ -393,7 +393,7 @@ struct Call { impl Call { // Prepares the extrinsic. - fn prepare_extrinsic( + pub(crate) fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 3ede2e410..5904f474b 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, - call::chain::Chain, - cli::traits::*, + build::spec::BuildSpecCommand, call::chain::Chain, cli::traits::*, common::wallet::submit_extrinsic_with_wallet, }; use anyhow::{anyhow, Result}; @@ -123,13 +121,13 @@ impl UpChain { // Registers a parachain by submitting an extrinsic. async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { cli.info("Registering a parachain ID")?; - let call_data = self.prepare_register_parachain_extrinsic()?; + let call_data = self.prepare_register_parachain_call_data()?; submit_extrinsic_with_wallet(&self.chain.client, &self.chain.url, call_data, cli).await?; Ok(()) } - // Constructs an extrinsic for registering a parachain. - fn prepare_register_parachain_extrinsic(&self) -> Result> { + // Prepares and returns the encoded call data for registering a parachain. + fn prepare_register_parachain_call_data(&self) -> Result> { let UpChain { id, genesis_code, genesis_state, chain } = self; let ex = find_dispatchable_by_name( &chain.pallets, @@ -145,10 +143,10 @@ impl UpChain { } } -/// Reserves a parachain ID by submitting an extrinsic. +// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { - let call_data = prepare_reserve_para_id_extrinsic(chain)?; - let events = submit_extrinsic_with_wallet(&chain.client.clone(), &chain.url, call_data, cli) + let call_data = prepare_reserve_parachain_call_data(chain)?; + let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; let para_id = extract_para_id_from_event(&events).await.map_err(|_| { @@ -158,8 +156,8 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { Ok(para_id) } -/// Constructs an extrinsic for reserving a parachain ID. -fn prepare_reserve_para_id_extrinsic(chain: &Chain) -> Result> { +// Prepares and returns the encoded call data for reserving a parachain ID. +fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { let function = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), @@ -169,7 +167,7 @@ fn prepare_reserve_para_id_extrinsic(chain: &Chain) -> Result> { Ok(xt.encode_call_data(&chain.client.metadata())?) } -/// Generates chain spec files for the parachain. +// Generates chain spec files for the parachain. async fn generate_spec_files( id: u32, path: Option, @@ -227,7 +225,7 @@ mod tests { } #[tokio::test] - async fn prepare_reserve_para_id_extrinsic_works() -> Result<()> { + async fn prepare_reserve_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -235,7 +233,7 @@ mod tests { } .configure_chain(&mut cli) .await?; - let call_data = prepare_reserve_para_id_extrinsic(&chain)?; + let call_data = prepare_reserve_parachain_call_data(&chain)?; assert_eq!(call_data, decode_call_data("0x4605")?); Ok(()) } @@ -261,7 +259,7 @@ mod tests { } #[tokio::test] - async fn prepare_register_parachain_extrinsic_works() -> Result<()> { + async fn prepare_register_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -282,7 +280,7 @@ mod tests { genesis_code: genesis_code_path, chain, } - .prepare_register_parachain_extrinsic()?; + .prepare_register_parachain_call_data()?; assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); Ok(()) } diff --git a/crates/pop-parachains/src/errors.rs b/crates/pop-parachains/src/errors.rs index 0288c4415..889bd2be9 100644 --- a/crates/pop-parachains/src/errors.rs +++ b/crates/pop-parachains/src/errors.rs @@ -27,7 +27,7 @@ pub enum Error { CurrentDirAccess, #[error("Failed to parse the endowment value")] EndowmentError, - /// The specified event was not found in the extrinsic events. + /// The specified event was not found. #[error("Event {0} not found.")] EventNotFound(String), /// An error occurred during the submission of an extrinsic. From cfc566e5e380849d566090db5db3e2ac887b1c63 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 15:41:30 +0100 Subject: [PATCH 039/117] refactor: move events module --- crates/pop-cli/src/commands/up/parachain.rs | 2 +- crates/pop-parachains/src/call/metadata/mod.rs | 1 - crates/pop-parachains/src/lib.rs | 2 +- crates/pop-parachains/src/{call/metadata => utils}/events.rs | 4 +--- crates/pop-parachains/src/utils/mod.rs | 1 + crates/pop-parachains/src/utils/onboard.rs | 0 6 files changed, 4 insertions(+), 6 deletions(-) rename crates/pop-parachains/src/{call/metadata => utils}/events.rs (88%) delete mode 100644 crates/pop-parachains/src/utils/onboard.rs diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 5904f474b..3f97df65e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -149,7 +149,7 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; - let para_id = extract_para_id_from_event(&events).await.map_err(|_| { + let para_id = extract_para_id_from_event(&events).map_err(|_| { anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with --id.") })?; cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 38f0bfaa7..5a9a9a2cb 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -7,7 +7,6 @@ use std::fmt::{Display, Formatter}; use subxt::{dynamic::Value, utils::to_hex, Metadata, OnlineClient, SubstrateConfig}; pub mod action; -pub mod events; pub mod params; /// Represents a pallet in the blockchain, including its dispatchable functions. diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 9e71dcbfe..4fe7003b1 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -21,7 +21,6 @@ pub use call::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, metadata::{ action::{supported_actions, Action}, - events::extract_para_id_from_event, find_dispatchable_by_name, find_pallet_by_name, params::Param, parse_chain_metadata, Function, Pallet, @@ -33,6 +32,7 @@ pub use indexmap::IndexSet; pub use new_pallet::{create_pallet_template, new_pallet_options::*, TemplatePalletConfig}; pub use new_parachain::instantiate_template_dir; pub use relay::{clear_dmpq, RelayChain}; +pub use utils::events::extract_para_id_from_event; // External export from subxt. pub use subxt::{ blocks::ExtrinsicEvents, diff --git a/crates/pop-parachains/src/call/metadata/events.rs b/crates/pop-parachains/src/utils/events.rs similarity index 88% rename from crates/pop-parachains/src/call/metadata/events.rs rename to crates/pop-parachains/src/utils/events.rs index f15957bcd..2f47e7e3e 100644 --- a/crates/pop-parachains/src/call/metadata/events.rs +++ b/crates/pop-parachains/src/utils/events.rs @@ -25,9 +25,7 @@ impl StaticEvent for Reserved { /// /// # Arguments /// * `events` - The extrinsic events from a transaction. -pub async fn extract_para_id_from_event( - events: &ExtrinsicEvents, -) -> Result { +pub fn extract_para_id_from_event(events: &ExtrinsicEvents) -> Result { let reserved_event = events.find_first::()?; reserved_event .map(|event| event.para_id) diff --git a/crates/pop-parachains/src/utils/mod.rs b/crates/pop-parachains/src/utils/mod.rs index 265ebafd4..a59eacd9a 100644 --- a/crates/pop-parachains/src/utils/mod.rs +++ b/crates/pop-parachains/src/utils/mod.rs @@ -1,3 +1,4 @@ // SPDX-License-Identifier: GPL-3.0 +pub mod events; pub mod helpers; diff --git a/crates/pop-parachains/src/utils/onboard.rs b/crates/pop-parachains/src/utils/onboard.rs deleted file mode 100644 index e69de29bb..000000000 From 93053df30135191304d38f214b8891da71ac0596 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 16:26:22 +0100 Subject: [PATCH 040/117] fix: submit_extrinsic_with_wallet under parachain feature --- crates/pop-cli/src/common/wallet.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 5b8f5a83e..a648285dd 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -6,6 +6,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use cliclack::{log, spinner}; +#[cfg(feature = "parachain")] use pop_parachains::{submit_signed_extrinsic, ExtrinsicEvents, OnlineClient, SubstrateConfig}; use url::Url; @@ -70,6 +71,7 @@ pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { } // Sign and submit an extrinsic using wallet integration, then returns the resulting events. +#[cfg(feature = "parachain")] pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, From 849dff007ba72e44158b91c7b7c879af7532b463 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 17:07:55 +0100 Subject: [PATCH 041/117] refactor: remove unnecesary code --- crates/pop-cli/src/commands/call/chain.rs | 4 ++-- crates/pop-cli/src/commands/up/parachain.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index c90ea66f2..5a3aacba3 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -372,7 +372,7 @@ pub(crate) struct Chain { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] -pub(crate) struct Call { +pub struct Call { /// The dispatchable function to execute. function: Function, /// The dispatchable function arguments, encoded as strings. @@ -393,7 +393,7 @@ pub(crate) struct Call { impl Call { // Prepares the extrinsic. - pub(crate) fn prepare_extrinsic( + pub fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 3f97df65e..ae329d564 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -90,7 +90,7 @@ impl UpChainCommand { Some(id) => Ok(id), None => { cli.info("Reserving a parachain ID...")?; - return reserve_para_id(&chain, cli).await; + reserve_para_id(chain, cli).await }, } } @@ -104,7 +104,7 @@ impl UpChainCommand { (Some(state), Some(code)) => Ok((state, code)), _ => { cli.info("Generating the chain spec for your parachain.")?; - return generate_spec_files(para_id, self.path.clone(), cli).await; + generate_spec_files(para_id, self.path.clone(), cli).await }, } } From 74cf39056b7c20d048da607f9366cee0b21958f9 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 17:40:09 +0100 Subject: [PATCH 042/117] test: increase coverage with reserve_parachain_id_fails_wrong_chain and resolve_genesis_files_fails_wrong_path --- crates/pop-cli/src/commands/up/parachain.rs | 124 +++++++++++++++++++- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index ae329d564..d06e83d5a 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -89,7 +89,7 @@ impl UpChainCommand { match self.id { Some(id) => Ok(id), None => { - cli.info("Reserving a parachain ID...")?; + cli.info("Reserving a parachain ID")?; reserve_para_id(chain, cli).await }, } @@ -103,7 +103,7 @@ impl UpChainCommand { match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => Ok((state, code)), _ => { - cli.info("Generating the chain spec for your parachain.")?; + cli.info("Generating the chain spec for your parachain")?; generate_spec_files(para_id, self.path.clone(), cli).await }, } @@ -192,9 +192,15 @@ async fn generate_spec_files( #[cfg(test)] mod tests { use super::*; - use crate::cli::MockCli; + use crate::{ + build::spec::{ChainType, RelayChain}, + cli::MockCli, + }; + use duct::cmd; + use pop_common::Profile; use pop_parachains::decode_call_data; - use std::fs; + use std::{env, fs}; + use strum::{EnumMessage, VariantArray}; use tempfile::tempdir; use url::Url; @@ -238,6 +244,80 @@ mod tests { Ok(()) } + #[tokio::test] + async fn reserve_parachain_id_fails_wrong_chain() -> Result<()> { + let mut cli = MockCli::new() + .expect_intro("Deploy a chain") + .expect_info("Reserving a parachain ID") + .expect_outro_cancel("Failed to find the pallet Registrar"); + let (genesis_state, genesis_code) = create_temp_genesis_files()?; + UpChainCommand { + id: None, + genesis_state: Some(genesis_state.clone()), + genesis_code: Some(genesis_code.clone()), + relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + path: None, + } + .execute(&mut cli) + .await?; + + cli.verify() + } + + #[tokio::test] + async fn resolve_genesis_files_fails_wrong_path() -> Result<()> { + // Mock a project path without node. + let name = "hello_world"; + let temp_dir = tempfile::tempdir()?; + let path = temp_dir.path(); + let project_path = path.join(name); + cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; + let original_dir = std::env::current_dir()?; + + let mut cli = MockCli::new() + .expect_intro("Deploy a chain") + .expect_info("Generating the chain spec for your parachain") + .expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) + .expect_input( + "Name or path for the plain chain spec file:", "output_file".to_string()) + .expect_input( + "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) + .expect_select( + "Choose the chain type: ", + Some(false), + true, + Some(chain_types()), + ChainType::Development as usize, + ).expect_select( + "Choose the relay your chain will be connecting to: ", + Some(false), + true, + Some(relays()), + RelayChain::PaseoLocal as usize, + ).expect_select( + "Choose the build profile of the binary that should be used: ", + Some(false), + true, + Some(profiles()), + Profile::Release as usize, + ).expect_outro_cancel(format!("Failed to get manifest path: {}/node/Cargo.toml", fs::canonicalize(&project_path)?.display().to_string())); + + UpChainCommand { + id: Some(2000), + genesis_state: None, + genesis_code: None, + relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + path: Some(project_path.clone()), + } + .execute(&mut cli) + .await?; + + assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(project_path)?); + // Reset working directory back to original + std::env::set_current_dir(original_dir)?; + cli.verify() + } + #[tokio::test] async fn register_parachain_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() @@ -296,4 +376,40 @@ mod tests { Ok((genesis_state_path, genesis_code_path)) } + + fn relays() -> Vec<(String, String)> { + RelayChain::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn chain_types() -> Vec<(String, String)> { + ChainType::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn profiles() -> Vec<(String, String)> { + Profile::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } } From 2548d0f1aeff9b6eab13ff550fbd4af86fe9fde1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:27:40 +0100 Subject: [PATCH 043/117] refactor: remove unnecesary clones --- crates/pop-cli/src/commands/call/chain.rs | 6 +++--- crates/pop-cli/src/commands/up/parachain.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 5a3aacba3..5950e9b19 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -362,11 +362,11 @@ impl CallChainCommand { // Represents a chain, including its URL, client connection, and available pallets. pub(crate) struct Chain { // Websocket endpoint of the node. - pub(crate) url: Url, + pub url: Url, // The client used to interact with the chain. - pub(crate) client: OnlineClient, + pub client: OnlineClient, // A list of pallets available on the chain. - pub(crate) pallets: Vec, + pub pallets: Vec, } /// Represents a configured dispatchable function call, including the pallet, function, arguments, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index d06e83d5a..b803af0f2 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -10,7 +10,7 @@ use pop_parachains::{ construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, Payload, }; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use url::Url; const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; @@ -100,11 +100,11 @@ impl UpChainCommand { para_id: u32, cli: &mut impl Cli, ) -> Result<(PathBuf, PathBuf)> { - match (self.genesis_state.clone(), self.genesis_code.clone()) { - (Some(state), Some(code)) => Ok((state, code)), + match (&self.genesis_state, &self.genesis_code) { + (Some(state), Some(code)) => Ok((state.clone(), code.clone())), _ => { cli.info("Generating the chain spec for your parachain")?; - generate_spec_files(para_id, self.path.clone(), cli).await + generate_spec_files(para_id, self.path.as_deref(), cli).await }, } } @@ -170,7 +170,7 @@ fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { // Generates chain spec files for the parachain. async fn generate_spec_files( id: u32, - path: Option, + path: Option<&Path>, cli: &mut impl Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { // Changes the working directory if a path is provided to ensure the build spec process runs in From 4656511fdb8f21a8c5374aa56422420fd49e3661 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:29:01 +0100 Subject: [PATCH 044/117] refactor: minor improvements --- crates/pop-cli/src/commands/up/parachain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index b803af0f2..84a4050e9 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -150,7 +150,7 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; let para_id = extract_para_id_from_event(&events).map_err(|_| { - anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with --id.") + anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with `--id`.") })?; cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; Ok(para_id) @@ -158,12 +158,12 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { // Prepares and returns the encoded call data for reserving a parachain ID. fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { - let function = find_dispatchable_by_name( + let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), Action::Reserve.function_name(), )?; - let xt = construct_extrinsic(function, Vec::new())?; + let xt = construct_extrinsic(dispatchable, Vec::new())?; Ok(xt.encode_call_data(&chain.client.metadata())?) } From dcbeb6a2cc73154ac6d26b1ba2f56ea35921f4df Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:44:44 +0100 Subject: [PATCH 045/117] test: refactor tests and include comments --- crates/pop-cli/src/commands/up/parachain.rs | 45 +++++++-------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 84a4050e9..ce131eb5e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -240,7 +240,10 @@ mod tests { .configure_chain(&mut cli) .await?; let call_data = prepare_reserve_parachain_call_data(&chain)?; - assert_eq!(call_data, decode_call_data("0x4605")?); + // Encoded call data for a reserve extrinsic. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4605 + let encoded_reserve_extrinsic: &str = "0x4605"; + assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); Ok(()) } @@ -286,19 +289,19 @@ mod tests { "Choose the chain type: ", Some(false), true, - Some(chain_types()), + Some(get_messages(ChainType::VARIANTS)), ChainType::Development as usize, ).expect_select( "Choose the relay your chain will be connecting to: ", Some(false), true, - Some(relays()), + Some(get_messages(RelayChain::VARIANTS)), RelayChain::PaseoLocal as usize, ).expect_select( "Choose the build profile of the binary that should be used: ", Some(false), true, - Some(profiles()), + Some(get_messages(Profile::VARIANTS)), Profile::Release as usize, ).expect_outro_cancel(format!("Failed to get manifest path: {}/node/Cargo.toml", fs::canonicalize(&project_path)?.display().to_string())); @@ -361,7 +364,10 @@ mod tests { chain, } .prepare_register_parachain_call_data()?; - assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); + // Encoded call data for a register extrinsic with the above values. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 + let encoded_reserve_extrinsic: &str = "0x4605"; + assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); Ok(()) } @@ -377,32 +383,9 @@ mod tests { Ok((genesis_state_path, genesis_code_path)) } - fn relays() -> Vec<(String, String)> { - RelayChain::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } - - fn chain_types() -> Vec<(String, String)> { - ChainType::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } - - fn profiles() -> Vec<(String, String)> { - Profile::VARIANTS + // Generic helper function to convert enum variants into (message, detailed message) tuples. + fn get_messages>(variants: &[T]) -> Vec<(String, String)> { + variants .iter() .map(|variant| { ( From 3b9aace4830ce4afc4f60d6c5dccf65af63cf9e4 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:52:26 +0100 Subject: [PATCH 046/117] refactor: map errors in submit_extrinsic_with_wallet --- crates/pop-cli/src/common/wallet.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index a648285dd..8eabd8fdb 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -79,20 +79,16 @@ pub(crate) async fn submit_extrinsic_with_wallet( cli: &mut impl Cli, ) -> Result> { let maybe_payload = request_signature(call_data, url.to_string()).await?; - if let Some(payload) = maybe_payload { - cli.success("Signed payload received.")?; - let spinner = cliclack::spinner(); - spinner.start( - "Submitting the extrinsic and then waiting for finalization, please be patient...", - ); + let payload = maybe_payload.ok_or_else(|| anyhow!("No signed payload received."))?; + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner + .start("Submitting the extrinsic and then waiting for finalization, please be patient..."); - let result = submit_signed_extrinsic(client.clone(), payload) - .await - .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(anyhow::Error::from)?; - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); - Ok(result) - } else { - Err(anyhow!("No signed payload received.")) - } + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); + Ok(result) } From 8bad4304eb17fd28fe161b294c694cf4dc75afa1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 17:05:23 +0100 Subject: [PATCH 047/117] test: fix prepare_register_parachain_call_data_works --- crates/pop-cli/src/commands/up/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index ce131eb5e..7339f08fc 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -366,8 +366,8 @@ mod tests { .prepare_register_parachain_call_data()?; // Encoded call data for a register extrinsic with the above values. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 - let encoded_reserve_extrinsic: &str = "0x4605"; - assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); + let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; + assert_eq!(call_data, decode_call_data(encoded_register_extrinsic)?); Ok(()) } From e5815379720538096222cda7bc7ac2ac3551d4a4 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 23 Feb 2025 18:27:23 +0100 Subject: [PATCH 048/117] refactor: move configure_chain into a common folder --- crates/pop-cli/src/commands/call/chain.rs | 110 ++++++++------------ crates/pop-cli/src/commands/up/parachain.rs | 63 +++++------ crates/pop-cli/src/common/chain.rs | 60 +++++++++++ crates/pop-cli/src/common/mod.rs | 2 + 4 files changed, 134 insertions(+), 101 deletions(-) create mode 100644 crates/pop-cli/src/common/chain.rs diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 5950e9b19..0758ee6da 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -4,15 +4,18 @@ use std::path::Path; use crate::{ cli::{self, traits::*}, - common::wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, + common::{ + chain::{configure_chain, Chain}, + wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, + }, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, - find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, - sign_and_submit_extrinsic, supported_actions, Action, CallData, DynamicPayload, Function, - OnlineClient, Pallet, Param, Payload, SubstrateConfig, + find_dispatchable_by_name, find_pallet_by_name, sign_and_submit_extrinsic, supported_actions, + Action, CallData, DynamicPayload, Function, OnlineClient, Pallet, Param, Payload, + SubstrateConfig, }; use url::Url; @@ -67,10 +70,17 @@ impl CallChainCommand { /// Executes the command. pub(crate) async fn execute(mut self) -> Result<()> { let mut cli = cli::Cli; + cli.intro("Call a chain")?; // Check if all fields are specified via the command line. let prompt_to_repeat_call = self.requires_user_input(); // Configure the chain. - let chain = self.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + DEFAULT_URL, + &self.url, + &mut cli, + ) + .await?; // Execute the call if call_data is provided. if let Some(call_data) = self.call_data.as_ref() { if let Err(e) = self @@ -134,33 +144,6 @@ impl CallChainCommand { Ok(()) } - // Configures the chain by resolving the URL and fetching its metadata. - async fn configure_chain(&self, cli: &mut impl Cli) -> Result { - cli.intro("Call a chain")?; - // Resolve url. - let url = match &self.url { - Some(url) => url.clone(), - None => { - // Prompt for url. - let url: String = cli - .input("Which chain would you like to interact with?") - .default_input(DEFAULT_URL) - .interact()?; - Url::parse(&url)? - }, - }; - - // Parse metadata from chain url. - let client = set_up_client(url.as_str()).await?; - let mut pallets = parse_chain_metadata(&client).map_err(|e| { - anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) - })?; - // Sort by name for display. - pallets.sort_by(|a, b| a.name.cmp(&b.name)); - pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); - Ok(Chain { url, client, pallets }) - } - // Configure the call based on command line arguments/call UI. fn configure_call(&mut self, chain: &Chain, cli: &mut impl Cli) -> Result { loop { @@ -359,16 +342,6 @@ impl CallChainCommand { } } -// Represents a chain, including its URL, client connection, and available pallets. -pub(crate) struct Chain { - // Websocket endpoint of the node. - pub url: Url, - // The client used to interact with the chain. - pub client: OnlineClient, - // A list of pallets available on the chain. - pub pallets: Vec, -} - /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] @@ -646,6 +619,7 @@ fn parse_function_name(name: &str) -> Result { mod tests { use super::*; use crate::{cli::MockCli, common::wallet::USE_WALLET_PROMPT}; + use pop_parachains::{parse_chain_metadata, set_up_client}; use tempfile::tempdir; use url::Url; @@ -653,26 +627,12 @@ mod tests { const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; const POLKADOT_NETWORK_URL: &str = "wss://polkadot-rpc.publicnode.com"; - #[tokio::test] - async fn configure_chain_works() -> Result<()> { - let call_config = - CallChainCommand { suri: Some(DEFAULT_URI.to_string()), ..Default::default() }; - let mut cli = MockCli::new().expect_intro("Call a chain").expect_input( - "Which chain would you like to interact with?", - POP_NETWORK_TESTNET_URL.into(), - ); - let chain = call_config.configure_chain(&mut cli).await?; - assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); - cli.verify() - } - #[tokio::test] async fn guide_user_to_call_chain_works() -> Result<()> { let mut call_config = CallChainCommand { pallet: Some("System".to_string()), ..Default::default() }; let mut cli = MockCli::new() - .expect_intro("Call a chain") .expect_input("Which chain would you like to interact with?", POP_NETWORK_TESTNET_URL.into()) .expect_select( "Select the function to call:", @@ -700,7 +660,13 @@ mod tests { .expect_confirm("Would you like to dispatch this function call with `Root` origin?", true) .expect_confirm(USE_WALLET_PROMPT, true); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &None, + &mut cli, + ) + .await?; assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); let call_chain = call_config.configure_call(&chain, &mut cli)?; @@ -718,11 +684,17 @@ mod tests { async fn guide_user_to_configure_predefined_action_works() -> Result<()> { let mut call_config = CallChainCommand::default(); - let mut cli = MockCli::new().expect_intro("Call a chain").expect_input( + let mut cli = MockCli::new().expect_input( "Which chain would you like to interact with?", POLKADOT_NETWORK_URL.into(), ); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &None, + &mut cli, + ) + .await?; assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); cli.verify()?; @@ -872,20 +844,30 @@ mod tests { sudo: true, }; let mut cli = MockCli::new() - .expect_intro("Call a chain") .expect_warning("NOTE: sudo is not supported by the chain. Ignoring `--sudo` flag."); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) + .await?; call_config.configure_sudo(&chain, &mut cli)?; assert!(!call_config.sudo); cli.verify()?; // Test when sudo pallet exist. - cli = MockCli::new().expect_intro("Call a chain").expect_confirm( + cli = MockCli::new().expect_confirm( "Would you like to dispatch this function call with `Root` origin?", true, ); - call_config.url = Some(Url::parse(POP_NETWORK_TESTNET_URL)?); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + &mut cli, + ) + .await?; call_config.configure_sudo(&chain, &mut cli)?; assert!(call_config.sudo); cli.verify() diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 7339f08fc..e2179fbbe 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, call::chain::Chain, cli::traits::*, - common::wallet::submit_extrinsic_with_wallet, + build::spec::BuildSpecCommand, + cli::traits::*, + common::{ + chain::{configure_chain, Chain}, + wallet::submit_extrinsic_with_wallet, + }, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, - parse_chain_metadata, set_up_client, Action, Payload, + construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, Action, Payload, }; use std::path::{Path, PathBuf}; use url::Url; @@ -56,34 +59,18 @@ impl UpChainCommand { // Prepares the chain for registration by setting up its configuration. async fn prepare_chain_for_registration(self, cli: &mut impl Cli) -> Result { - let chain = self.configure_chain(cli).await?; + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &self.relay_url, + cli, + ) + .await?; let para_id = self.resolve_parachain_id(&chain, cli).await?; let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) } - // Configures the chain by resolving the URL and fetching its metadata. - async fn configure_chain(&self, cli: &mut impl Cli) -> Result { - // Resolve url. - let url = match &self.relay_url { - Some(url) => url.clone(), - None => { - // Prompt for url. - let url: String = cli - .input("Enter the relay chain node URL to deploy your parachain") - .default_input(DEFAULT_URL) - .interact()?; - Url::parse(&url)? - }, - }; - - // Parse metadata from chain url. - let client = set_up_client(url.as_str()).await?; - let pallets = parse_chain_metadata(&client).map_err(|e| { - anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) - })?; - Ok(Chain { url, client, pallets }) - } // Resolves the parachain ID, reserving a new one if necessary. async fn resolve_parachain_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { match self.id { @@ -233,11 +220,12 @@ mod tests { #[tokio::test] async fn prepare_reserve_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpChainCommand { - relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), - ..Default::default() - } - .configure_chain(&mut cli) + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) .await?; let call_data = prepare_reserve_parachain_call_data(&chain)?; // Encoded call data for a reserve extrinsic. @@ -344,11 +332,12 @@ mod tests { #[tokio::test] async fn prepare_register_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpChainCommand { - relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), - ..Default::default() - } - .configure_chain(&mut cli) + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) .await?; // Create a temporary files to act as genesis_state and genesis_code files. let temp_dir = tempdir()?; diff --git a/crates/pop-cli/src/common/chain.rs b/crates/pop-cli/src/common/chain.rs new file mode 100644 index 000000000..d82272548 --- /dev/null +++ b/crates/pop-cli/src/common/chain.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::cli::traits::*; +use anyhow::{anyhow, Result}; +use pop_parachains::{parse_chain_metadata, set_up_client, OnlineClient, Pallet, SubstrateConfig}; +use url::Url; + +// Represents a chain, including its URL, client connection, and available pallets. +pub(crate) struct Chain { + // Websocket endpoint of the node. + pub url: Url, + // The client used to interact with the chain. + pub client: OnlineClient, + // A list of pallets available on the chain. + pub pallets: Vec, +} + +// Configures the chain by resolving the URL and fetching its metadata. +pub(crate) async fn configure_chain( + input_message: &str, + default_input: &str, + url: &Option, + cli: &mut impl Cli, +) -> Result { + // Resolve url. + let url = match url { + Some(url) => url.clone(), + None => { + // Prompt for url. + let url: String = cli.input(input_message).default_input(default_input).interact()?; + Url::parse(&url)? + }, + }; + + // Parse metadata from chain url. + let client = set_up_client(url.as_str()).await?; + let mut pallets = parse_chain_metadata(&client) + .map_err(|e| anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())))?; + // Sort by name for display. + pallets.sort_by(|a, b| a.name.cmp(&b.name)); + pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); + Ok(Chain { url, client, pallets }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::MockCli; + + const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; + + #[tokio::test] + async fn configure_chain_works() -> Result<()> { + let message = "Enter the URL of the chain:"; + let mut cli = MockCli::new().expect_input(message, POP_NETWORK_TESTNET_URL.into()); + let chain = configure_chain(message, POP_NETWORK_TESTNET_URL, &None, &mut cli).await?; + assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); + cli.verify() + } +} diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index 512d2a670..8730cb87f 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-3.0 pub mod builds; +#[cfg(feature = "parachain")] +pub mod chain; #[cfg(feature = "contract")] pub mod contracts; pub mod helpers; From f5cab4d18ffb3978a4fa51a130449019235e15f1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 24 Feb 2025 09:06:00 +0100 Subject: [PATCH 049/117] refactor: function visibility --- crates/pop-cli/src/commands/call/chain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 0758ee6da..c437c78cf 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -345,7 +345,7 @@ impl CallChainCommand { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] -pub struct Call { +struct Call { /// The dispatchable function to execute. function: Function, /// The dispatchable function arguments, encoded as strings. @@ -366,7 +366,7 @@ pub struct Call { impl Call { // Prepares the extrinsic. - pub fn prepare_extrinsic( + fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, From 18abc5b65a89d5e61b155c387d6f764e6ce4f4b9 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 26 Feb 2025 09:47:34 +0100 Subject: [PATCH 050/117] fix: error message and include test for it --- crates/pop-cli/src/commands/up/parachain.rs | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index e2179fbbe..9cf099a1b 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -124,7 +124,7 @@ impl UpChain { let state = std::fs::read_to_string(genesis_state) .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; let code = std::fs::read_to_string(genesis_code) - .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + .map_err(|err| anyhow!("Failed to read genesis code file: {}", err.to_string()))?; let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; Ok(xt.encode_call_data(&chain.client.metadata())?) } @@ -343,20 +343,34 @@ mod tests { let temp_dir = tempdir()?; let genesis_state_path = temp_dir.path().join("genesis_state"); let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); + let up_chain = UpChain { + id: 2000, + genesis_state: genesis_state_path.clone(), + genesis_code: genesis_code_path.clone(), + chain, + }; + + // Expect failure when the genesis state file cannot be read. + assert!(matches!( + up_chain.prepare_register_parachain_call_data(), + Err(message) if message.to_string().contains("Failed to read genesis state file") + )); std::fs::write(&genesis_state_path, "0x1234")?; + + // Expect failure when the genesis code file cannot be read. + assert!(matches!( + up_chain.prepare_register_parachain_call_data(), + Err(message) if message.to_string().contains("Failed to read genesis code file") + )); std::fs::write(&genesis_code_path, "0x1234")?; - let call_data = UpChain { - id: 2000, - genesis_state: genesis_state_path, - genesis_code: genesis_code_path, - chain, - } - .prepare_register_parachain_call_data()?; // Encoded call data for a register extrinsic with the above values. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; - assert_eq!(call_data, decode_call_data(encoded_register_extrinsic)?); + assert_eq!( + up_chain.prepare_register_parachain_call_data()?, + decode_call_data(encoded_register_extrinsic)? + ); Ok(()) } From 7d7e34529301976b647094eb09dffe66a0f15e66 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 25 Feb 2025 19:12:39 +0100 Subject: [PATCH 051/117] feat: construct_proxy_extrinsic --- crates/pop-parachains/src/call/mod.rs | 62 ++++++++++++++++++++++++++- crates/pop-parachains/src/lib.rs | 3 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 7ad7bcc07..b2e4b9b63 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 -use crate::{errors::Error, Function}; +use crate::{errors::Error, Function, Param}; +use metadata::params::field_to_param; use pop_common::{ call::{DefaultEnvironment, DisplayEvents, TokenMetadata, Verbosity}, create_signer, @@ -46,6 +47,45 @@ pub fn construct_sudo_extrinsic(xt: DynamicPayload) -> DynamicPayload { subxt::dynamic::tx("Sudo", "sudo", [xt.into_value()].to_vec()) } +/// Constructs a Proxy call extrinsic. +/// +/// # Arguments +/// * `client` - The client used to interact with the chain. +/// * `proxy_account` - The proxied account that will execute the extrinsic. +/// * `xt`: The extrinsic representing the dispatchable function call to be dispatched using the +/// proxy. +pub fn construct_proxy_extrinsic( + client: &OnlineClient, + proxy_account: String, + xt: DynamicPayload, +) -> Result { + let metadata = client.metadata(); + let proxy_call_fields: Vec = metadata + .pallet_by_name("Proxy") + .and_then(|p| p.call_variant_by_name("proxy")) + .map(|c| { + c.fields + .iter() + .filter(|f| { + // Skip fields where type_name contains "RuntimeCall" + f.type_name.as_deref().map_or(true, |name| !name.contains("RuntimeCall")) + }) + .map(|f| field_to_param(&metadata, f)) + .collect::, Error>>() + }) + .ok_or_else(|| Error::MetadataParsingError("Proxy call".to_string()))??; + let parsed_args: Vec = metadata::parse_dispatchable_arguments( + &proxy_call_fields, + vec![proxy_account, "None()".to_string()], + )?; + + Ok(subxt::dynamic::tx( + "Proxy", + "proxy", + [parsed_args[0].clone(), parsed_args[1].clone(), xt.into_value()].to_vec(), + )) +} + /// Signs and submits a given extrinsic. /// /// # Arguments @@ -169,6 +209,26 @@ mod tests { Ok(()) } + #[tokio::test] + async fn construct_proxy_extrinsic_work() -> Result<()> { + let client = set_up_client(POP_NETWORK_TESTNET_URL).await?; + let pallets = parse_chain_metadata(&client)?; + let remark_dispatchable = find_dispatchable_by_name(&pallets, "System", "remark")?; + let remark = construct_extrinsic(remark_dispatchable, ["0x11".to_string()].to_vec())?; + let xt = construct_proxy_extrinsic( + &client, + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), + remark, + )?; + // Encoded call data for a proxy extrinsic with remark as the call. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc1.paseo.popnetwork.xyz#/extrinsics/decode/0x29000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de0000000411 + assert_eq!( + encode_call_data(&client, &xt)?, + "0x29000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de0000000411" + ); + Ok(()) + } + #[tokio::test] async fn construct_extrinsic_works() -> Result<()> { let client = set_up_client(POP_NETWORK_TESTNET_URL).await?; diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 4fe7003b1..c49e11ee0 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -18,7 +18,8 @@ pub use build::{ generate_plain_chain_spec, generate_raw_chain_spec, is_supported, ChainSpec, }; pub use call::{ - construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, + construct_extrinsic, construct_proxy_extrinsic, construct_sudo_extrinsic, decode_call_data, + encode_call_data, metadata::{ action::{supported_actions, Action}, find_dispatchable_by_name, find_pallet_by_name, From aa5a26ffdefb60dd4c56c659ebe31141ed042315 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 26 Feb 2025 21:35:59 +0100 Subject: [PATCH 052/117] feat: prompt for proxy address in the UI --- crates/pop-cli/src/commands/call/chain.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 187 ++++++++++++++---- .../pop-parachains/src/call/metadata/mod.rs | 1 - crates/pop-parachains/src/call/mod.rs | 32 ++- 4 files changed, 165 insertions(+), 57 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index c437c78cf..11c82a3ea 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -473,7 +473,7 @@ fn prompt_predefined_actions(pallets: &[Pallet], cli: &mut impl Cli) -> Result Result { +pub fn prompt_for_param(cli: &mut impl Cli, param: &Param) -> Result { if param.is_optional { if !cli .confirm(format!( diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 9cf099a1b..499a2900e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,6 +2,7 @@ use crate::{ build::spec::BuildSpecCommand, + call::chain::prompt_for_param, cli::traits::*, common::{ chain::{configure_chain, Chain}, @@ -11,7 +12,8 @@ use crate::{ use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, Action, Payload, + construct_extrinsic, construct_proxy_extrinsic, extract_para_id_from_event, + find_dispatchable_by_name, Action, Payload, }; use std::path::{Path, PathBuf}; use url::Url; @@ -37,6 +39,9 @@ pub struct UpChainCommand { /// Websocket endpoint of the relay chain. #[arg(long)] pub(crate) relay_url: Option, + /// Proxy address for registration. + #[arg(long = "proxy")] + pub(crate) proxy_address: Option, } impl UpChainCommand { @@ -66,18 +71,39 @@ impl UpChainCommand { cli, ) .await?; - let para_id = self.resolve_parachain_id(&chain, cli).await?; + + let proxy = self.resolve_proxy_address(&chain, cli)?; + let para_id = self.resolve_parachain_id(&chain, &proxy, cli).await?; let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; - Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) + Ok(UpChain { id: para_id, genesis_state, genesis_code, chain, proxy }) + } + + // Retrieves the proxy address, prompting the user if none is specified. + fn resolve_proxy_address(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + if let Some(addr) = &self.proxy_address { + return Ok(ProxyConfig::Address(addr.clone())); + } + if cli.confirm("Do you want to use a proxy for registration?").interact()? { + let proxy = find_dispatchable_by_name(&chain.pallets, "Proxy", "proxy")?; + let address = prompt_for_param(cli, &proxy.params[0])?; + Ok(ProxyConfig::Address(address)) + } else { + Ok(ProxyConfig::None) + } } // Resolves the parachain ID, reserving a new one if necessary. - async fn resolve_parachain_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + async fn resolve_parachain_id( + &self, + chain: &Chain, + proxy: &ProxyConfig, + cli: &mut impl Cli, + ) -> Result { match self.id { Some(id) => Ok(id), None => { cli.info("Reserving a parachain ID")?; - reserve_para_id(chain, cli).await + reserve_para_id(chain, proxy, cli).await }, } } @@ -97,12 +123,19 @@ impl UpChainCommand { } } +#[derive(Clone, Debug, PartialEq)] +enum ProxyConfig { + None, + Address(String), +} + // Represents the configuration for deploying a chain. pub(crate) struct UpChain { id: u32, genesis_state: PathBuf, genesis_code: PathBuf, chain: Chain, + proxy: ProxyConfig, } impl UpChain { // Registers a parachain by submitting an extrinsic. @@ -115,7 +148,7 @@ impl UpChain { // Prepares and returns the encoded call data for registering a parachain. fn prepare_register_parachain_call_data(&self) -> Result> { - let UpChain { id, genesis_code, genesis_state, chain } = self; + let UpChain { id, genesis_code, genesis_state, chain, proxy } = self; let ex = find_dispatchable_by_name( &chain.pallets, Action::Register.pallet_name(), @@ -124,15 +157,18 @@ impl UpChain { let state = std::fs::read_to_string(genesis_state) .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; let code = std::fs::read_to_string(genesis_code) - .map_err(|err| anyhow!("Failed to read genesis code file: {}", err.to_string()))?; - let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let mut xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + if let ProxyConfig::Address(addr) = proxy { + xt = construct_proxy_extrinsic(&chain.pallets, addr.to_string(), xt)?; + } Ok(xt.encode_call_data(&chain.client.metadata())?) } } // Reserves a parachain ID by submitting an extrinsic. -async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { - let call_data = prepare_reserve_parachain_call_data(chain)?; +async fn reserve_para_id(chain: &Chain, proxy: &ProxyConfig, cli: &mut impl Cli) -> Result { + let call_data = prepare_reserve_parachain_call_data(chain, proxy)?; let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; @@ -144,13 +180,16 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { } // Prepares and returns the encoded call data for reserving a parachain ID. -fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { +fn prepare_reserve_parachain_call_data(chain: &Chain, proxy: &ProxyConfig) -> Result> { let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), Action::Reserve.function_name(), )?; - let xt = construct_extrinsic(dispatchable, Vec::new())?; + let mut xt = construct_extrinsic(dispatchable, Vec::new())?; + if let ProxyConfig::Address(addr) = proxy { + xt = construct_proxy_extrinsic(&chain.pallets, addr.to_string(), xt)?; + } Ok(xt.encode_call_data(&chain.client.metadata())?) } @@ -205,6 +244,7 @@ mod tests { id: Some(2000), genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), + proxy_address: Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()), ..Default::default() } .prepare_chain_for_registration(&mut cli) @@ -214,6 +254,72 @@ mod tests { assert_eq!(chain_config.genesis_code, genesis_code); assert_eq!(chain_config.genesis_state, genesis_state); assert_eq!(chain_config.chain.url, Url::parse(POLKADOT_NETWORK_URL)?); + assert_eq!( + chain_config.proxy, + ProxyConfig::Address( + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string() + ) + ); + cli.verify() + } + + #[tokio::test] + async fn resolve_proxy_address_works() -> Result<()> { + let mut cli = MockCli::new() + .expect_confirm("Do you want to use a proxy for registration?", true) + .expect_select( + "Select the value for the parameter: real", + Some(true), + true, + Some( + [ + ("Id".to_string(), "".to_string()), + ("Index".to_string(), "".to_string()), + ("Raw".to_string(), "".to_string()), + ("Address32".to_string(), "".to_string()), + ("Address20".to_string(), "".to_string()), + ] + .to_vec(), + ), + 0, // "Id" action + ) + .expect_input( + "Enter the value for the parameter: Id", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty".into(), + ); + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) + .await?; + let proxy_address = UpChainCommand::default().resolve_proxy_address(&chain, &mut cli)?; + assert_eq!( + proxy_address, + ProxyConfig::Address( + "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string() + ) + ); + cli.verify()?; + + cli = MockCli::new().expect_confirm("Do you want to use a proxy for registration?", false); + let proxy_address = UpChainCommand::default().resolve_proxy_address(&chain, &mut cli)?; + assert_eq!(proxy_address, ProxyConfig::None); + cli.verify()?; + + cli = MockCli::new(); + let proxy_address = UpChainCommand { + proxy_address: Some("Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string()), + ..Default::default() + } + .resolve_proxy_address(&chain, &mut cli)?; + assert_eq!( + proxy_address, + ProxyConfig::Address( + "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string() + ) + ); cli.verify() } @@ -227,11 +333,24 @@ mod tests { &mut cli, ) .await?; - let call_data = prepare_reserve_parachain_call_data(&chain)?; + let call_data = prepare_reserve_parachain_call_data(&chain, &ProxyConfig::None)?; // Encoded call data for a reserve extrinsic. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4605 let encoded_reserve_extrinsic: &str = "0x4605"; assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); + + // Ensure `prepare_reserve_parachain_call_data` works with a proxy. + let call_data = prepare_reserve_parachain_call_data( + &chain, + &ProxyConfig::Address( + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), + ), + )?; + // Encoded call data for a proxy extrinsic with reserve as the call. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x1d000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de004605 + let encoded_reserve_extrinsic: &str = + "0x1d000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de004605"; + assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); Ok(()) } @@ -248,6 +367,7 @@ mod tests { genesis_code: Some(genesis_code.clone()), relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: None, + proxy_address: None, } .execute(&mut cli) .await?; @@ -299,6 +419,7 @@ mod tests { genesis_code: None, relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: Some(project_path.clone()), + proxy_address: None, } .execute(&mut cli) .await?; @@ -322,6 +443,7 @@ mod tests { genesis_code: Some(genesis_code.clone()), relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: None, + proxy_address: None, } .execute(&mut cli) .await?; @@ -343,34 +465,31 @@ mod tests { let temp_dir = tempdir()?; let genesis_state_path = temp_dir.path().join("genesis_state"); let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); - let up_chain = UpChain { - id: 2000, - genesis_state: genesis_state_path.clone(), - genesis_code: genesis_code_path.clone(), - chain, - }; - - // Expect failure when the genesis state file cannot be read. - assert!(matches!( - up_chain.prepare_register_parachain_call_data(), - Err(message) if message.to_string().contains("Failed to read genesis state file") - )); std::fs::write(&genesis_state_path, "0x1234")?; - - // Expect failure when the genesis code file cannot be read. - assert!(matches!( - up_chain.prepare_register_parachain_call_data(), - Err(message) if message.to_string().contains("Failed to read genesis code file") - )); std::fs::write(&genesis_code_path, "0x1234")?; + let mut up_chain = UpChain { + id: 2000, + genesis_state: genesis_state_path, + genesis_code: genesis_code_path, + chain, + proxy: ProxyConfig::None, + }; + let call_data = up_chain.prepare_register_parachain_call_data()?; // Encoded call data for a register extrinsic with the above values. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; - assert_eq!( - up_chain.prepare_register_parachain_call_data()?, - decode_call_data(encoded_register_extrinsic)? + assert_eq!(call_data, decode_call_data(encoded_register_extrinsic)?); + + // Ensure `prepare_register_parachain_call_data` works with a proxy. + up_chain.proxy = ProxyConfig::Address( + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), ); + let call_data = up_chain.prepare_register_parachain_call_data()?; + // Encoded call data for a proxy extrinsic with register as the call. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x1d000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de004600d0070000081234081234 + let encoded_reserve_extrinsic: &str = "0x1d000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de004600d0070000081234081234"; + assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); Ok(()) } diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 5a9a9a2cb..1ce4e3c0d 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -82,7 +82,6 @@ pub fn parse_chain_metadata(client: &OnlineClient) -> Result DynamicPayload { /// Constructs a Proxy call extrinsic. /// /// # Arguments -/// * `client` - The client used to interact with the chain. +/// * `pallets`: List of pallets available within the chain's runtime. /// * `proxy_account` - The proxied account that will execute the extrinsic. /// * `xt`: The extrinsic representing the dispatchable function call to be dispatched using the /// proxy. pub fn construct_proxy_extrinsic( - client: &OnlineClient, + pallets: &[Pallet], proxy_account: String, xt: DynamicPayload, ) -> Result { - let metadata = client.metadata(); - let proxy_call_fields: Vec = metadata - .pallet_by_name("Proxy") - .and_then(|p| p.call_variant_by_name("proxy")) - .map(|c| { - c.fields - .iter() - .filter(|f| { - // Skip fields where type_name contains "RuntimeCall" - f.type_name.as_deref().map_or(true, |name| !name.contains("RuntimeCall")) - }) - .map(|f| field_to_param(&metadata, f)) - .collect::, Error>>() - }) - .ok_or_else(|| Error::MetadataParsingError("Proxy call".to_string()))??; + let proxy_function = find_dispatchable_by_name(&pallets, "Proxy", "proxy")?; + // `find_dispatchable_by_name` doesn't support parsing parameters that are calls. + // Therefore, we only parse the first two parameters for the proxy call + // using `parse_dispatchable_arguments`, while the last parameter (which is the call) + // must be manually added. + let required_params: Vec = proxy_function.params.iter().take(2).cloned().collect(); let parsed_args: Vec = metadata::parse_dispatchable_arguments( - &proxy_call_fields, + &required_params, vec![proxy_account, "None()".to_string()], )?; @@ -216,7 +206,7 @@ mod tests { let remark_dispatchable = find_dispatchable_by_name(&pallets, "System", "remark")?; let remark = construct_extrinsic(remark_dispatchable, ["0x11".to_string()].to_vec())?; let xt = construct_proxy_extrinsic( - &client, + &pallets, "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), remark, )?; From 10a879a1168c17e52a3f68265e4e00e2a4565ab1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 27 Feb 2025 09:33:37 +0100 Subject: [PATCH 053/117] docs: improve flag description and display command to be executed --- crates/pop-cli/src/commands/up/parachain.rs | 54 ++++++++++++++++++++- crates/pop-parachains/src/call/mod.rs | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 499a2900e..6d374abbe 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -39,7 +39,8 @@ pub struct UpChainCommand { /// Websocket endpoint of the relay chain. #[arg(long)] pub(crate) relay_url: Option, - /// Proxy address for registration. + /// Proxy address for registration. Specify the address type, e.g. + /// `Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)`. #[arg(long = "proxy")] pub(crate) proxy_address: Option, } @@ -55,6 +56,7 @@ impl UpChainCommand { return Ok(()); }, }; + chain_config.display(); match chain_config.register_parachain(cli).await { Ok(_) => cli.success("Chain deployed successfully")?, Err(e) => cli.outro_cancel(format!("{}", e))?, @@ -164,6 +166,18 @@ impl UpChain { } Ok(xt.encode_call_data(&chain.client.metadata())?) } + + fn display(&self) -> String { + let mut full_message = "pop up".to_string(); + full_message.push_str(&format!(" --id {}", self.id)); + full_message.push_str(&format!(" --genesis-state {}", self.genesis_state.display())); + full_message.push_str(&format!(" --genesis-code {}", self.genesis_code.display())); + full_message.push_str(&format!(" --relay-url {}", self.chain.url)); + if let ProxyConfig::Address(addr) = &self.proxy { + full_message.push_str(&format!(" --proxy {}", addr)); + } + full_message + } } // Reserves a parachain ID by submitting an extrinsic. @@ -493,6 +507,44 @@ mod tests { Ok(()) } + #[tokio::test] + async fn display_works() -> Result<()> { + let mut cli = MockCli::new(); + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) + .await?; + // Create a temporary files to act as genesis_state and genesis_code files. + let temp_dir = tempdir()?; + let genesis_state_path = temp_dir.path().join("genesis_state"); + let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); + + let up_chain = UpChain { + id: 2000, + genesis_state: genesis_state_path, + genesis_code: genesis_code_path, + chain, + proxy: ProxyConfig::Address( + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), + ), + }; + assert_eq!( + up_chain.display(), + format!( + "pop up --id {} --genesis-state {} --genesis-code {} --relay-url {} --proxy {}", + up_chain.id, + up_chain.genesis_state.display(), + up_chain.genesis_code.display(), + up_chain.chain.url, + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)" + ) + ); + Ok(()) + } + // Creates temporary files to act as `genesis_state` and `genesis_code` files. fn create_temp_genesis_files() -> Result<(PathBuf, PathBuf)> { let temp_dir = tempdir()?; // Create a temporary directory diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 44ba4c530..24d862409 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -58,7 +58,7 @@ pub fn construct_proxy_extrinsic( proxy_account: String, xt: DynamicPayload, ) -> Result { - let proxy_function = find_dispatchable_by_name(&pallets, "Proxy", "proxy")?; + let proxy_function = find_dispatchable_by_name(pallets, "Proxy", "proxy")?; // `find_dispatchable_by_name` doesn't support parsing parameters that are calls. // Therefore, we only parse the first two parameters for the proxy call // using `parse_dispatchable_arguments`, while the last parameter (which is the call) From 3dad2edba970075e936feecc309e3ec01a690e56 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 27 Feb 2025 18:09:28 +0100 Subject: [PATCH 054/117] docs: improve prompt to the user message for proxy --- crates/pop-cli/src/commands/up/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 6d374abbe..26d83745f 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -85,7 +85,7 @@ impl UpChainCommand { if let Some(addr) = &self.proxy_address { return Ok(ProxyConfig::Address(addr.clone())); } - if cli.confirm("Do you want to use a proxy for registration?").interact()? { + if cli.confirm("Would you like to use a proxy for registration? This is considered a best practice.").interact()? { let proxy = find_dispatchable_by_name(&chain.pallets, "Proxy", "proxy")?; let address = prompt_for_param(cli, &proxy.params[0])?; Ok(ProxyConfig::Address(address)) @@ -280,7 +280,7 @@ mod tests { #[tokio::test] async fn resolve_proxy_address_works() -> Result<()> { let mut cli = MockCli::new() - .expect_confirm("Do you want to use a proxy for registration?", true) + .expect_confirm("Would you like to use a proxy for registration? This is considered a best practice.", true) .expect_select( "Select the value for the parameter: real", Some(true), From 4e903fbee581a4185e2fcab33516095ce13c5bd6 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 27 Feb 2025 18:11:26 +0100 Subject: [PATCH 055/117] feat: replace index.html with costs for calls with proxy in ui --- crates/pop-cli/src/assets/index.html | 200 +++++++++++++-------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/crates/pop-cli/src/assets/index.html b/crates/pop-cli/src/assets/index.html index 2b4430f8b..668758c5e 100644 --- a/crates/pop-cli/src/assets/index.html +++ b/crates/pop-cli/src/assets/index.html @@ -7,7 +7,7 @@ href="data:image/svg+xml,%3Csvg width='512' height='512' viewBox='0 0 512 512' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_873_174)'%3E%3Crect width='512' height='512' rx='256' fill='%231C0533'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint0_radial_873_174)' fill-opacity='0.8'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint1_radial_873_174)' fill-opacity='0.6'/%3E%3Cmask id='mask0_873_174' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='-429' y='-502' width='1428' height='1351'%3E%3Cpath d='M127.637 -501.289L998.911 322.903L984.661 336.383L113.388 -487.809L127.637 -501.289Z' fill='%23D9D9D9'/%3E%3Cpath d='M88.9594 -464.701L960.232 359.491L945.983 372.971L74.7096 -451.221L88.9594 -464.701Z' fill='%23D9D9D9'/%3E%3Cpath d='M50.2814 -428.113L921.554 396.079L907.305 409.559L36.0316 -414.633L50.2814 -428.113Z' fill='%23D9D9D9'/%3E%3Cpath d='M11.6034 -391.525L882.876 432.667L868.627 446.147L-2.64642 -378.045L11.6034 -391.525Z' fill='%23D9D9D9'/%3E%3Cpath d='M-27.0746 -354.937L844.198 469.255L829.949 482.735L-41.3244 -341.457L-27.0746 -354.937Z' fill='%23D9D9D9'/%3E%3Cpath d='M-65.7526 -318.349L805.52 505.843L791.271 519.323L-80.0024 -304.869L-65.7526 -318.349Z' fill='%23D9D9D9'/%3E%3Cpath d='M-104.431 -281.761L766.842 542.431L752.593 555.911L-118.68 -268.281L-104.431 -281.761Z' fill='%23D9D9D9'/%3E%3Cpath d='M-143.109 -245.173L728.164 579.019L713.915 592.499L-157.358 -231.693L-143.109 -245.173Z' fill='%23D9D9D9'/%3E%3Cpath d='M-181.787 -208.585L689.486 615.607L675.237 629.087L-196.036 -195.105L-181.787 -208.585Z' fill='%23D9D9D9'/%3E%3Cpath d='M-220.465 -171.997L650.808 652.195L636.559 665.675L-234.714 -158.517L-220.465 -171.997Z' fill='%23D9D9D9'/%3E%3Cpath d='M-259.143 -135.409L612.13 688.783L597.881 702.263L-273.392 -121.929L-259.143 -135.409Z' fill='%23D9D9D9'/%3E%3Cpath d='M-297.821 -98.8211L573.452 725.371L559.203 738.851L-312.07 -85.3413L-297.821 -98.8211Z' fill='%23D9D9D9'/%3E%3Cpath d='M-336.499 -62.2331L534.774 761.959L520.525 775.439L-350.748 -48.7533L-336.499 -62.2331Z' fill='%23D9D9D9'/%3E%3Cpath d='M-375.177 -25.6452L496.096 798.547L481.847 812.026L-389.426 -12.1654L-375.177 -25.6452Z' fill='%23D9D9D9'/%3E%3Cpath d='M-413.855 10.9428L457.418 835.135L443.169 848.615L-428.104 24.4225L-413.855 10.9428Z' fill='%23D9D9D9'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_873_174)'%3E%3Cpath d='M511.169 254.929C511.169 396.905 396.739 512 255.584 512C114.428 512 -0.000976562 396.905 -0.000976562 254.929C-0.000976562 112.953 114.428 -2.14209 255.584 -2.14209C396.739 -2.14209 511.169 112.953 511.169 254.929Z' fill='%23E6007A'/%3E%3C/g%3E%3Cpath d='M183.804 160.44C171.052 151.144 156.458 147.168 141.864 147.168H52.2334L5.63086 367.567H122.684L132.083 322.765C141.879 321.622 151.384 318.84 160.403 314.394C161.8 313.705 163.178 312.986 164.536 312.239C168.249 321.014 173.177 329.173 179.315 336.581C187.573 346.548 197.657 354.366 209.117 360.016L209.12 360.018L209.517 360.213L209.917 360.399C222.605 366.289 236.098 368.784 249.63 368.784C263.218 368.784 276.507 366.489 289.292 361.868C295.36 359.675 301.212 357.046 306.832 353.988L303.961 367.567H421.013L430.413 322.765C440.209 321.622 449.714 318.84 458.733 314.394C469.61 309.03 479.317 301.877 487.672 293.046L487.682 293.035L487.841 292.867L487.995 292.7L487.998 292.697C496.09 283.931 502.611 273.995 507.338 262.975C512.203 251.631 514.88 239.536 514.88 226.953C514.88 214.447 512.231 202.327 507.001 190.977C501.508 178.753 493.141 168.335 482.134 160.44C469.382 151.144 454.788 147.168 440.194 147.168H350.563L345.323 171.949C338.297 165.041 330.227 159.445 321.314 155.191C308.55 148.958 294.91 146.357 281.28 146.357C267.691 146.357 254.402 148.652 241.618 153.273C229.473 157.663 218.174 163.78 207.814 171.541C205.377 173.358 203.011 175.252 200.715 177.221C196.062 170.823 190.408 165.177 183.804 160.44Z' fill='%232B0532'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M229.569 331.163L229.632 331.194L229.696 331.224C237.592 334.889 246.407 336.65 256.019 336.65C265.979 336.65 275.609 334.978 284.877 331.628C294.055 328.31 302.528 323.689 310.277 317.771C317.967 311.898 324.68 305.15 330.397 297.531L330.413 297.509L330.429 297.487C336.137 289.737 340.585 281.451 343.748 272.638C347.074 263.611 348.753 254.388 348.753 244.999C348.753 236.541 347.45 228.398 344.825 220.6C342.311 212.598 338.452 205.423 333.24 199.132C327.963 192.763 321.484 187.769 313.875 184.16C306.078 180.325 297.295 178.491 287.67 178.491C277.709 178.491 268.079 180.164 258.812 183.513C249.647 186.826 241.125 191.437 233.261 197.332C225.542 203.084 218.751 209.844 212.899 217.597L212.889 217.611C207.16 225.246 202.642 233.54 199.345 242.478L199.33 242.518L199.316 242.559C196.135 251.575 194.532 260.778 194.532 270.142C194.532 278.624 195.843 286.844 198.472 294.776L198.477 294.792L198.482 294.808C201.135 302.663 205.057 309.744 210.248 316.009C215.517 322.37 221.981 327.422 229.569 331.163ZM287.732 276.015L287.742 276C290.74 271.657 292.939 267.045 294.364 262.148C295.831 257.108 296.54 252.276 296.54 247.635C296.54 243.3 295.843 239.458 294.528 236.043L294.487 235.934L294.449 235.824C293.328 232.545 291.583 230.131 289.252 228.349C287.226 226.801 284.199 225.736 279.606 225.736C274.271 225.736 269.81 226.979 266.027 229.305L265.974 229.337L265.921 229.368C261.869 231.746 258.495 234.905 255.765 238.909C252.893 243.123 250.673 247.698 249.104 252.654C247.65 257.67 246.947 262.48 246.947 267.101C246.947 271.459 247.585 275.422 248.809 279.033C250.055 282.339 251.89 284.885 254.293 286.836C256.34 288.379 259.301 289.405 263.68 289.405C269.175 289.405 273.697 288.15 277.46 285.837C281.567 283.313 284.975 280.058 287.722 276.029L287.732 276.015Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M171.542 186.53C164.879 181.64 157.017 179.302 148.254 179.302H84.4909L51.4775 335.434H103.163L112.436 291.23H128.094C136.792 291.23 145.029 289.337 152.73 285.54C160.225 281.844 166.856 276.954 172.594 270.888L172.619 270.861L172.644 270.834C178.339 264.665 182.824 257.798 186.062 250.247C189.323 242.645 190.992 234.866 190.992 226.953C190.992 219.052 189.327 211.496 186.009 204.348C182.762 197.062 177.927 191.083 171.542 186.53ZM135.575 242.131L135.598 242.101C136.363 241.1 137.06 239.71 137.567 237.773L137.604 237.631L137.648 237.49C138.222 235.663 138.577 233.269 138.577 230.197C138.577 227.864 138.266 226.771 138.111 226.428C138.024 226.235 137.948 226.093 137.889 225.994L137.862 225.949L137.849 225.939H126.349L122.444 244.594H132.693C132.756 244.581 132.999 244.516 133.45 244.244C133.961 243.935 134.685 243.325 135.553 242.16L135.575 242.131Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M469.872 186.53C463.209 181.64 455.347 179.302 446.583 179.302H382.821L349.807 335.434H401.493L410.766 291.23H426.424C435.122 291.23 443.359 289.337 451.06 285.54C458.555 281.844 465.186 276.954 470.923 270.888L470.949 270.861L470.973 270.834C476.669 264.665 481.153 257.798 484.392 250.247C487.652 242.645 489.322 234.866 489.322 226.953C489.322 219.052 487.657 211.496 484.339 204.348C481.092 197.062 476.257 191.083 469.872 186.53ZM433.905 242.131L433.928 242.101C434.693 241.1 435.39 239.71 435.896 237.773L435.934 237.631L435.978 237.49C436.552 235.663 436.907 233.269 436.907 230.197C436.907 227.864 436.596 226.771 436.441 226.428C436.354 226.235 436.278 226.093 436.219 225.994L436.192 225.949L436.178 225.939H424.678L420.774 244.594H431.023C431.086 244.581 431.329 244.516 431.779 244.244C432.29 243.935 433.014 243.325 433.883 242.16L433.905 242.131Z' fill='%23E6007A'/%3E%3Cmask id='path-6-outside-1_873_174' maskUnits='userSpaceOnUse' x='35.8232' y='166.788' width='435' height='156' fill='black'%3E%3Crect fill='white' x='35.8232' y='166.788' width='435' height='156'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z'/%3E%3C/mask%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' stroke='white' stroke-width='0.3' mask='url(%23path-6-outside-1_873_174)'/%3E%3C/g%3E%3Cdefs%3E%3CradialGradient id='paint0_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(-62.8 -86.4) rotate(70.436) scale(686.854 394.059)'%3E%3Cstop stop-color='%236335EC'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='paint1_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(640.4 677.6) rotate(-116.448) scale(1055.25 662.094)'%3E%3Cstop stop-color='%23E6007A'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='clip0_873_174'%3E%3Crect width='512' height='512' rx='256' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A" /> Pop CLI Signing Portal - - + `],K);pj=function(e,t,n,r){var o,i=arguments.length,s=i<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(i<3?o(s):i>3?o(t,n,s):o(t,n))||s);return i>3&&s&&Object.defineProperty(t,n,s),s}([TA("dc-connection-button")],pj);const hj={chains:{},wallets:[new class extends YE{constructor(e){super(),X(this,"options"),this.options=e}async getWallets(){return await lS,(()=>{const{injectedWeb3:e}=window;return e?Object.keys(e):[]})().map((e=>new aS(e,this.options)))}}]};function vj(e,t,{checkForDefaultPrevented:n=!0}={}){return function(r){if(null==e||e(r),!1===n||!r.defaultPrevented)return null==t?void 0:t(r)}}function mj(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function gj(...e){return t=>{let n=!1;const r=e.map((e=>{const r=mj(e,t);return n||"function"!=typeof r||(n=!0),r}));if(n)return()=>{for(let t=0;t{const t=n.map((e=>qe.createContext(e)));return function(n){const r=(null==n?void 0:n[e])||t;return qe.useMemo((()=>({[`__scope${e}`]:{...n,[e]:r}})),[n,r])}};return r.scopeName=e,[function(t,r){const o=qe.createContext(r),i=n.length;n=[...n,r];const s=t=>{var n;const{scope:r,children:s,...a}=t,l=(null==(n=null==r?void 0:r[e])?void 0:n[i])||o,c=qe.useMemo((()=>a),Object.values(a));return nt.jsx(l.Provider,{value:c,children:s})};return s.displayName=t+"Provider",[s,function(n,s){var a;const l=(null==(a=null==s?void 0:s[e])?void 0:a[i])||o,c=qe.useContext(l);if(c)return c;if(void 0!==r)return r;throw new Error(`\`${n}\` must be used within \`${t}\``)}]},wj(r,...t)]}function wj(...e){const t=e[0];if(1===e.length)return t;const n=()=>{const n=e.map((e=>({useScope:e(),scopeName:e.scopeName})));return function(e){const r=n.reduce(((t,{useScope:n,scopeName:r})=>({...t,...n(e)[`__scope${r}`]})),{});return qe.useMemo((()=>({[`__scope${t.scopeName}`]:r})),[r])}};return n.scopeName=t.scopeName,n}function xj(e){const t=qe.useRef(e);return qe.useEffect((()=>{t.current=e})),qe.useMemo((()=>(...e)=>{var n;return null==(n=t.current)?void 0:n.call(t,...e)}),[])}function kj({prop:e,defaultProp:t,onChange:n=()=>{}}){const[r,o]=function({defaultProp:e,onChange:t}){const n=qe.useState(e),[r]=n,o=qe.useRef(r),i=xj(t);return qe.useEffect((()=>{o.current!==r&&(i(r),o.current=r)}),[r,o,i]),n}({defaultProp:t,onChange:n}),i=void 0!==e,s=i?e:r,a=xj(n);return[s,qe.useCallback((t=>{if(i){const n="function"==typeof t?t(e):t;n!==e&&a(n)}else o(t)}),[i,e,o,a])]}!function(e){NA.next(e.wallets)}({wallets:hj.wallets});var Ej=qe.forwardRef(((e,t)=>{const{children:n,...r}=e,o=qe.Children.toArray(n),i=o.find(_j);if(i){const e=i.props.children,n=o.map((t=>t===i?qe.Children.count(e)>1?qe.Children.only(null):qe.isValidElement(e)?e.props.children:null:t));return nt.jsx(Sj,{...r,ref:t,children:qe.isValidElement(e)?qe.cloneElement(e,void 0,n):null})}return nt.jsx(Sj,{...r,ref:t,children:n})}));Ej.displayName="Slot";var Sj=qe.forwardRef(((e,t)=>{const{children:n,...r}=e;if(qe.isValidElement(n)){const e=function(e){var t,n;let r=null==(t=Object.getOwnPropertyDescriptor(e.props,"ref"))?void 0:t.get,o=r&&"isReactWarning"in r&&r.isReactWarning;if(o)return e.ref;if(r=null==(n=Object.getOwnPropertyDescriptor(e,"ref"))?void 0:n.get,o=r&&"isReactWarning"in r&&r.isReactWarning,o)return e.props.ref;return e.props.ref||e.ref}(n);return qe.cloneElement(n,{...Aj(r,n.props),ref:t?gj(t,e):e})}return qe.Children.count(n)>1?qe.Children.only(null):null}));Sj.displayName="SlotClone";var Cj=({children:e})=>nt.jsx(nt.Fragment,{children:e});function _j(e){return qe.isValidElement(e)&&e.type===Cj}function Aj(e,t){const n={...t};for(const r in t){const o=e[r],i=t[r];/^on[A-Z]/.test(r)?o&&i?n[r]=(...e)=>{i(...e),o(...e)}:o&&(n[r]=o):"style"===r?n[r]={...o,...i}:"className"===r&&(n[r]=[o,i].filter(Boolean).join(" "))}return{...e,...n}}var Nj=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","span","svg","ul"].reduce(((e,t)=>{const n=qe.forwardRef(((e,n)=>{const{asChild:r,...o}=e,i=r?Ej:t;return"undefined"!=typeof window&&(window[Symbol.for("radix-ui")]=!0),nt.jsx(i,{...o,ref:n})}));return n.displayName=`Primitive.${t}`,{...e,[t]:n}}),{});function Pj(e,t){e&&df.flushSync((()=>e.dispatchEvent(t)))}function jj(e){const t=e+"CollectionProvider",[n,r]=bj(t),[o,i]=n(t,{collectionRef:{current:null},itemMap:new Map}),s=e=>{const{scope:t,children:n}=e,r=Ke.useRef(null),i=Ke.useRef(new Map).current;return nt.jsx(o,{scope:t,itemMap:i,collectionRef:r,children:n})};s.displayName=t;const a=e+"CollectionSlot",l=Ke.forwardRef(((e,t)=>{const{scope:n,children:r}=e,o=yj(t,i(a,n).collectionRef);return nt.jsx(Ej,{ref:o,children:r})}));l.displayName=a;const c=e+"CollectionItemSlot",u="data-radix-collection-item",d=Ke.forwardRef(((e,t)=>{const{scope:n,children:r,...o}=e,s=Ke.useRef(null),a=yj(t,s),l=i(c,n);return Ke.useEffect((()=>(l.itemMap.set(s,{ref:s,...o}),()=>{l.itemMap.delete(s)}))),nt.jsx(Ej,{[u]:"",ref:a,children:r})}));return d.displayName=c,[{Provider:s,Slot:l,ItemSlot:d},function(t){const n=i(e+"CollectionConsumer",t),r=Ke.useCallback((()=>{const e=n.collectionRef.current;if(!e)return[];const t=Array.from(e.querySelectorAll(`[${u}]`)),r=Array.from(n.itemMap.values()).sort(((e,n)=>t.indexOf(e.ref.current)-t.indexOf(n.ref.current)));return r}),[n.collectionRef,n.itemMap]);return r},r]}var $j=qe.createContext(void 0);function Oj(e){const t=qe.useContext($j);return e||t||"ltr"}var Rj,Mj="dismissableLayer.update",Tj="dismissableLayer.pointerDownOutside",Lj="dismissableLayer.focusOutside",Ij=qe.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),zj=qe.forwardRef(((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:o,onFocusOutside:i,onInteractOutside:s,onDismiss:a,...l}=e,c=qe.useContext(Ij),[u,d]=qe.useState(null),f=(null==u?void 0:u.ownerDocument)??(null==globalThis?void 0:globalThis.document),[,p]=qe.useState({}),h=yj(t,(e=>d(e))),v=Array.from(c.layers),[m]=[...c.layersWithOutsidePointerEventsDisabled].slice(-1),g=v.indexOf(m),y=u?v.indexOf(u):-1,b=c.layersWithOutsidePointerEventsDisabled.size>0,w=y>=g,x=function(e,t=(null==globalThis?void 0:globalThis.document)){const n=xj(e),r=qe.useRef(!1),o=qe.useRef((()=>{}));return qe.useEffect((()=>{const e=e=>{if(e.target&&!r.current){let r=function(){Bj(Tj,n,i,{discrete:!0})};const i={originalEvent:e};"touch"===e.pointerType?(t.removeEventListener("click",o.current),o.current=r,t.addEventListener("click",o.current,{once:!0})):r()}else t.removeEventListener("click",o.current);r.current=!1},i=window.setTimeout((()=>{t.addEventListener("pointerdown",e)}),0);return()=>{window.clearTimeout(i),t.removeEventListener("pointerdown",e),t.removeEventListener("click",o.current)}}),[t,n]),{onPointerDownCapture:()=>r.current=!0}}((e=>{const t=e.target,n=[...c.branches].some((e=>e.contains(t)));w&&!n&&(null==o||o(e),null==s||s(e),e.defaultPrevented||null==a||a())}),f),k=function(e,t=(null==globalThis?void 0:globalThis.document)){const n=xj(e),r=qe.useRef(!1);return qe.useEffect((()=>{const e=e=>{if(e.target&&!r.current){Bj(Lj,n,{originalEvent:e},{discrete:!1})}};return t.addEventListener("focusin",e),()=>t.removeEventListener("focusin",e)}),[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}((e=>{const t=e.target;[...c.branches].some((e=>e.contains(t)))||(null==i||i(e),null==s||s(e),e.defaultPrevented||null==a||a())}),f);return function(e,t=(null==globalThis?void 0:globalThis.document)){const n=xj(e);qe.useEffect((()=>{const e=e=>{"Escape"===e.key&&n(e)};return t.addEventListener("keydown",e,{capture:!0}),()=>t.removeEventListener("keydown",e,{capture:!0})}),[n,t])}((e=>{y===c.layers.size-1&&(null==r||r(e),!e.defaultPrevented&&a&&(e.preventDefault(),a()))}),f),qe.useEffect((()=>{if(u)return n&&(0===c.layersWithOutsidePointerEventsDisabled.size&&(Rj=f.body.style.pointerEvents,f.body.style.pointerEvents="none"),c.layersWithOutsidePointerEventsDisabled.add(u)),c.layers.add(u),Dj(),()=>{n&&1===c.layersWithOutsidePointerEventsDisabled.size&&(f.body.style.pointerEvents=Rj)}}),[u,f,n,c]),qe.useEffect((()=>()=>{u&&(c.layers.delete(u),c.layersWithOutsidePointerEventsDisabled.delete(u),Dj())}),[u,c]),qe.useEffect((()=>{const e=()=>p({});return document.addEventListener(Mj,e),()=>document.removeEventListener(Mj,e)}),[]),nt.jsx(Nj.div,{...l,ref:h,style:{pointerEvents:b?w?"auto":"none":void 0,...e.style},onFocusCapture:vj(e.onFocusCapture,k.onFocusCapture),onBlurCapture:vj(e.onBlurCapture,k.onBlurCapture),onPointerDownCapture:vj(e.onPointerDownCapture,x.onPointerDownCapture)})}));zj.displayName="DismissableLayer";function Dj(){const e=new CustomEvent(Mj);document.dispatchEvent(e)}function Bj(e,t,n,{discrete:r}){const o=n.originalEvent.target,i=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&o.addEventListener(e,t,{once:!0}),r?Pj(o,i):o.dispatchEvent(i)}qe.forwardRef(((e,t)=>{const n=qe.useContext(Ij),r=qe.useRef(null),o=yj(t,r);return qe.useEffect((()=>{const e=r.current;if(e)return n.branches.add(e),()=>{n.branches.delete(e)}}),[n.branches]),nt.jsx(Nj.div,{...e,ref:o})})).displayName="DismissableLayerBranch";var Uj=0;function Fj(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var Hj="focusScope.autoFocusOnMount",Vj="focusScope.autoFocusOnUnmount",Wj={bubbles:!1,cancelable:!0},qj=qe.forwardRef(((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:i,...s}=e,[a,l]=qe.useState(null),c=xj(o),u=xj(i),d=qe.useRef(null),f=yj(t,(e=>l(e))),p=qe.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;qe.useEffect((()=>{if(r){let e=function(e){if(p.paused||!a)return;const t=e.target;a.contains(t)?d.current=t:Gj(d.current,{select:!0})},t=function(e){if(p.paused||!a)return;const t=e.relatedTarget;null!==t&&(a.contains(t)||Gj(d.current,{select:!0}))},n=function(e){if(document.activeElement===document.body)for(const t of e)t.removedNodes.length>0&&Gj(a)};document.addEventListener("focusin",e),document.addEventListener("focusout",t);const r=new MutationObserver(n);return a&&r.observe(a,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",e),document.removeEventListener("focusout",t),r.disconnect()}}}),[r,a,p.paused]),qe.useEffect((()=>{if(a){Jj.add(p);const t=document.activeElement;if(!a.contains(t)){const n=new CustomEvent(Hj,Wj);a.addEventListener(Hj,c),a.dispatchEvent(n),n.defaultPrevented||(!function(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(Gj(r,{select:t}),document.activeElement!==n)return}((e=Kj(a),e.filter((e=>"A"!==e.tagName))),{select:!0}),document.activeElement===t&&Gj(a))}return()=>{a.removeEventListener(Hj,c),setTimeout((()=>{const e=new CustomEvent(Vj,Wj);a.addEventListener(Vj,u),a.dispatchEvent(e),e.defaultPrevented||Gj(t??document.body,{select:!0}),a.removeEventListener(Vj,u),Jj.remove(p)}),0)}}var e}),[a,c,u,p]);const h=qe.useCallback((e=>{if(!n&&!r)return;if(p.paused)return;const t="Tab"===e.key&&!e.altKey&&!e.ctrlKey&&!e.metaKey,o=document.activeElement;if(t&&o){const t=e.currentTarget,[r,i]=function(e){const t=Kj(e),n=Yj(t,e),r=Yj(t.reverse(),e);return[n,r]}(t);r&&i?e.shiftKey||o!==i?e.shiftKey&&o===r&&(e.preventDefault(),n&&Gj(i,{select:!0})):(e.preventDefault(),n&&Gj(r,{select:!0})):o===t&&e.preventDefault()}}),[n,r,p.paused]);return nt.jsx(Nj.div,{tabIndex:-1,...s,ref:f,onKeyDown:h})}));function Kj(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:e=>{const t="INPUT"===e.tagName&&"hidden"===e.type;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function Yj(e,t){for(const n of e)if(!Qj(n,{upTo:t}))return n}function Qj(e,{upTo:t}){if("hidden"===getComputedStyle(e).visibility)return!0;for(;e;){if(void 0!==t&&e===t)return!1;if("none"===getComputedStyle(e).display)return!0;e=e.parentElement}return!1}function Gj(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&function(e){return e instanceof HTMLInputElement&&"select"in e}(e)&&t&&e.select()}}qj.displayName="FocusScope";var Jj=function(){let e=[];return{add(t){const n=e[0];t!==n&&(null==n||n.pause()),e=Xj(e,t),e.unshift(t)},remove(t){var n;e=Xj(e,t),null==(n=e[0])||n.resume()}}}();function Xj(e,t){const n=[...e],r=n.indexOf(t);return-1!==r&&n.splice(r,1),n}var Zj=Boolean(null==globalThis?void 0:globalThis.document)?qe.useLayoutEffect:()=>{},e$=Ye["useId".toString()]||(()=>{}),t$=0;function n$(e){const[t,n]=qe.useState(e$());return Zj((()=>{n((e=>e??String(t$++)))}),[e]),t?`radix-${t}`:""}const r$=["top","right","bottom","left"],o$=Math.min,i$=Math.max,s$=Math.round,a$=Math.floor,l$=e=>({x:e,y:e}),c$={left:"right",right:"left",bottom:"top",top:"bottom"},u$={start:"end",end:"start"};function d$(e,t,n){return i$(e,o$(t,n))}function f$(e,t){return"function"==typeof e?e(t):e}function p$(e){return e.split("-")[0]}function h$(e){return e.split("-")[1]}function v$(e){return"x"===e?"y":"x"}function m$(e){return"y"===e?"height":"width"}function g$(e){return["top","bottom"].includes(p$(e))?"y":"x"}function y$(e){return v$(g$(e))}function b$(e){return e.replace(/start|end/g,(e=>u$[e]))}function w$(e){return e.replace(/left|right|bottom|top/g,(e=>c$[e]))}function x$(e){return"number"!=typeof e?function(e){return{top:0,right:0,bottom:0,left:0,...e}}(e):{top:e,right:e,bottom:e,left:e}}function k$(e){const{x:t,y:n,width:r,height:o}=e;return{width:r,height:o,top:n,left:t,right:t+r,bottom:n+o,x:t,y:n}}function E$(e,t,n){let{reference:r,floating:o}=e;const i=g$(t),s=y$(t),a=m$(s),l=p$(t),c="y"===i,u=r.x+r.width/2-o.width/2,d=r.y+r.height/2-o.height/2,f=r[a]/2-o[a]/2;let p;switch(l){case"top":p={x:u,y:r.y-o.height};break;case"bottom":p={x:u,y:r.y+r.height};break;case"right":p={x:r.x+r.width,y:d};break;case"left":p={x:r.x-o.width,y:d};break;default:p={x:r.x,y:r.y}}switch(h$(t)){case"start":p[s]-=f*(n&&c?-1:1);break;case"end":p[s]+=f*(n&&c?-1:1)}return p}async function S$(e,t){var n;void 0===t&&(t={});const{x:r,y:o,platform:i,rects:s,elements:a,strategy:l}=e,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:d="floating",altBoundary:f=!1,padding:p=0}=f$(t,e),h=x$(p),v=a[f?"floating"===d?"reference":"floating":d],m=k$(await i.getClippingRect({element:null==(n=await(null==i.isElement?void 0:i.isElement(v)))||n?v:v.contextElement||await(null==i.getDocumentElement?void 0:i.getDocumentElement(a.floating)),boundary:c,rootBoundary:u,strategy:l})),g="floating"===d?{x:r,y:o,width:s.floating.width,height:s.floating.height}:s.reference,y=await(null==i.getOffsetParent?void 0:i.getOffsetParent(a.floating)),b=await(null==i.isElement?void 0:i.isElement(y))&&await(null==i.getScale?void 0:i.getScale(y))||{x:1,y:1},w=k$(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:a,rect:g,offsetParent:y,strategy:l}):g);return{top:(m.top-w.top+h.top)/b.y,bottom:(w.bottom-m.bottom+h.bottom)/b.y,left:(m.left-w.left+h.left)/b.x,right:(w.right-m.right+h.right)/b.x}}function C$(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function _$(e){return r$.some((t=>e[t]>=0))}function A$(){return"undefined"!=typeof window}function N$(e){return $$(e)?(e.nodeName||"").toLowerCase():"#document"}function P$(e){var t;return(null==e||null==(t=e.ownerDocument)?void 0:t.defaultView)||window}function j$(e){var t;return null==(t=($$(e)?e.ownerDocument:e.document)||window.document)?void 0:t.documentElement}function $$(e){return!!A$()&&(e instanceof Node||e instanceof P$(e).Node)}function O$(e){return!!A$()&&(e instanceof Element||e instanceof P$(e).Element)}function R$(e){return!!A$()&&(e instanceof HTMLElement||e instanceof P$(e).HTMLElement)}function M$(e){return!(!A$()||"undefined"==typeof ShadowRoot)&&(e instanceof ShadowRoot||e instanceof P$(e).ShadowRoot)}function T$(e){const{overflow:t,overflowX:n,overflowY:r,display:o}=U$(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&!["inline","contents"].includes(o)}function L$(e){return["table","td","th"].includes(N$(e))}function I$(e){return[":popover-open",":modal"].some((t=>{try{return e.matches(t)}catch(n){return!1}}))}function z$(e){const t=D$(),n=O$(e)?U$(e):e;return"none"!==n.transform||"none"!==n.perspective||!!n.containerType&&"normal"!==n.containerType||!t&&!!n.backdropFilter&&"none"!==n.backdropFilter||!t&&!!n.filter&&"none"!==n.filter||["transform","perspective","filter"].some((e=>(n.willChange||"").includes(e)))||["paint","layout","strict","content"].some((e=>(n.contain||"").includes(e)))}function D$(){return!("undefined"==typeof CSS||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function B$(e){return["html","body","#document"].includes(N$(e))}function U$(e){return P$(e).getComputedStyle(e)}function F$(e){return O$(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function H$(e){if("html"===N$(e))return e;const t=e.assignedSlot||e.parentNode||M$(e)&&e.host||j$(e);return M$(t)?t.host:t}function V$(e){const t=H$(e);return B$(t)?e.ownerDocument?e.ownerDocument.body:e.body:R$(t)&&T$(t)?t:V$(t)}function W$(e,t,n){var r;void 0===t&&(t=[]),void 0===n&&(n=!0);const o=V$(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),s=P$(o);if(i){const e=q$(s);return t.concat(s,s.visualViewport||[],T$(o)?o:[],e&&n?W$(e):[])}return t.concat(o,W$(o,[],n))}function q$(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function K$(e){const t=U$(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const o=R$(e),i=o?e.offsetWidth:n,s=o?e.offsetHeight:r,a=s$(n)!==i||s$(r)!==s;return a&&(n=i,r=s),{width:n,height:r,$:a}}function Y$(e){return O$(e)?e:e.contextElement}function Q$(e){const t=Y$(e);if(!R$(t))return l$(1);const n=t.getBoundingClientRect(),{width:r,height:o,$:i}=K$(t);let s=(i?s$(n.width):n.width)/r,a=(i?s$(n.height):n.height)/o;return s&&Number.isFinite(s)||(s=1),a&&Number.isFinite(a)||(a=1),{x:s,y:a}}const G$=l$(0);function J$(e){const t=P$(e);return D$()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:G$}function X$(e,t,n,r){void 0===t&&(t=!1),void 0===n&&(n=!1);const o=e.getBoundingClientRect(),i=Y$(e);let s=l$(1);t&&(r?O$(r)&&(s=Q$(r)):s=Q$(e));const a=function(e,t,n){return void 0===t&&(t=!1),!(!n||t&&n!==P$(e))&&t}(i,n,r)?J$(i):l$(0);let l=(o.left+a.x)/s.x,c=(o.top+a.y)/s.y,u=o.width/s.x,d=o.height/s.y;if(i){const e=P$(i),t=r&&O$(r)?P$(r):r;let n=e,o=q$(n);for(;o&&r&&t!==n;){const e=Q$(o),t=o.getBoundingClientRect(),r=U$(o),i=t.left+(o.clientLeft+parseFloat(r.paddingLeft))*e.x,s=t.top+(o.clientTop+parseFloat(r.paddingTop))*e.y;l*=e.x,c*=e.y,u*=e.x,d*=e.y,l+=i,c+=s,n=P$(o),o=q$(n)}}return k$({width:u,height:d,x:l,y:c})}function Z$(e,t){const n=F$(e).scrollLeft;return t?t.left+n:X$(j$(e)).left+n}function eO(e,t,n){void 0===n&&(n=!1);const r=e.getBoundingClientRect();return{x:r.left+t.scrollLeft-(n?0:Z$(e,r)),y:r.top+t.scrollTop}}function tO(e,t,n){let r;if("viewport"===t)r=function(e,t){const n=P$(e),r=j$(e),o=n.visualViewport;let i=r.clientWidth,s=r.clientHeight,a=0,l=0;if(o){i=o.width,s=o.height;const e=D$();(!e||e&&"fixed"===t)&&(a=o.offsetLeft,l=o.offsetTop)}return{width:i,height:s,x:a,y:l}}(e,n);else if("document"===t)r=function(e){const t=j$(e),n=F$(e),r=e.ownerDocument.body,o=i$(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),i=i$(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight);let s=-n.scrollLeft+Z$(e);const a=-n.scrollTop;return"rtl"===U$(r).direction&&(s+=i$(t.clientWidth,r.clientWidth)-o),{width:o,height:i,x:s,y:a}}(j$(e));else if(O$(t))r=function(e,t){const n=X$(e,!0,"fixed"===t),r=n.top+e.clientTop,o=n.left+e.clientLeft,i=R$(e)?Q$(e):l$(1);return{width:e.clientWidth*i.x,height:e.clientHeight*i.y,x:o*i.x,y:r*i.y}}(t,n);else{const n=J$(e);r={x:t.x-n.x,y:t.y-n.y,width:t.width,height:t.height}}return k$(r)}function nO(e,t){const n=H$(e);return!(n===t||!O$(n)||B$(n))&&("fixed"===U$(n).position||nO(n,t))}function rO(e,t,n){const r=R$(t),o=j$(t),i="fixed"===n,s=X$(e,!0,i,t);let a={scrollLeft:0,scrollTop:0};const l=l$(0);if(r||!r&&!i)if(("body"!==N$(t)||T$(o))&&(a=F$(t)),r){const e=X$(t,!0,i,t);l.x=e.x+t.clientLeft,l.y=e.y+t.clientTop}else o&&(l.x=Z$(o));const c=!o||r||i?l$(0):eO(o,a);return{x:s.left+a.scrollLeft-l.x-c.x,y:s.top+a.scrollTop-l.y-c.y,width:s.width,height:s.height}}function oO(e){return"static"===U$(e).position}function iO(e,t){if(!R$(e)||"fixed"===U$(e).position)return null;if(t)return t(e);let n=e.offsetParent;return j$(e)===n&&(n=n.ownerDocument.body),n}function sO(e,t){const n=P$(e);if(I$(e))return n;if(!R$(e)){let t=H$(e);for(;t&&!B$(t);){if(O$(t)&&!oO(t))return t;t=H$(t)}return n}let r=iO(e,t);for(;r&&L$(r)&&oO(r);)r=iO(r,t);return r&&B$(r)&&oO(r)&&!z$(r)?n:r||function(e){let t=H$(e);for(;R$(t)&&!B$(t);){if(z$(t))return t;if(I$(t))return null;t=H$(t)}return null}(e)||n}const aO={convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{elements:t,rect:n,offsetParent:r,strategy:o}=e;const i="fixed"===o,s=j$(r),a=!!t&&I$(t.floating);if(r===s||a&&i)return n;let l={scrollLeft:0,scrollTop:0},c=l$(1);const u=l$(0),d=R$(r);if((d||!d&&!i)&&(("body"!==N$(r)||T$(s))&&(l=F$(r)),R$(r))){const e=X$(r);c=Q$(r),u.x=e.x+r.clientLeft,u.y=e.y+r.clientTop}const f=!s||d||i?l$(0):eO(s,l,!0);return{width:n.width*c.x,height:n.height*c.y,x:n.x*c.x-l.scrollLeft*c.x+u.x+f.x,y:n.y*c.y-l.scrollTop*c.y+u.y+f.y}},getDocumentElement:j$,getClippingRect:function(e){let{element:t,boundary:n,rootBoundary:r,strategy:o}=e;const i=[..."clippingAncestors"===n?I$(t)?[]:function(e,t){const n=t.get(e);if(n)return n;let r=W$(e,[],!1).filter((e=>O$(e)&&"body"!==N$(e))),o=null;const i="fixed"===U$(e).position;let s=i?H$(e):e;for(;O$(s)&&!B$(s);){const t=U$(s),n=z$(s);n||"fixed"!==t.position||(o=null),(i?!n&&!o:!n&&"static"===t.position&&o&&["absolute","fixed"].includes(o.position)||T$(s)&&!n&&nO(e,s))?r=r.filter((e=>e!==s)):o=t,s=H$(s)}return t.set(e,r),r}(t,this._c):[].concat(n),r],s=i[0],a=i.reduce(((e,n)=>{const r=tO(t,n,o);return e.top=i$(r.top,e.top),e.right=o$(r.right,e.right),e.bottom=o$(r.bottom,e.bottom),e.left=i$(r.left,e.left),e}),tO(t,s,o));return{width:a.right-a.left,height:a.bottom-a.top,x:a.left,y:a.top}},getOffsetParent:sO,getElementRects:async function(e){const t=this.getOffsetParent||sO,n=this.getDimensions,r=await n(e.floating);return{reference:rO(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}},getClientRects:function(e){return Array.from(e.getClientRects())},getDimensions:function(e){const{width:t,height:n}=K$(e);return{width:t,height:n}},getScale:Q$,isElement:O$,isRTL:function(e){return"rtl"===U$(e).direction}};function lO(e,t,n,r){void 0===r&&(r={});const{ancestorScroll:o=!0,ancestorResize:i=!0,elementResize:s="function"==typeof ResizeObserver,layoutShift:a="function"==typeof IntersectionObserver,animationFrame:l=!1}=r,c=Y$(e),u=o||i?[...c?W$(c):[],...W$(t)]:[];u.forEach((e=>{o&&e.addEventListener("scroll",n,{passive:!0}),i&&e.addEventListener("resize",n)}));const d=c&&a?function(e,t){let n,r=null;const o=j$(e);function i(){var e;clearTimeout(n),null==(e=r)||e.disconnect(),r=null}return function s(a,l){void 0===a&&(a=!1),void 0===l&&(l=1),i();const{left:c,top:u,width:d,height:f}=e.getBoundingClientRect();if(a||t(),!d||!f)return;const p={rootMargin:-a$(u)+"px "+-a$(o.clientWidth-(c+d))+"px "+-a$(o.clientHeight-(u+f))+"px "+-a$(c)+"px",threshold:i$(0,o$(1,l))||1};let h=!0;function v(e){const t=e[0].intersectionRatio;if(t!==l){if(!h)return s();t?s(!1,t):n=setTimeout((()=>{s(!1,1e-7)}),1e3)}h=!1}try{r=new IntersectionObserver(v,{...p,root:o.ownerDocument})}catch(m){r=new IntersectionObserver(v,p)}r.observe(e)}(!0),i}(c,n):null;let f,p=-1,h=null;s&&(h=new ResizeObserver((e=>{let[r]=e;r&&r.target===c&&h&&(h.unobserve(t),cancelAnimationFrame(p),p=requestAnimationFrame((()=>{var e;null==(e=h)||e.observe(t)}))),n()})),c&&!l&&h.observe(c),h.observe(t));let v=l?X$(e):null;return l&&function t(){const r=X$(e);!v||r.x===v.x&&r.y===v.y&&r.width===v.width&&r.height===v.height||n();v=r,f=requestAnimationFrame(t)}(),n(),()=>{var e;u.forEach((e=>{o&&e.removeEventListener("scroll",n),i&&e.removeEventListener("resize",n)})),null==d||d(),null==(e=h)||e.disconnect(),h=null,l&&cancelAnimationFrame(f)}}const cO=function(e){return void 0===e&&(e=0),{name:"offset",options:e,async fn(t){var n,r;const{x:o,y:i,placement:s,middlewareData:a}=t,l=await async function(e,t){const{placement:n,platform:r,elements:o}=e,i=await(null==r.isRTL?void 0:r.isRTL(o.floating)),s=p$(n),a=h$(n),l="y"===g$(n),c=["left","top"].includes(s)?-1:1,u=i&&l?-1:1,d=f$(t,e);let{mainAxis:f,crossAxis:p,alignmentAxis:h}="number"==typeof d?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return a&&"number"==typeof h&&(p="end"===a?-1*h:h),l?{x:p*u,y:f*c}:{x:f*c,y:p*u}}(t,e);return s===(null==(n=a.offset)?void 0:n.placement)&&null!=(r=a.arrow)&&r.alignmentOffset?{}:{x:o+l.x,y:i+l.y,data:{...l,placement:s}}}}},uO=function(e){return void 0===e&&(e={}),{name:"shift",options:e,async fn(t){const{x:n,y:r,placement:o}=t,{mainAxis:i=!0,crossAxis:s=!1,limiter:a={fn:e=>{let{x:t,y:n}=e;return{x:t,y:n}}},...l}=f$(e,t),c={x:n,y:r},u=await S$(t,l),d=g$(p$(o)),f=v$(d);let p=c[f],h=c[d];if(i){const e="y"===f?"bottom":"right";p=d$(p+u["y"===f?"top":"left"],p,p-u[e])}if(s){const e="y"===d?"bottom":"right";h=d$(h+u["y"===d?"top":"left"],h,h-u[e])}const v=a.fn({...t,[f]:p,[d]:h});return{...v,data:{x:v.x-n,y:v.y-r,enabled:{[f]:i,[d]:s}}}}}},dO=function(e){return void 0===e&&(e={}),{name:"flip",options:e,async fn(t){var n,r;const{placement:o,middlewareData:i,rects:s,initialPlacement:a,platform:l,elements:c}=t,{mainAxis:u=!0,crossAxis:d=!0,fallbackPlacements:f,fallbackStrategy:p="bestFit",fallbackAxisSideDirection:h="none",flipAlignment:v=!0,...m}=f$(e,t);if(null!=(n=i.arrow)&&n.alignmentOffset)return{};const g=p$(o),y=g$(a),b=p$(a)===a,w=await(null==l.isRTL?void 0:l.isRTL(c.floating)),x=f||(b||!v?[w$(a)]:function(e){const t=w$(e);return[b$(e),t,b$(t)]}(a)),k="none"!==h;!f&&k&&x.push(...function(e,t,n,r){const o=h$(e);let i=function(e,t,n){const r=["left","right"],o=["right","left"],i=["top","bottom"],s=["bottom","top"];switch(e){case"top":case"bottom":return n?t?o:r:t?r:o;case"left":case"right":return t?i:s;default:return[]}}(p$(e),"start"===n,r);return o&&(i=i.map((e=>e+"-"+o)),t&&(i=i.concat(i.map(b$)))),i}(a,v,h,w));const E=[a,...x],S=await S$(t,m),C=[];let _=(null==(r=i.flip)?void 0:r.overflows)||[];if(u&&C.push(S[g]),d){const e=function(e,t,n){void 0===n&&(n=!1);const r=h$(e),o=y$(e),i=m$(o);let s="x"===o?r===(n?"end":"start")?"right":"left":"start"===r?"bottom":"top";return t.reference[i]>t.floating[i]&&(s=w$(s)),[s,w$(s)]}(o,s,w);C.push(S[e[0]],S[e[1]])}if(_=[..._,{placement:o,overflows:C}],!C.every((e=>e<=0))){var A,N;const e=((null==(A=i.flip)?void 0:A.index)||0)+1,t=E[e];if(t)return{data:{index:e,overflows:_},reset:{placement:t}};let n=null==(N=_.filter((e=>e.overflows[0]<=0)).sort(((e,t)=>e.overflows[1]-t.overflows[1]))[0])?void 0:N.placement;if(!n)switch(p){case"bestFit":{var P;const e=null==(P=_.filter((e=>{if(k){const t=g$(e.placement);return t===y||"y"===t}return!0})).map((e=>[e.placement,e.overflows.filter((e=>e>0)).reduce(((e,t)=>e+t),0)])).sort(((e,t)=>e[1]-t[1]))[0])?void 0:P[0];e&&(n=e);break}case"initialPlacement":n=a}if(o!==n)return{reset:{placement:n}}}return{}}}},fO=function(e){return void 0===e&&(e={}),{name:"size",options:e,async fn(t){var n,r;const{placement:o,rects:i,platform:s,elements:a}=t,{apply:l=()=>{},...c}=f$(e,t),u=await S$(t,c),d=p$(o),f=h$(o),p="y"===g$(o),{width:h,height:v}=i.floating;let m,g;"top"===d||"bottom"===d?(m=d,g=f===(await(null==s.isRTL?void 0:s.isRTL(a.floating))?"start":"end")?"left":"right"):(g=d,m="end"===f?"top":"bottom");const y=v-u.top-u.bottom,b=h-u.left-u.right,w=o$(v-u[m],y),x=o$(h-u[g],b),k=!t.middlewareData.shift;let E=w,S=x;if(null!=(n=t.middlewareData.shift)&&n.enabled.x&&(S=b),null!=(r=t.middlewareData.shift)&&r.enabled.y&&(E=y),k&&!f){const e=i$(u.left,0),t=i$(u.right,0),n=i$(u.top,0),r=i$(u.bottom,0);p?S=h-2*(0!==e||0!==t?e+t:i$(u.left,u.right)):E=v-2*(0!==n||0!==r?n+r:i$(u.top,u.bottom))}await l({...t,availableWidth:S,availableHeight:E});const C=await s.getDimensions(a.floating);return h!==C.width||v!==C.height?{reset:{rects:!0}}:{}}}},pO=function(e){return void 0===e&&(e={}),{name:"hide",options:e,async fn(t){const{rects:n}=t,{strategy:r="referenceHidden",...o}=f$(e,t);switch(r){case"referenceHidden":{const e=C$(await S$(t,{...o,elementContext:"reference"}),n.reference);return{data:{referenceHiddenOffsets:e,referenceHidden:_$(e)}}}case"escaped":{const e=C$(await S$(t,{...o,altBoundary:!0}),n.floating);return{data:{escapedOffsets:e,escaped:_$(e)}}}default:return{}}}}},hO=e=>({name:"arrow",options:e,async fn(t){const{x:n,y:r,placement:o,rects:i,platform:s,elements:a,middlewareData:l}=t,{element:c,padding:u=0}=f$(e,t)||{};if(null==c)return{};const d=x$(u),f={x:n,y:r},p=y$(o),h=m$(p),v=await s.getDimensions(c),m="y"===p,g=m?"top":"left",y=m?"bottom":"right",b=m?"clientHeight":"clientWidth",w=i.reference[h]+i.reference[p]-f[p]-i.floating[h],x=f[p]-i.reference[p],k=await(null==s.getOffsetParent?void 0:s.getOffsetParent(c));let E=k?k[b]:0;E&&await(null==s.isElement?void 0:s.isElement(k))||(E=a.floating[b]||i.floating[h]);const S=w/2-x/2,C=E/2-v[h]/2-1,_=o$(d[g],C),A=o$(d[y],C),N=_,P=E-v[h]-A,j=E/2-v[h]/2+S,$=d$(N,j,P),O=!l.arrow&&null!=h$(o)&&j!==$&&i.reference[h]/2-(jn&&(p=n)}if(c){var g,y;const e="y"===f?"width":"height",t=["top","left"].includes(p$(o)),n=i.reference[d]-i.floating[e]+(t&&(null==(g=s.offset)?void 0:g[d])||0)+(t?0:m.crossAxis),r=i.reference[d]+i.reference[e]+(t?0:(null==(y=s.offset)?void 0:y[d])||0)-(t?m.crossAxis:0);hr&&(h=r)}return{[f]:p,[d]:h}}}},mO=(e,t,n)=>{const r=new Map,o={platform:aO,...n},i={...o.platform,_c:r};return(async(e,t,n)=>{const{placement:r="bottom",strategy:o="absolute",middleware:i=[],platform:s}=n,a=i.filter(Boolean),l=await(null==s.isRTL?void 0:s.isRTL(t));let c=await s.getElementRects({reference:e,floating:t,strategy:o}),{x:u,y:d}=E$(c,r,l),f=r,p={},h=0;for(let v=0;v{t.current=e})),t}const kO=e=>({name:"arrow",options:e,fn(t){const{element:n,padding:r}="function"==typeof e?e(t):e;return n&&(o=n,{}.hasOwnProperty.call(o,"current"))?null!=n.current?hO({element:n.current,padding:r}).fn(t):{}:n?hO({element:n,padding:r}).fn(t):{};var o}}),EO=(e,t)=>({...cO(e),options:[e,t]}),SO=(e,t)=>({...uO(e),options:[e,t]}),CO=(e,t)=>({...vO(e),options:[e,t]}),_O=(e,t)=>({...dO(e),options:[e,t]}),AO=(e,t)=>({...fO(e),options:[e,t]}),NO=(e,t)=>({...pO(e),options:[e,t]}),PO=(e,t)=>({...kO(e),options:[e,t]});var jO=qe.forwardRef(((e,t)=>{const{children:n,width:r=10,height:o=5,...i}=e;return nt.jsx(Nj.svg,{...i,ref:t,width:r,height:o,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:e.asChild?n:nt.jsx("polygon",{points:"0,0 30,0 15,10"})})}));jO.displayName="Arrow";var $O=jO;var OO="Popper",[RO,MO]=bj(OO),[TO,LO]=RO(OO),IO=e=>{const{__scopePopper:t,children:n}=e,[r,o]=qe.useState(null);return nt.jsx(TO,{scope:t,anchor:r,onAnchorChange:o,children:n})};IO.displayName=OO;var zO="PopperAnchor",DO=qe.forwardRef(((e,t)=>{const{__scopePopper:n,virtualRef:r,...o}=e,i=LO(zO,n),s=qe.useRef(null),a=yj(t,s);return qe.useEffect((()=>{i.onAnchorChange((null==r?void 0:r.current)||s.current)})),r?null:nt.jsx(Nj.div,{...o,ref:a})}));DO.displayName=zO;var BO="PopperContent",[UO,FO]=RO(BO),HO=qe.forwardRef(((e,t)=>{var n,r,o,i,s,a;const{__scopePopper:l,side:c="bottom",sideOffset:u=0,align:d="center",alignOffset:f=0,arrowPadding:p=0,avoidCollisions:h=!0,collisionBoundary:v=[],collisionPadding:m=0,sticky:g="partial",hideWhenDetached:y=!1,updatePositionStrategy:b="optimized",onPlaced:w,...x}=e,k=LO(BO,l),[E,S]=qe.useState(null),C=yj(t,(e=>S(e))),[_,A]=qe.useState(null),N=function(e){const[t,n]=qe.useState(void 0);return Zj((()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});const t=new ResizeObserver((t=>{if(!Array.isArray(t))return;if(!t.length)return;const r=t[0];let o,i;if("borderBoxSize"in r){const e=r.borderBoxSize,t=Array.isArray(e)?e[0]:e;o=t.inlineSize,i=t.blockSize}else o=e.offsetWidth,i=e.offsetHeight;n({width:o,height:i})}));return t.observe(e,{box:"border-box"}),()=>t.unobserve(e)}n(void 0)}),[e]),t}(_),P=(null==N?void 0:N.width)??0,j=(null==N?void 0:N.height)??0,$=c+("center"!==d?"-"+d:""),O="number"==typeof m?m:{top:0,right:0,bottom:0,left:0,...m},R=Array.isArray(v)?v:[v],M=R.length>0,T={padding:O,boundary:R.filter(KO),altBoundary:M},{refs:L,floatingStyles:I,placement:z,isPositioned:D,middlewareData:B}=function(e){void 0===e&&(e={});const{placement:t="bottom",strategy:n="absolute",middleware:r=[],platform:o,elements:{reference:i,floating:s}={},transform:a=!0,whileElementsMounted:l,open:c}=e,[u,d]=qe.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[f,p]=qe.useState(r);yO(f,r)||p(r);const[h,v]=qe.useState(null),[m,g]=qe.useState(null),y=qe.useCallback((e=>{e!==k.current&&(k.current=e,v(e))}),[]),b=qe.useCallback((e=>{e!==E.current&&(E.current=e,g(e))}),[]),w=i||h,x=s||m,k=qe.useRef(null),E=qe.useRef(null),S=qe.useRef(u),C=null!=l,_=xO(l),A=xO(o),N=xO(c),P=qe.useCallback((()=>{if(!k.current||!E.current)return;const e={placement:t,strategy:n,middleware:f};A.current&&(e.platform=A.current),mO(k.current,E.current,e).then((e=>{const t={...e,isPositioned:!1!==N.current};j.current&&!yO(S.current,t)&&(S.current=t,df.flushSync((()=>{d(t)})))}))}),[f,t,n,A,N]);gO((()=>{!1===c&&S.current.isPositioned&&(S.current.isPositioned=!1,d((e=>({...e,isPositioned:!1}))))}),[c]);const j=qe.useRef(!1);gO((()=>(j.current=!0,()=>{j.current=!1})),[]),gO((()=>{if(w&&(k.current=w),x&&(E.current=x),w&&x){if(_.current)return _.current(w,x,P);P()}}),[w,x,P,_,C]);const $=qe.useMemo((()=>({reference:k,floating:E,setReference:y,setFloating:b})),[y,b]),O=qe.useMemo((()=>({reference:w,floating:x})),[w,x]),R=qe.useMemo((()=>{const e={position:n,left:0,top:0};if(!O.floating)return e;const t=wO(O.floating,u.x),r=wO(O.floating,u.y);return a?{...e,transform:"translate("+t+"px, "+r+"px)",...bO(O.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:t,top:r}}),[n,a,O.floating,u.x,u.y]);return qe.useMemo((()=>({...u,update:P,refs:$,elements:O,floatingStyles:R})),[u,P,$,O,R])}({strategy:"fixed",placement:$,whileElementsMounted:(...e)=>lO(...e,{animationFrame:"always"===b}),elements:{reference:k.anchor},middleware:[EO({mainAxis:u+j,alignmentAxis:f}),h&&SO({mainAxis:!0,crossAxis:!1,limiter:"partial"===g?CO():void 0,...T}),h&&_O({...T}),AO({...T,apply:({elements:e,rects:t,availableWidth:n,availableHeight:r})=>{const{width:o,height:i}=t.reference,s=e.floating.style;s.setProperty("--radix-popper-available-width",`${n}px`),s.setProperty("--radix-popper-available-height",`${r}px`),s.setProperty("--radix-popper-anchor-width",`${o}px`),s.setProperty("--radix-popper-anchor-height",`${i}px`)}}),_&&PO({element:_,padding:p}),YO({arrowWidth:P,arrowHeight:j}),y&&NO({strategy:"referenceHidden",...T})]}),[U,F]=QO(z),H=xj(w);Zj((()=>{D&&(null==H||H())}),[D,H]);const V=null==(n=B.arrow)?void 0:n.x,W=null==(r=B.arrow)?void 0:r.y,q=0!==(null==(o=B.arrow)?void 0:o.centerOffset),[K,Y]=qe.useState();return Zj((()=>{E&&Y(window.getComputedStyle(E).zIndex)}),[E]),nt.jsx("div",{ref:L.setFloating,"data-radix-popper-content-wrapper":"",style:{...I,transform:D?I.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:K,"--radix-popper-transform-origin":[null==(i=B.transformOrigin)?void 0:i.x,null==(s=B.transformOrigin)?void 0:s.y].join(" "),...(null==(a=B.hide)?void 0:a.referenceHidden)&&{visibility:"hidden",pointerEvents:"none"}},dir:e.dir,children:nt.jsx(UO,{scope:l,placedSide:U,onArrowChange:A,arrowX:V,arrowY:W,shouldHideArrow:q,children:nt.jsx(Nj.div,{"data-side":U,"data-align":F,...x,ref:C,style:{...x.style,animation:D?void 0:"none"}})})})}));HO.displayName=BO;var VO="PopperArrow",WO={top:"bottom",right:"left",bottom:"top",left:"right"},qO=qe.forwardRef((function(e,t){const{__scopePopper:n,...r}=e,o=FO(VO,n),i=WO[o.placedSide];return nt.jsx("span",{ref:o.onArrowChange,style:{position:"absolute",left:o.arrowX,top:o.arrowY,[i]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[o.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[o.placedSide],visibility:o.shouldHideArrow?"hidden":void 0},children:nt.jsx($O,{...r,ref:t,style:{...r.style,display:"block"}})})}));function KO(e){return null!==e}qO.displayName=VO;var YO=e=>({name:"transformOrigin",options:e,fn(t){var n,r,o;const{placement:i,rects:s,middlewareData:a}=t,l=0!==(null==(n=a.arrow)?void 0:n.centerOffset),c=l?0:e.arrowWidth,u=l?0:e.arrowHeight,[d,f]=QO(i),p={start:"0%",center:"50%",end:"100%"}[f],h=((null==(r=a.arrow)?void 0:r.x)??0)+c/2,v=((null==(o=a.arrow)?void 0:o.y)??0)+u/2;let m="",g="";return"bottom"===d?(m=l?p:`${h}px`,g=-u+"px"):"top"===d?(m=l?p:`${h}px`,g=`${s.floating.height+u}px`):"right"===d?(m=-u+"px",g=l?p:`${v}px`):"left"===d&&(m=`${s.floating.width+u}px`,g=l?p:`${v}px`),{data:{x:m,y:g}}}});function QO(e){const[t,n="center"]=e.split("-");return[t,n]}var GO=IO,JO=DO,XO=HO,ZO=qO,eR=qe.forwardRef(((e,t)=>{var n;const{container:r,...o}=e,[i,s]=qe.useState(!1);Zj((()=>s(!0)),[]);const a=r||i&&(null==(n=null==globalThis?void 0:globalThis.document)?void 0:n.body);return a?ff.createPortal(nt.jsx(Nj.div,{...o,ref:t}),a):null}));eR.displayName="Portal";var tR=e=>{const{present:t,children:n}=e,r=function(e){const[t,n]=qe.useState(),r=qe.useRef({}),o=qe.useRef(e),i=qe.useRef("none"),s=e?"mounted":"unmounted",[a,l]=function(e,t){return qe.useReducer(((e,n)=>t[e][n]??e),e)}(s,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return qe.useEffect((()=>{const e=nR(r.current);i.current="mounted"===a?e:"none"}),[a]),Zj((()=>{const t=r.current,n=o.current;if(n!==e){const r=i.current,s=nR(t);if(e)l("MOUNT");else if("none"===s||"none"===(null==t?void 0:t.display))l("UNMOUNT");else{l(n&&r!==s?"ANIMATION_OUT":"UNMOUNT")}o.current=e}}),[e,l]),Zj((()=>{if(t){let e;const n=t.ownerDocument.defaultView??window,s=i=>{const s=nR(r.current).includes(i.animationName);if(i.target===t&&s&&(l("ANIMATION_END"),!o.current)){const r=t.style.animationFillMode;t.style.animationFillMode="forwards",e=n.setTimeout((()=>{"forwards"===t.style.animationFillMode&&(t.style.animationFillMode=r)}))}},a=e=>{e.target===t&&(i.current=nR(r.current))};return t.addEventListener("animationstart",a),t.addEventListener("animationcancel",s),t.addEventListener("animationend",s),()=>{n.clearTimeout(e),t.removeEventListener("animationstart",a),t.removeEventListener("animationcancel",s),t.removeEventListener("animationend",s)}}l("ANIMATION_END")}),[t,l]),{isPresent:["mounted","unmountSuspended"].includes(a),ref:qe.useCallback((e=>{e&&(r.current=getComputedStyle(e)),n(e)}),[])}}(t),o="function"==typeof n?n({present:r.isPresent}):qe.Children.only(n),i=yj(r.ref,function(e){var t,n;let r=null==(t=Object.getOwnPropertyDescriptor(e.props,"ref"))?void 0:t.get,o=r&&"isReactWarning"in r&&r.isReactWarning;if(o)return e.ref;if(r=null==(n=Object.getOwnPropertyDescriptor(e,"ref"))?void 0:n.get,o=r&&"isReactWarning"in r&&r.isReactWarning,o)return e.props.ref;return e.props.ref||e.ref}(o));return"function"==typeof n||r.isPresent?qe.cloneElement(o,{ref:i}):null};function nR(e){return(null==e?void 0:e.animationName)||"none"}tR.displayName="Presence";var rR="rovingFocusGroup.onEntryFocus",oR={bubbles:!1,cancelable:!0},iR="RovingFocusGroup",[sR,aR,lR]=jj(iR),[cR,uR]=bj(iR,[lR]),[dR,fR]=cR(iR),pR=qe.forwardRef(((e,t)=>nt.jsx(sR.Provider,{scope:e.__scopeRovingFocusGroup,children:nt.jsx(sR.Slot,{scope:e.__scopeRovingFocusGroup,children:nt.jsx(hR,{...e,ref:t})})})));pR.displayName=iR;var hR=qe.forwardRef(((e,t)=>{const{__scopeRovingFocusGroup:n,orientation:r,loop:o=!1,dir:i,currentTabStopId:s,defaultCurrentTabStopId:a,onCurrentTabStopIdChange:l,onEntryFocus:c,preventScrollOnEntryFocus:u=!1,...d}=e,f=qe.useRef(null),p=yj(t,f),h=Oj(i),[v=null,m]=kj({prop:s,defaultProp:a,onChange:l}),[g,y]=qe.useState(!1),b=xj(c),w=aR(n),x=qe.useRef(!1),[k,E]=qe.useState(0);return qe.useEffect((()=>{const e=f.current;if(e)return e.addEventListener(rR,b),()=>e.removeEventListener(rR,b)}),[b]),nt.jsx(dR,{scope:n,orientation:r,dir:h,loop:o,currentTabStopId:v,onItemFocus:qe.useCallback((e=>m(e)),[m]),onItemShiftTab:qe.useCallback((()=>y(!0)),[]),onFocusableItemAdd:qe.useCallback((()=>E((e=>e+1))),[]),onFocusableItemRemove:qe.useCallback((()=>E((e=>e-1))),[]),children:nt.jsx(Nj.div,{tabIndex:g||0===k?-1:0,"data-orientation":r,...d,ref:p,style:{outline:"none",...e.style},onMouseDown:vj(e.onMouseDown,(()=>{x.current=!0})),onFocus:vj(e.onFocus,(e=>{const t=!x.current;if(e.target===e.currentTarget&&t&&!g){const t=new CustomEvent(rR,oR);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){const e=w().filter((e=>e.focusable));yR([e.find((e=>e.active)),e.find((e=>e.id===v)),...e].filter(Boolean).map((e=>e.ref.current)),u)}}x.current=!1})),onBlur:vj(e.onBlur,(()=>y(!1)))})})})),vR="RovingFocusGroupItem",mR=qe.forwardRef(((e,t)=>{const{__scopeRovingFocusGroup:n,focusable:r=!0,active:o=!1,tabStopId:i,...s}=e,a=n$(),l=i||a,c=fR(vR,n),u=c.currentTabStopId===l,d=aR(n),{onFocusableItemAdd:f,onFocusableItemRemove:p}=c;return qe.useEffect((()=>{if(r)return f(),()=>p()}),[r,f,p]),nt.jsx(sR.ItemSlot,{scope:n,id:l,focusable:r,active:o,children:nt.jsx(Nj.span,{tabIndex:u?0:-1,"data-orientation":c.orientation,...s,ref:t,onMouseDown:vj(e.onMouseDown,(e=>{r?c.onItemFocus(l):e.preventDefault()})),onFocus:vj(e.onFocus,(()=>c.onItemFocus(l))),onKeyDown:vj(e.onKeyDown,(e=>{if("Tab"===e.key&&e.shiftKey)return void c.onItemShiftTab();if(e.target!==e.currentTarget)return;const t=function(e,t,n){const r=function(e,t){return"rtl"!==t?e:"ArrowLeft"===e?"ArrowRight":"ArrowRight"===e?"ArrowLeft":e}(e.key,n);return"vertical"===t&&["ArrowLeft","ArrowRight"].includes(r)||"horizontal"===t&&["ArrowUp","ArrowDown"].includes(r)?void 0:gR[r]}(e,c.orientation,c.dir);if(void 0!==t){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let o=d().filter((e=>e.focusable)).map((e=>e.ref.current));if("last"===t)o.reverse();else if("prev"===t||"next"===t){"prev"===t&&o.reverse();const i=o.indexOf(e.currentTarget);o=c.loop?(r=i+1,(n=o).map(((e,t)=>n[(r+t)%n.length]))):o.slice(i+1)}setTimeout((()=>yR(o)))}var n,r}))})})}));mR.displayName=vR;var gR={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function yR(e,t=!1){const n=document.activeElement;for(const r of e){if(r===n)return;if(r.focus({preventScroll:t}),document.activeElement!==n)return}}var bR=pR,wR=mR,xR=new WeakMap,kR=new WeakMap,ER={},SR=0,CR=function(e){return e&&(e.host||CR(e.parentNode))},_R=function(e,t,n,r){var o=function(e,t){return t.map((function(t){if(e.contains(t))return t;var n=CR(t);return n&&e.contains(n)?n:(console.error("aria-hidden",t,"in not contained inside",e,". Doing nothing"),null)})).filter((function(e){return Boolean(e)}))}(t,Array.isArray(e)?e:[e]);ER[n]||(ER[n]=new WeakMap);var i=ER[n],s=[],a=new Set,l=new Set(o),c=function(e){e&&!a.has(e)&&(a.add(e),c(e.parentNode))};o.forEach(c);var u=function(e){e&&!l.has(e)&&Array.prototype.forEach.call(e.children,(function(e){if(a.has(e))u(e);else try{var t=e.getAttribute(r),o=null!==t&&"false"!==t,l=(xR.get(e)||0)+1,c=(i.get(e)||0)+1;xR.set(e,l),i.set(e,c),s.push(e),1===l&&o&&kR.set(e,!0),1===c&&e.setAttribute(n,"true"),o||e.setAttribute(r,"true")}catch(d){console.error("aria-hidden: cannot operate on ",e,d)}}))};return u(t),a.clear(),SR++,function(){s.forEach((function(e){var t=xR.get(e)-1,o=i.get(e)-1;xR.set(e,t),i.set(e,o),t||(kR.has(e)||e.removeAttribute(r),kR.delete(e)),o||e.removeAttribute(n)})),--SR||(xR=new WeakMap,xR=new WeakMap,kR=new WeakMap,ER={})}},AR=function(e,t,n){void 0===n&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=function(e){return"undefined"==typeof document?null:(Array.isArray(e)?e[0]:e).ownerDocument.body}(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live]"))),_R(r,o,n,"aria-hidden")):function(){return null}},NR="right-scroll-bar-position",PR="width-before-scroll-bar";function jR(e,t){return"function"==typeof e?e(t):e&&(e.current=t),e}var $R="undefined"!=typeof window?qe.useLayoutEffect:qe.useEffect,OR=new WeakMap;function RR(e,t){var n,r,o,i=(n=null,r=function(t){return e.forEach((function(e){return jR(e,t)}))},(o=qe.useState((function(){return{value:n,callback:r,facade:{get current(){return o.value},set current(e){var t=o.value;t!==e&&(o.value=e,o.callback(e,t))}}}}))[0]).callback=r,o.facade);return $R((function(){var t=OR.get(i);if(t){var n=new Set(t),r=new Set(e),o=i.current;n.forEach((function(e){r.has(e)||jR(e,null)})),r.forEach((function(e){n.has(e)||jR(e,o)}))}OR.set(i,e)}),[e]),i}function MR(e){return e}var TR=function(e){var t=e.sideCar,n=Yy(e,["sideCar"]);if(!t)throw new Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw new Error("Sidecar medium not found");return qe.createElement(r,Ky({},n))};TR.isSideCarExport=!0;var LR=function(e){void 0===e&&(e={});var t=function(e,t){void 0===t&&(t=MR);var n=[],r=!1;return{read:function(){if(r)throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return n.length?n[n.length-1]:e},useMedium:function(e){var o=t(e,r);return n.push(o),function(){n=n.filter((function(e){return e!==o}))}},assignSyncMedium:function(e){for(r=!0;n.length;){var t=n;n=[],t.forEach(e)}n={push:function(t){return e(t)},filter:function(){return n}}},assignMedium:function(e){r=!0;var t=[];if(n.length){var o=n;n=[],o.forEach(e),t=n}var i=function(){var n=t;t=[],n.forEach(e)},s=function(){return Promise.resolve().then(i)};s(),n={push:function(e){t.push(e),s()},filter:function(e){return t=t.filter(e),n}}}}}(null);return t.options=Ky({async:!0,ssr:!1},e),t}(),IR=function(){},zR=qe.forwardRef((function(e,t){var n=qe.useRef(null),r=qe.useState({onScrollCapture:IR,onWheelCapture:IR,onTouchMoveCapture:IR}),o=r[0],i=r[1],s=e.forwardProps,a=e.children,l=e.className,c=e.removeScrollBar,u=e.enabled,d=e.shards,f=e.sideCar,p=e.noIsolation,h=e.inert,v=e.allowPinchZoom,m=e.as,g=void 0===m?"div":m,y=e.gapMode,b=Yy(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noIsolation","inert","allowPinchZoom","as","gapMode"]),w=f,x=RR([n,t]),k=Ky(Ky({},b),o);return qe.createElement(qe.Fragment,null,u&&qe.createElement(w,{sideCar:LR,removeScrollBar:c,shards:d,noIsolation:p,inert:h,setCallbacks:i,allowPinchZoom:!!v,lockRef:n,gapMode:y}),s?qe.cloneElement(qe.Children.only(a),Ky(Ky({},k),{ref:x})):qe.createElement(g,Ky({},k,{className:l,ref:x}),a))}));zR.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},zR.classNames={fullWidth:PR,zeroRight:NR};function DR(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=function(){if("undefined"!=typeof __webpack_nonce__)return __webpack_nonce__}();return t&&e.setAttribute("nonce",t),e}var BR=function(){var e=0,t=null;return{add:function(n){var r,o;0==e&&(t=DR())&&(o=n,(r=t).styleSheet?r.styleSheet.cssText=o:r.appendChild(document.createTextNode(o)),function(e){(document.head||document.getElementsByTagName("head")[0]).appendChild(e)}(t)),e++},remove:function(){! --e&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},UR=function(){var e,t=(e=BR(),function(t,n){qe.useEffect((function(){return e.add(t),function(){e.remove()}}),[t&&n])});return function(e){var n=e.styles,r=e.dynamic;return t(n,r),null}},FR={left:0,top:0,right:0,gap:0},HR=function(e){return parseInt(e||"",10)||0},VR=function(e){if(void 0===e&&(e="margin"),"undefined"==typeof window)return FR;var t=function(e){var t=window.getComputedStyle(document.body),n=t["padding"===e?"paddingLeft":"marginLeft"],r=t["padding"===e?"paddingTop":"marginTop"],o=t["padding"===e?"paddingRight":"marginRight"];return[HR(n),HR(r),HR(o)]}(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},WR=UR(),qR="data-scroll-locked",KR=function(e,t,n,r){var o=e.left,i=e.top,s=e.right,a=e.gap;return void 0===n&&(n="margin"),"\n .".concat("with-scroll-bars-hidden"," {\n overflow: hidden ").concat(r,";\n padding-right: ").concat(a,"px ").concat(r,";\n }\n body[").concat(qR,"] {\n overflow: hidden ").concat(r,";\n overscroll-behavior: contain;\n ").concat([t&&"position: relative ".concat(r,";"),"margin"===n&&"\n padding-left: ".concat(o,"px;\n padding-top: ").concat(i,"px;\n padding-right: ").concat(s,"px;\n margin-left:0;\n margin-top:0;\n margin-right: ").concat(a,"px ").concat(r,";\n "),"padding"===n&&"padding-right: ".concat(a,"px ").concat(r,";")].filter(Boolean).join(""),"\n }\n \n .").concat(NR," {\n right: ").concat(a,"px ").concat(r,";\n }\n \n .").concat(PR," {\n margin-right: ").concat(a,"px ").concat(r,";\n }\n \n .").concat(NR," .").concat(NR," {\n right: 0 ").concat(r,";\n }\n \n .").concat(PR," .").concat(PR," {\n margin-right: 0 ").concat(r,";\n }\n \n body[").concat(qR,"] {\n ").concat("--removed-body-scroll-bar-size",": ").concat(a,"px;\n }\n")},YR=function(){var e=parseInt(document.body.getAttribute(qR)||"0",10);return isFinite(e)?e:0},QR=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,o=void 0===r?"margin":r;qe.useEffect((function(){return document.body.setAttribute(qR,(YR()+1).toString()),function(){var e=YR()-1;e<=0?document.body.removeAttribute(qR):document.body.setAttribute(qR,e.toString())}}),[]);var i=qe.useMemo((function(){return VR(o)}),[o]);return qe.createElement(WR,{styles:KR(i,!t,o,n?"":"!important")})},GR=!1;if("undefined"!=typeof window)try{var JR=Object.defineProperty({},"passive",{get:function(){return GR=!0,!0}});window.addEventListener("test",JR,JR),window.removeEventListener("test",JR,JR)}catch(cI){GR=!1}var XR=!!GR&&{passive:!1},ZR=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return"hidden"!==n[t]&&!(n.overflowY===n.overflowX&&!function(e){return"TEXTAREA"===e.tagName}(e)&&"visible"===n[t])},eM=function(e,t){var n=t.ownerDocument,r=t;do{if("undefined"!=typeof ShadowRoot&&r instanceof ShadowRoot&&(r=r.host),tM(e,r)){var o=nM(e,r);if(o[1]>o[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},tM=function(e,t){return"v"===e?function(e){return ZR(e,"overflowY")}(t):function(e){return ZR(e,"overflowX")}(t)},nM=function(e,t){return"v"===e?[(n=t).scrollTop,n.scrollHeight,n.clientHeight]:function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]}(t);var n},rM=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},oM=function(e){return[e.deltaX,e.deltaY]},iM=function(e){return e&&"current"in e?e.current:e},sM=function(e){return"\n .block-interactivity-".concat(e," {pointer-events: none;}\n .allow-interactivity-").concat(e," {pointer-events: all;}\n")},aM=0,lM=[];function cM(e){for(var t=null;null!==e;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const uM=(dM=function(e){var t=qe.useRef([]),n=qe.useRef([0,0]),r=qe.useRef(),o=qe.useState(aM++)[0],i=qe.useState(UR)[0],s=qe.useRef(e);qe.useEffect((function(){s.current=e}),[e]),qe.useEffect((function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var t=Zy([e.lockRef.current],(e.shards||[]).map(iM),!0).filter(Boolean);return t.forEach((function(e){return e.classList.add("allow-interactivity-".concat(o))})),function(){document.body.classList.remove("block-interactivity-".concat(o)),t.forEach((function(e){return e.classList.remove("allow-interactivity-".concat(o))}))}}}),[e.inert,e.lockRef.current,e.shards]);var a=qe.useCallback((function(e,t){if("touches"in e&&2===e.touches.length||"wheel"===e.type&&e.ctrlKey)return!s.current.allowPinchZoom;var o,i=rM(e),a=n.current,l="deltaX"in e?e.deltaX:a[0]-i[0],c="deltaY"in e?e.deltaY:a[1]-i[1],u=e.target,d=Math.abs(l)>Math.abs(c)?"h":"v";if("touches"in e&&"h"===d&&"range"===u.type)return!1;var f=eM(d,u);if(!f)return!0;if(f?o=d:(o="v"===d?"h":"v",f=eM(d,u)),!f)return!1;if(!r.current&&"changedTouches"in e&&(l||c)&&(r.current=o),!o)return!0;var p=r.current||o;return function(e,t,n,r,o){var i=function(e,t){return"h"===e&&"rtl"===t?-1:1}(e,window.getComputedStyle(t).direction),s=i*r,a=n.target,l=t.contains(a),c=!1,u=s>0,d=0,f=0;do{var p=nM(e,a),h=p[0],v=p[1]-p[2]-i*h;(h||v)&&tM(e,a)&&(d+=v,f+=h),a=a instanceof ShadowRoot?a.host:a.parentNode}while(!l&&a!==document.body||l&&(t.contains(a)||t===a));return u&&(Math.abs(d)<1||!o)?c=!0:u||!(Math.abs(f)<1)&&o||(c=!0),c}(p,t,e,"h"===p?l:c,!0)}),[]),l=qe.useCallback((function(e){var n=e;if(lM.length&&lM[lM.length-1]===i){var r="deltaY"in n?oM(n):rM(n),o=t.current.filter((function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&(t=e.delta,o=r,t[0]===o[0]&&t[1]===o[1]);var t,o}))[0];if(o&&o.should)n.cancelable&&n.preventDefault();else if(!o){var l=(s.current.shards||[]).map(iM).filter(Boolean).filter((function(e){return e.contains(n.target)}));(l.length>0?a(n,l[0]):!s.current.noIsolation)&&n.cancelable&&n.preventDefault()}}}),[]),c=qe.useCallback((function(e,n,r,o){var i={name:e,delta:n,target:r,should:o,shadowParent:cM(r)};t.current.push(i),setTimeout((function(){t.current=t.current.filter((function(e){return e!==i}))}),1)}),[]),u=qe.useCallback((function(e){n.current=rM(e),r.current=void 0}),[]),d=qe.useCallback((function(t){c(t.type,oM(t),t.target,a(t,e.lockRef.current))}),[]),f=qe.useCallback((function(t){c(t.type,rM(t),t.target,a(t,e.lockRef.current))}),[]);qe.useEffect((function(){return lM.push(i),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener("wheel",l,XR),document.addEventListener("touchmove",l,XR),document.addEventListener("touchstart",u,XR),function(){lM=lM.filter((function(e){return e!==i})),document.removeEventListener("wheel",l,XR),document.removeEventListener("touchmove",l,XR),document.removeEventListener("touchstart",u,XR)}}),[]);var p=e.removeScrollBar,h=e.inert;return qe.createElement(qe.Fragment,null,h?qe.createElement(i,{styles:sM(o)}):null,p?qe.createElement(QR,{gapMode:e.gapMode}):null)},LR.useMedium(dM),TR);var dM,fM=qe.forwardRef((function(e,t){return qe.createElement(zR,Ky({},e,{ref:t,sideCar:uM}))}));fM.classNames=zR.classNames;var pM=["Enter"," "],hM=["ArrowUp","PageDown","End"],vM=["ArrowDown","PageUp","Home",...hM],mM={ltr:[...pM,"ArrowRight"],rtl:[...pM,"ArrowLeft"]},gM={ltr:["ArrowLeft"],rtl:["ArrowRight"]},yM="Menu",[bM,wM,xM]=jj(yM),[kM,EM]=bj(yM,[xM,MO,uR]),SM=MO(),CM=uR(),[_M,AM]=kM(yM),[NM,PM]=kM(yM),jM=e=>{const{__scopeMenu:t,open:n=!1,children:r,dir:o,onOpenChange:i,modal:s=!0}=e,a=SM(t),[l,c]=qe.useState(null),u=qe.useRef(!1),d=xj(i),f=Oj(o);return qe.useEffect((()=>{const e=()=>{u.current=!0,document.addEventListener("pointerdown",t,{capture:!0,once:!0}),document.addEventListener("pointermove",t,{capture:!0,once:!0})},t=()=>u.current=!1;return document.addEventListener("keydown",e,{capture:!0}),()=>{document.removeEventListener("keydown",e,{capture:!0}),document.removeEventListener("pointerdown",t,{capture:!0}),document.removeEventListener("pointermove",t,{capture:!0})}}),[]),nt.jsx(GO,{...a,children:nt.jsx(_M,{scope:t,open:n,onOpenChange:d,content:l,onContentChange:c,children:nt.jsx(NM,{scope:t,onClose:qe.useCallback((()=>d(!1)),[d]),isUsingKeyboardRef:u,dir:f,modal:s,children:r})})})};jM.displayName=yM;var $M=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e,o=SM(n);return nt.jsx(JO,{...o,...r,ref:t})}));$M.displayName="MenuAnchor";var OM="MenuPortal",[RM,MM]=kM(OM,{forceMount:void 0}),TM=e=>{const{__scopeMenu:t,forceMount:n,children:r,container:o}=e,i=AM(OM,t);return nt.jsx(RM,{scope:t,forceMount:n,children:nt.jsx(tR,{present:n||i.open,children:nt.jsx(eR,{asChild:!0,container:o,children:r})})})};TM.displayName=OM;var LM="MenuContent",[IM,zM]=kM(LM),DM=qe.forwardRef(((e,t)=>{const n=MM(LM,e.__scopeMenu),{forceMount:r=n.forceMount,...o}=e,i=AM(LM,e.__scopeMenu),s=PM(LM,e.__scopeMenu);return nt.jsx(bM.Provider,{scope:e.__scopeMenu,children:nt.jsx(tR,{present:r||i.open,children:nt.jsx(bM.Slot,{scope:e.__scopeMenu,children:s.modal?nt.jsx(BM,{...o,ref:t}):nt.jsx(UM,{...o,ref:t})})})})})),BM=qe.forwardRef(((e,t)=>{const n=AM(LM,e.__scopeMenu),r=qe.useRef(null),o=yj(t,r);return qe.useEffect((()=>{const e=r.current;if(e)return AR(e)}),[]),nt.jsx(FM,{...e,ref:o,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:vj(e.onFocusOutside,(e=>e.preventDefault()),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})})),UM=qe.forwardRef(((e,t)=>{const n=AM(LM,e.__scopeMenu);return nt.jsx(FM,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})})),FM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,loop:r=!1,trapFocus:o,onOpenAutoFocus:i,onCloseAutoFocus:s,disableOutsidePointerEvents:a,onEntryFocus:l,onEscapeKeyDown:c,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,disableOutsideScroll:h,...v}=e,m=AM(LM,n),g=PM(LM,n),y=SM(n),b=CM(n),w=wM(n),[x,k]=qe.useState(null),E=qe.useRef(null),S=yj(t,E,m.onContentChange),C=qe.useRef(0),_=qe.useRef(""),A=qe.useRef(0),N=qe.useRef(null),P=qe.useRef("right"),j=qe.useRef(0),$=h?fM:qe.Fragment,O=h?{as:Ej,allowPinchZoom:!0}:void 0,R=e=>{var t,n;const r=_.current+e,o=w().filter((e=>!e.disabled)),i=document.activeElement,s=null==(t=o.find((e=>e.ref.current===i)))?void 0:t.textValue,a=function(e,t,n){const r=t.length>1&&Array.from(t).every((e=>e===t[0])),o=r?t[0]:t,i=n?e.indexOf(n):-1;let s=(a=e,l=Math.max(i,0),a.map(((e,t)=>a[(l+t)%a.length])));var a,l;1===o.length&&(s=s.filter((e=>e!==n)));const c=s.find((e=>e.toLowerCase().startsWith(o.toLowerCase())));return c!==n?c:void 0}(o.map((e=>e.textValue)),r,s),l=null==(n=o.find((e=>e.textValue===a)))?void 0:n.ref.current;!function e(t){_.current=t,window.clearTimeout(C.current),""!==t&&(C.current=window.setTimeout((()=>e("")),1e3))}(r),l&&setTimeout((()=>l.focus()))};qe.useEffect((()=>()=>window.clearTimeout(C.current)),[]),qe.useEffect((()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??Fj()),document.body.insertAdjacentElement("beforeend",e[1]??Fj()),Uj++,()=>{1===Uj&&document.querySelectorAll("[data-radix-focus-guard]").forEach((e=>e.remove())),Uj--}}),[]);const M=qe.useCallback((e=>{var t,n;return P.current===(null==(t=N.current)?void 0:t.side)&&function(e,t){if(!t)return!1;const n={x:e.clientX,y:e.clientY};return function(e,t){const{x:n,y:r}=e;let o=!1;for(let i=0,s=t.length-1;ir!=c>r&&n<(l-e)*(r-a)/(c-a)+e&&(o=!o)}return o}(n,t)}(e,null==(n=N.current)?void 0:n.area)}),[]);return nt.jsx(IM,{scope:n,searchRef:_,onItemEnter:qe.useCallback((e=>{M(e)&&e.preventDefault()}),[M]),onItemLeave:qe.useCallback((e=>{var t;M(e)||(null==(t=E.current)||t.focus(),k(null))}),[M]),onTriggerLeave:qe.useCallback((e=>{M(e)&&e.preventDefault()}),[M]),pointerGraceTimerRef:A,onPointerGraceIntentChange:qe.useCallback((e=>{N.current=e}),[]),children:nt.jsx($,{...O,children:nt.jsx(qj,{asChild:!0,trapped:o,onMountAutoFocus:vj(i,(e=>{var t;e.preventDefault(),null==(t=E.current)||t.focus({preventScroll:!0})})),onUnmountAutoFocus:s,children:nt.jsx(zj,{asChild:!0,disableOutsidePointerEvents:a,onEscapeKeyDown:c,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,children:nt.jsx(bR,{asChild:!0,...b,dir:g.dir,orientation:"vertical",loop:r,currentTabStopId:x,onCurrentTabStopIdChange:k,onEntryFocus:vj(l,(e=>{g.isUsingKeyboardRef.current||e.preventDefault()})),preventScrollOnEntryFocus:!0,children:nt.jsx(XO,{role:"menu","aria-orientation":"vertical","data-state":hT(m.open),"data-radix-menu-content":"",dir:g.dir,...y,...v,ref:S,style:{outline:"none",...v.style},onKeyDown:vj(v.onKeyDown,(e=>{const t=e.target.closest("[data-radix-menu-content]")===e.currentTarget,n=e.ctrlKey||e.altKey||e.metaKey,r=1===e.key.length;t&&("Tab"===e.key&&e.preventDefault(),!n&&r&&R(e.key));const o=E.current;if(e.target!==o)return;if(!vM.includes(e.key))return;e.preventDefault();const i=w().filter((e=>!e.disabled)).map((e=>e.ref.current));hM.includes(e.key)&&i.reverse(),function(e){const t=document.activeElement;for(const n of e){if(n===t)return;if(n.focus(),document.activeElement!==t)return}}(i)})),onBlur:vj(e.onBlur,(e=>{e.currentTarget.contains(e.target)||(window.clearTimeout(C.current),_.current="")})),onPointerMove:vj(e.onPointerMove,gT((e=>{const t=e.target,n=j.current!==e.clientX;if(e.currentTarget.contains(t)&&n){const t=e.clientX>j.current?"right":"left";P.current=t,j.current=e.clientX}})))})})})})})})}));DM.displayName=LM;var HM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(Nj.div,{role:"group",...r,ref:t})}));HM.displayName="MenuGroup";var VM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(Nj.div,{...r,ref:t})}));VM.displayName="MenuLabel";var WM="MenuItem",qM="menu.itemSelect",KM=qe.forwardRef(((e,t)=>{const{disabled:n=!1,onSelect:r,...o}=e,i=qe.useRef(null),s=PM(WM,e.__scopeMenu),a=zM(WM,e.__scopeMenu),l=yj(t,i),c=qe.useRef(!1);return nt.jsx(YM,{...o,ref:l,disabled:n,onClick:vj(e.onClick,(()=>{const e=i.current;if(!n&&e){const t=new CustomEvent(qM,{bubbles:!0,cancelable:!0});e.addEventListener(qM,(e=>null==r?void 0:r(e)),{once:!0}),Pj(e,t),t.defaultPrevented?c.current=!1:s.onClose()}})),onPointerDown:t=>{var n;null==(n=e.onPointerDown)||n.call(e,t),c.current=!0},onPointerUp:vj(e.onPointerUp,(e=>{var t;c.current||null==(t=e.currentTarget)||t.click()})),onKeyDown:vj(e.onKeyDown,(e=>{const t=""!==a.searchRef.current;n||t&&" "===e.key||pM.includes(e.key)&&(e.currentTarget.click(),e.preventDefault())}))})}));KM.displayName=WM;var YM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,disabled:r=!1,textValue:o,...i}=e,s=zM(WM,n),a=CM(n),l=qe.useRef(null),c=yj(t,l),[u,d]=qe.useState(!1),[f,p]=qe.useState("");return qe.useEffect((()=>{const e=l.current;e&&p((e.textContent??"").trim())}),[i.children]),nt.jsx(bM.ItemSlot,{scope:n,disabled:r,textValue:o??f,children:nt.jsx(wR,{asChild:!0,...a,focusable:!r,children:nt.jsx(Nj.div,{role:"menuitem","data-highlighted":u?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0,...i,ref:c,onPointerMove:vj(e.onPointerMove,gT((e=>{if(r)s.onItemLeave(e);else if(s.onItemEnter(e),!e.defaultPrevented){e.currentTarget.focus({preventScroll:!0})}}))),onPointerLeave:vj(e.onPointerLeave,gT((e=>s.onItemLeave(e)))),onFocus:vj(e.onFocus,(()=>d(!0))),onBlur:vj(e.onBlur,(()=>d(!1)))})})})})),QM=qe.forwardRef(((e,t)=>{const{checked:n=!1,onCheckedChange:r,...o}=e;return nt.jsx(rT,{scope:e.__scopeMenu,checked:n,children:nt.jsx(KM,{role:"menuitemcheckbox","aria-checked":vT(n)?"mixed":n,...o,ref:t,"data-state":mT(n),onSelect:vj(o.onSelect,(()=>null==r?void 0:r(!!vT(n)||!n)),{checkForDefaultPrevented:!1})})})}));QM.displayName="MenuCheckboxItem";var GM="MenuRadioGroup",[JM,XM]=kM(GM,{value:void 0,onValueChange:()=>{}}),ZM=qe.forwardRef(((e,t)=>{const{value:n,onValueChange:r,...o}=e,i=xj(r);return nt.jsx(JM,{scope:e.__scopeMenu,value:n,onValueChange:i,children:nt.jsx(HM,{...o,ref:t})})}));ZM.displayName=GM;var eT="MenuRadioItem",tT=qe.forwardRef(((e,t)=>{const{value:n,...r}=e,o=XM(eT,e.__scopeMenu),i=n===o.value;return nt.jsx(rT,{scope:e.__scopeMenu,checked:i,children:nt.jsx(KM,{role:"menuitemradio","aria-checked":i,...r,ref:t,"data-state":mT(i),onSelect:vj(r.onSelect,(()=>{var e;return null==(e=o.onValueChange)?void 0:e.call(o,n)}),{checkForDefaultPrevented:!1})})})}));tT.displayName=eT;var nT="MenuItemIndicator",[rT,oT]=kM(nT,{checked:!1}),iT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,forceMount:r,...o}=e,i=oT(nT,n);return nt.jsx(tR,{present:r||vT(i.checked)||!0===i.checked,children:nt.jsx(Nj.span,{...o,ref:t,"data-state":mT(i.checked)})})}));iT.displayName=nT;var sT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(Nj.div,{role:"separator","aria-orientation":"horizontal",...r,ref:t})}));sT.displayName="MenuSeparator";var aT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e,o=SM(n);return nt.jsx(ZO,{...o,...r,ref:t})}));aT.displayName="MenuArrow";var[lT,cT]=kM("MenuSub"),uT="MenuSubTrigger",dT=qe.forwardRef(((e,t)=>{const n=AM(uT,e.__scopeMenu),r=PM(uT,e.__scopeMenu),o=cT(uT,e.__scopeMenu),i=zM(uT,e.__scopeMenu),s=qe.useRef(null),{pointerGraceTimerRef:a,onPointerGraceIntentChange:l}=i,c={__scopeMenu:e.__scopeMenu},u=qe.useCallback((()=>{s.current&&window.clearTimeout(s.current),s.current=null}),[]);return qe.useEffect((()=>u),[u]),qe.useEffect((()=>{const e=a.current;return()=>{window.clearTimeout(e),l(null)}}),[a,l]),nt.jsx($M,{asChild:!0,...c,children:nt.jsx(YM,{id:o.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":o.contentId,"data-state":hT(n.open),...e,ref:gj(t,o.onTriggerChange),onClick:t=>{var r;null==(r=e.onClick)||r.call(e,t),e.disabled||t.defaultPrevented||(t.currentTarget.focus(),n.open||n.onOpenChange(!0))},onPointerMove:vj(e.onPointerMove,gT((t=>{i.onItemEnter(t),t.defaultPrevented||e.disabled||n.open||s.current||(i.onPointerGraceIntentChange(null),s.current=window.setTimeout((()=>{n.onOpenChange(!0),u()}),100))}))),onPointerLeave:vj(e.onPointerLeave,gT((e=>{var t,r;u();const o=null==(t=n.content)?void 0:t.getBoundingClientRect();if(o){const t=null==(r=n.content)?void 0:r.dataset.side,s="right"===t,l=s?-5:5,c=o[s?"left":"right"],u=o[s?"right":"left"];i.onPointerGraceIntentChange({area:[{x:e.clientX+l,y:e.clientY},{x:c,y:o.top},{x:u,y:o.top},{x:u,y:o.bottom},{x:c,y:o.bottom}],side:t}),window.clearTimeout(a.current),a.current=window.setTimeout((()=>i.onPointerGraceIntentChange(null)),300)}else{if(i.onTriggerLeave(e),e.defaultPrevented)return;i.onPointerGraceIntentChange(null)}}))),onKeyDown:vj(e.onKeyDown,(t=>{var o;const s=""!==i.searchRef.current;e.disabled||s&&" "===t.key||mM[r.dir].includes(t.key)&&(n.onOpenChange(!0),null==(o=n.content)||o.focus(),t.preventDefault())}))})})}));dT.displayName=uT;var fT="MenuSubContent",pT=qe.forwardRef(((e,t)=>{const n=MM(LM,e.__scopeMenu),{forceMount:r=n.forceMount,...o}=e,i=AM(LM,e.__scopeMenu),s=PM(LM,e.__scopeMenu),a=cT(fT,e.__scopeMenu),l=qe.useRef(null),c=yj(t,l);return nt.jsx(bM.Provider,{scope:e.__scopeMenu,children:nt.jsx(tR,{present:r||i.open,children:nt.jsx(bM.Slot,{scope:e.__scopeMenu,children:nt.jsx(FM,{id:a.contentId,"aria-labelledby":a.triggerId,...o,ref:c,align:"start",side:"rtl"===s.dir?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:e=>{var t;s.isUsingKeyboardRef.current&&(null==(t=l.current)||t.focus()),e.preventDefault()},onCloseAutoFocus:e=>e.preventDefault(),onFocusOutside:vj(e.onFocusOutside,(e=>{e.target!==a.trigger&&i.onOpenChange(!1)})),onEscapeKeyDown:vj(e.onEscapeKeyDown,(e=>{s.onClose(),e.preventDefault()})),onKeyDown:vj(e.onKeyDown,(e=>{var t;const n=e.currentTarget.contains(e.target),r=gM[s.dir].includes(e.key);n&&r&&(i.onOpenChange(!1),null==(t=a.trigger)||t.focus(),e.preventDefault())}))})})})})}));function hT(e){return e?"open":"closed"}function vT(e){return"indeterminate"===e}function mT(e){return vT(e)?"indeterminate":e?"checked":"unchecked"}function gT(e){return t=>"mouse"===t.pointerType?e(t):void 0}pT.displayName=fT;var yT=jM,bT=$M,wT=TM,xT=DM,kT=HM,ET=VM,ST=KM,CT=QM,_T=ZM,AT=tT,NT=iT,PT=sT,jT=aT,$T=dT,OT=pT,RT="DropdownMenu",[MT,TT]=bj(RT,[EM]),LT=EM(),[IT,zT]=MT(RT),DT=e=>{const{__scopeDropdownMenu:t,children:n,dir:r,open:o,defaultOpen:i,onOpenChange:s,modal:a=!0}=e,l=LT(t),c=qe.useRef(null),[u=!1,d]=kj({prop:o,defaultProp:i,onChange:s});return nt.jsx(IT,{scope:t,triggerId:n$(),triggerRef:c,contentId:n$(),open:u,onOpenChange:d,onOpenToggle:qe.useCallback((()=>d((e=>!e))),[d]),modal:a,children:nt.jsx(yT,{...l,open:u,onOpenChange:d,dir:r,modal:a,children:n})})};DT.displayName=RT;var BT="DropdownMenuTrigger",UT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,disabled:r=!1,...o}=e,i=zT(BT,n),s=LT(n);return nt.jsx(bT,{asChild:!0,...s,children:nt.jsx(Nj.button,{type:"button",id:i.triggerId,"aria-haspopup":"menu","aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":i.open?"open":"closed","data-disabled":r?"":void 0,disabled:r,...o,ref:gj(t,i.triggerRef),onPointerDown:vj(e.onPointerDown,(e=>{r||0!==e.button||!1!==e.ctrlKey||(i.onOpenToggle(),i.open||e.preventDefault())})),onKeyDown:vj(e.onKeyDown,(e=>{r||(["Enter"," "].includes(e.key)&&i.onOpenToggle(),"ArrowDown"===e.key&&i.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(e.key)&&e.preventDefault())}))})})}));UT.displayName=BT;var FT=e=>{const{__scopeDropdownMenu:t,...n}=e,r=LT(t);return nt.jsx(wT,{...r,...n})};FT.displayName="DropdownMenuPortal";var HT="DropdownMenuContent",VT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=zT(HT,n),i=LT(n),s=qe.useRef(!1);return nt.jsx(xT,{id:o.contentId,"aria-labelledby":o.triggerId,...i,...r,ref:t,onCloseAutoFocus:vj(e.onCloseAutoFocus,(e=>{var t;s.current||null==(t=o.triggerRef.current)||t.focus(),s.current=!1,e.preventDefault()})),onInteractOutside:vj(e.onInteractOutside,(e=>{const t=e.detail.originalEvent,n=0===t.button&&!0===t.ctrlKey,r=2===t.button||n;o.modal&&!r||(s.current=!0)})),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})}));VT.displayName=HT;qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(kT,{...o,...r,ref:t})})).displayName="DropdownMenuGroup";var WT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(ET,{...o,...r,ref:t})}));WT.displayName="DropdownMenuLabel";var qT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(ST,{...o,...r,ref:t})}));qT.displayName="DropdownMenuItem";var KT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(CT,{...o,...r,ref:t})}));KT.displayName="DropdownMenuCheckboxItem";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(_T,{...o,...r,ref:t})})).displayName="DropdownMenuRadioGroup";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(AT,{...o,...r,ref:t})})).displayName="DropdownMenuRadioItem";var YT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(NT,{...o,...r,ref:t})}));YT.displayName="DropdownMenuItemIndicator";var QT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(PT,{...o,...r,ref:t})}));QT.displayName="DropdownMenuSeparator";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(jT,{...o,...r,ref:t})})).displayName="DropdownMenuArrow";var GT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx($T,{...o,...r,ref:t})}));GT.displayName="DropdownMenuSubTrigger";var JT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(OT,{...o,...r,ref:t,style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})}));JT.displayName="DropdownMenuSubContent";var XT=DT,ZT=UT,eL=FT,tL=VT,nL=WT,rL=qT,oL=KT,iL=YT,sL=QT,aL=GT,lL=JT;const cL=(...e)=>e.filter(((e,t,n)=>Boolean(e)&&""!==e.trim()&&n.indexOf(e)===t)).join(" ").trim();var uL={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const dL=qe.forwardRef((({color:e="currentColor",size:t=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:o="",children:i,iconNode:s,...a},l)=>qe.createElement("svg",{ref:l,...uL,width:t,height:t,stroke:e,strokeWidth:r?24*Number(n)/Number(t):n,className:cL("lucide",o),...a},[...s.map((([e,t])=>qe.createElement(e,t))),...Array.isArray(i)?i:[i]]))),fL=(e,t)=>{const n=qe.forwardRef((({className:n,...r},o)=>{return qe.createElement(dL,{ref:o,iconNode:t,className:cL(`lucide-${i=e,i.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}`,n),...r});var i}));return n.displayName=`${e}`,n},pL=fL("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),hL=fL("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]),vL=XT,mL=ZT;qe.forwardRef((({className:e,inset:t,children:n,...r},o)=>nt.jsxs(aL,{ref:o,className:ap("flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",t&&"pl-8",e),...r,children:[n,nt.jsx(hL,{className:"ml-auto h-4 w-4"})]}))).displayName=aL.displayName;qe.forwardRef((({className:e,...t},n)=>nt.jsx(lL,{ref:n,className:ap("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...t}))).displayName=lL.displayName;const gL=qe.forwardRef((({className:e,sideOffset:t=4,...n},r)=>nt.jsx(eL,{children:nt.jsx(tL,{ref:r,sideOffset:t,className:ap("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})})));gL.displayName=tL.displayName;const yL=qe.forwardRef((({className:e,inset:t,...n},r)=>nt.jsx(rL,{ref:r,className:ap("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",t&&"pl-8",e),...n})));yL.displayName=rL.displayName;qe.forwardRef((({className:e,children:t,checked:n,...r},o)=>nt.jsxs(oL,{ref:o,className:ap("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),checked:n,...r,children:[nt.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:nt.jsx(iL,{children:nt.jsx(pL,{className:"h-4 w-4"})})}),t]}))).displayName=oL.displayName;qe.forwardRef((({className:e,inset:t,...n},r)=>nt.jsx(nL,{ref:r,className:ap("px-2 py-1.5 text-sm font-semibold",t&&"pl-8",e),...n}))).displayName=nL.displayName;const bL=qe.forwardRef((({className:e,...t},n)=>nt.jsx(sL,{ref:n,className:ap("-mx-1 my-1 h-px bg-muted",e),...t})));bL.displayName=sL.displayName;const wL=new Set(["children","localName","ref","style","className"]),xL=new WeakMap,kL=(e,t,n,r,o)=>{const i=null==o?void 0:o[t];void 0===i?(e[t]=n,null==n&&t in HTMLElement.prototype&&e.removeAttribute(t)):n!==r&&((e,t,n)=>{let r=xL.get(e);void 0===r&&xL.set(e,r=new Map);let o=r.get(t);void 0!==n?void 0===o?(r.set(t,o={handleEvent:n}),e.addEventListener(t,o)):o.handleEvent=n:void 0!==o&&(r.delete(t),e.removeEventListener(t,o))})(e,i,n)},EL=({react:e,tagName:t,elementClass:n,events:r,displayName:o})=>{const i=new Set(Object.keys(r??{})),s=e.forwardRef(((o,s)=>{const a=e.useRef(new Map),l=e.useRef(null),c={},u={};for(const[e,t]of Object.entries(o))wL.has(e)?c["className"===e?"class":e]=t:i.has(e)||e in n.prototype?u[e]=t:c[e]=t;return e.useLayoutEffect((()=>{if(null===l.current)return;const e=new Map;for(const t in u)kL(l.current,t,o[t],a.current.get(t),r),a.current.delete(t),e.set(t,o[t]);for(const[t,n]of a.current)kL(l.current,t,void 0,n,r);a.current=e})),e.useLayoutEffect((()=>{var e;null==(e=l.current)||e.removeAttribute("defer-hydration")}),[]),c.suppressHydrationWarning=!0,e.createElement(t,{...c,ref:e.useCallback((e=>{l.current=e,"function"==typeof s?s(e):null!==s&&(s.current=e)}),[s])})}));return s.displayName=o??n.name,s};EL({tagName:"dc-connection-button",elementClass:pj,react:Ke});const SL=EL({tagName:"dc-connection-dialog",elementClass:sj,react:Ke,events:{onClose:"close"}});function CL(...e){const t=e=>e,n=(e,t)=>n=>e(t(n));return{encode:e.map((e=>e.encode)).reduceRight(n,t),decode:e.map((e=>e.decode)).reduce(n,t)}}function _L(e){return{encode:t=>{if(!Array.isArray(t)||t.length&&"number"!=typeof t[0])throw new Error("alphabet.encode input should be an array of numbers");return t.map((t=>{if(t<0||t>=e.length)throw new Error(`Digit index outside alphabet: ${t} (alphabet: ${e.length})`);return e[t]}))},decode:t=>{if(!Array.isArray(t)||t.length&&"string"!=typeof t[0])throw new Error("alphabet.decode input should be array of strings");return t.map((t=>{if("string"!=typeof t)throw new Error(`alphabet.decode: not string element=${t}`);const n=e.indexOf(t);if(-1===n)throw new Error(`Unknown letter: "${t}". Allowed: ${e}`);return n}))}}}function AL(e=""){if("string"!=typeof e)throw new Error("join separator should be string");return{encode:t=>{if(!Array.isArray(t)||t.length&&"string"!=typeof t[0])throw new Error("join.encode input should be array of strings");for(let e of t)if("string"!=typeof e)throw new Error(`join.encode: non-string input=${e}`);return t.join(e)},decode:t=>{if("string"!=typeof t)throw new Error("join.decode input should be string");return t.split(e)}}}function NL(e,t,n){if(t<2)throw new Error(`convertRadix: wrong from=${t}, base cannot be less than 2`);if(n<2)throw new Error(`convertRadix: wrong to=${n}, base cannot be less than 2`);if(!Array.isArray(e))throw new Error("convertRadix: data should be array");if(!e.length)return[];let r=0;const o=[],i=Array.from(e);for(i.forEach((e=>{if(e<0||e>=t)throw new Error(`Wrong integer: ${e}`)}));;){let e=0,s=!0;for(let o=r;o{if(!function(e){return e instanceof Uint8Array||null!=e&&"object"==typeof e&&"Uint8Array"===e.constructor.name}(t))throw new Error("radix.encode input should be Uint8Array");return NL(Array.from(t),256,e)},decode:t=>{if(!Array.isArray(t)||t.length&&"number"!=typeof t[0])throw new Error("radix.decode input should be array of numbers");return Uint8Array.from(NL(t,e,256))}}}const jL=(e=>CL(PL(58),_L(e),AL("")))("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");function $L(e){if(!Number.isSafeInteger(e)||e<0)throw new Error(`positive integer expected, not ${e}`)}function OL(e,...t){if(!function(e){return e instanceof Uint8Array||null!=e&&"object"==typeof e&&"Uint8Array"===e.constructor.name}(e))throw new Error("Uint8Array expected");if(t.length>0&&!t.includes(e.length))throw new Error(`Uint8Array expected of length ${t}, not of length=${e.length}`)}function RL(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}const ML=e=>new Uint32Array(e.buffer,e.byteOffset,Math.floor(e.byteLength/4)),TL=68===new Uint8Array(new Uint32Array([287454020]).buffer)[0],LL=e=>e<<24&4278190080|e<<8&16711680|e>>>8&65280|e>>>24&255,IL=TL?e=>e:e=>LL(e);function zL(e){for(let t=0;te(n).update(DL(t)).digest(),n=e({});return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=t=>e(t),t}const FL=new Uint8Array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3,11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4,7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8,9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13,2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9,12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11,13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10,6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5,10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3]);class HL extends BL{constructor(e,t,n={},r,o,i){if(super(),this.blockLen=e,this.outputLen=t,this.length=0,this.pos=0,this.finished=!1,this.destroyed=!1,$L(e),$L(t),$L(r),t<0||t>r)throw new Error("outputLen bigger than keyLen");if(void 0!==n.key&&(n.key.length<1||n.key.length>r))throw new Error(`key must be up 1..${r} byte long or undefined`);if(void 0!==n.salt&&n.salt.length!==o)throw new Error(`salt must be ${o} byte long or undefined`);if(void 0!==n.personalization&&n.personalization.length!==i)throw new Error(`personalization must be ${i} byte long or undefined`);this.buffer32=ML(this.buffer=new Uint8Array(e))}update(e){RL(this);const{blockLen:t,buffer:n,buffer32:r}=this,o=(e=DL(e)).length,i=e.byteOffset,s=e.buffer;for(let a=0;ar[t]=IL(e)))}digest(){const{buffer:e,outputLen:t}=this;this.digestInto(e);const n=e.slice(0,t);return this.destroy(),n}_cloneInto(e){const{buffer:t,length:n,finished:r,destroyed:o,outputLen:i,pos:s}=this;return e||(e=new this.constructor({dkLen:i})),e.set(...this.get()),e.length=n,e.finished=r,e.destroyed=o,e.outputLen=i,e.buffer.set(t),e.pos=s,e}}const VL=BigInt(2**32-1),WL=BigInt(32);function qL(e,t=!1){return t?{h:Number(e&VL),l:Number(e>>WL&VL)}:{h:0|Number(e>>WL&VL),l:0|Number(e&VL)}}const KL={fromBig:qL,split:function(e,t=!1){let n=new Uint32Array(e.length),r=new Uint32Array(e.length);for(let o=0;oBigInt(e>>>0)<>>0),shrSH:(e,t,n)=>e>>>n,shrSL:(e,t,n)=>e<<32-n|t>>>n,rotrSH:(e,t,n)=>e>>>n|t<<32-n,rotrSL:(e,t,n)=>e<<32-n|t>>>n,rotrBH:(e,t,n)=>e<<64-n|t>>>n-32,rotrBL:(e,t,n)=>e>>>n-32|t<<64-n,rotr32H:(e,t)=>t,rotr32L:(e,t)=>e,rotlSH:(e,t,n)=>e<>>32-n,rotlSL:(e,t,n)=>t<>>32-n,rotlBH:(e,t,n)=>t<>>64-n,rotlBL:(e,t,n)=>e<>>64-n,add:function(e,t,n,r){const o=(t>>>0)+(r>>>0);return{h:e+n+(o/2**32|0)|0,l:0|o}},add3L:(e,t,n)=>(e>>>0)+(t>>>0)+(n>>>0),add3H:(e,t,n,r)=>t+n+r+(e/2**32|0)|0,add4L:(e,t,n,r)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0),add4H:(e,t,n,r,o)=>t+n+r+o+(e/2**32|0)|0,add5H:(e,t,n,r,o,i)=>t+n+r+o+i+(e/2**32|0)|0,add5L:(e,t,n,r,o)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0)+(o>>>0)},YL=new Uint32Array([4089235720,1779033703,2227873595,3144134277,4271175723,1013904242,1595750129,2773480762,2917565137,1359893119,725511199,2600822924,4215389547,528734635,327033209,1541459225]),QL=new Uint32Array(32);function GL(e,t,n,r,o,i){const s=o[i],a=o[i+1];let l=QL[2*e],c=QL[2*e+1],u=QL[2*t],d=QL[2*t+1],f=QL[2*n],p=QL[2*n+1],h=QL[2*r],v=QL[2*r+1],m=KL.add3L(l,u,s);c=KL.add3H(m,c,d,a),l=0|m,({Dh:v,Dl:h}={Dh:v^c,Dl:h^l}),({Dh:v,Dl:h}={Dh:KL.rotr32H(v,h),Dl:KL.rotr32L(v,h)}),({h:p,l:f}=KL.add(p,f,v,h)),({Bh:d,Bl:u}={Bh:d^p,Bl:u^f}),({Bh:d,Bl:u}={Bh:KL.rotrSH(d,u,24),Bl:KL.rotrSL(d,u,24)}),QL[2*e]=l,QL[2*e+1]=c,QL[2*t]=u,QL[2*t+1]=d,QL[2*n]=f,QL[2*n+1]=p,QL[2*r]=h,QL[2*r+1]=v}function JL(e,t,n,r,o,i){const s=o[i],a=o[i+1];let l=QL[2*e],c=QL[2*e+1],u=QL[2*t],d=QL[2*t+1],f=QL[2*n],p=QL[2*n+1],h=QL[2*r],v=QL[2*r+1],m=KL.add3L(l,u,s);c=KL.add3H(m,c,d,a),l=0|m,({Dh:v,Dl:h}={Dh:v^c,Dl:h^l}),({Dh:v,Dl:h}={Dh:KL.rotrSH(v,h,16),Dl:KL.rotrSL(v,h,16)}),({h:p,l:f}=KL.add(p,f,v,h)),({Bh:d,Bl:u}={Bh:d^p,Bl:u^f}),({Bh:d,Bl:u}={Bh:KL.rotrBH(d,u,63),Bl:KL.rotrBL(d,u,63)}),QL[2*e]=l,QL[2*e+1]=c,QL[2*t]=u,QL[2*t+1]=d,QL[2*n]=f,QL[2*n+1]=p,QL[2*r]=h,QL[2*r+1]=v}class XL extends HL{constructor(e={}){super(128,void 0===e.dkLen?64:e.dkLen,e,64,16,16),this.v0l=0|YL[0],this.v0h=0|YL[1],this.v1l=0|YL[2],this.v1h=0|YL[3],this.v2l=0|YL[4],this.v2h=0|YL[5],this.v3l=0|YL[6],this.v3h=0|YL[7],this.v4l=0|YL[8],this.v4h=0|YL[9],this.v5l=0|YL[10],this.v5h=0|YL[11],this.v6l=0|YL[12],this.v6h=0|YL[13],this.v7l=0|YL[14],this.v7h=0|YL[15];const t=e.key?e.key.length:0;if(this.v0l^=this.outputLen|t<<8|65536|1<<24,e.salt){const t=ML(DL(e.salt));this.v4l^=IL(t[0]),this.v4h^=IL(t[1]),this.v5l^=IL(t[2]),this.v5h^=IL(t[3])}if(e.personalization){const t=ML(DL(e.personalization));this.v6l^=IL(t[0]),this.v6h^=IL(t[1]),this.v7l^=IL(t[2]),this.v7h^=IL(t[3])}if(e.key){const t=new Uint8Array(this.blockLen);t.set(DL(e.key)),this.update(t)}}get(){let{v0l:e,v0h:t,v1l:n,v1h:r,v2l:o,v2h:i,v3l:s,v3h:a,v4l:l,v4h:c,v5l:u,v5h:d,v6l:f,v6h:p,v7l:h,v7h:v}=this;return[e,t,n,r,o,i,s,a,l,c,u,d,f,p,h,v]}set(e,t,n,r,o,i,s,a,l,c,u,d,f,p,h,v){this.v0l=0|e,this.v0h=0|t,this.v1l=0|n,this.v1h=0|r,this.v2l=0|o,this.v2h=0|i,this.v3l=0|s,this.v3h=0|a,this.v4l=0|l,this.v4h=0|c,this.v5l=0|u,this.v5h=0|d,this.v6l=0|f,this.v6h=0|p,this.v7l=0|h,this.v7h=0|v}compress(e,t,n){this.get().forEach(((e,t)=>QL[t]=e)),QL.set(YL,16);let{h:r,l:o}=KL.fromBig(BigInt(this.length));QL[24]=YL[8]^o,QL[25]=YL[9]^r,n&&(QL[28]=~QL[28],QL[29]=~QL[29]);let i=0;const s=FL;for(let a=0;a<12;a++)GL(0,4,8,12,e,t+2*s[i++]),JL(0,4,8,12,e,t+2*s[i++]),GL(1,5,9,13,e,t+2*s[i++]),JL(1,5,9,13,e,t+2*s[i++]),GL(2,6,10,14,e,t+2*s[i++]),JL(2,6,10,14,e,t+2*s[i++]),GL(3,7,11,15,e,t+2*s[i++]),JL(3,7,11,15,e,t+2*s[i++]),GL(0,5,10,15,e,t+2*s[i++]),JL(0,5,10,15,e,t+2*s[i++]),GL(1,6,11,12,e,t+2*s[i++]),JL(1,6,11,12,e,t+2*s[i++]),GL(2,7,8,13,e,t+2*s[i++]),JL(2,7,8,13,e,t+2*s[i++]),GL(3,4,9,14,e,t+2*s[i++]),JL(3,4,9,14,e,t+2*s[i++]);this.v0l^=QL[0]^QL[16],this.v0h^=QL[1]^QL[17],this.v1l^=QL[2]^QL[18],this.v1h^=QL[3]^QL[19],this.v2l^=QL[4]^QL[20],this.v2h^=QL[5]^QL[21],this.v3l^=QL[6]^QL[22],this.v3h^=QL[7]^QL[23],this.v4l^=QL[8]^QL[24],this.v4h^=QL[9]^QL[25],this.v5l^=QL[10]^QL[26],this.v5h^=QL[11]^QL[27],this.v6l^=QL[12]^QL[28],this.v6h^=QL[13]^QL[29],this.v7l^=QL[14]^QL[30],this.v7h^=QL[15]^QL[31],QL.fill(0)}destroy(){this.destroyed=!0,this.buffer32.fill(0),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}}const ZL=UL((e=>new XL(e))),eI=(new TextEncoder).encode("SS58PRE"),tI=e=>{try{const t=jL.decode(e),n=t.subarray(0,64&t[0]?2:1),r=t.subarray(n.length,t.length-2),o=t.subarray(n.length+r.length),i=ZL(Uint8Array.of(...eI,...n,...r),{dkLen:64}).subarray(0,2);return o[0]===i[0]&&o[1]===i[1]?{isValid:!0,ss58Format:nI(n),publicKey:r.slice()}:{isValid:!1}}catch{return{isValid:!1}}},nI=e=>{const t=new DataView(e.buffer,e.byteOffset,e.byteLength);return 1===t.byteLength?t.getUint8(0):t.getUint16(0)},rI={target:{colors:[0,28,0,0,28,0,0,28,0,0,28,0,0,28,0,0,28,0,1],freq:1},cube:{colors:[0,1,3,2,4,3,0,1,3,2,4,3,0,1,3,2,4,3,5],freq:20},quazar:{colors:[1,2,3,1,2,4,5,5,4,1,2,3,1,2,4,5,5,4,0],freq:16},flower:{colors:[0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,3],freq:32},cyclic:{colors:[0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,6],freq:32},vmirror:{colors:[0,1,2,3,4,5,3,4,2,0,1,6,7,8,9,7,8,6,10],freq:128},hmirror:{colors:[0,1,2,3,4,5,6,7,8,9,10,8,6,7,5,3,4,2,11],freq:128}},oI=e=>{let t=0;const n=Object.values(rI).find((n=>(t+=n.freq,e{var l;const[c,u]=qe.useState(!1),[d,f]=qe.useState(i),[p,h]=qe.useState(),[v,m]=qe.useState(),[g,y]=qe.useState();qe.useEffect((()=>{if("string"==typeof e&&!e.includes("px")&&!e.includes("rem"))throw new Error("Providing a string for 'size' in Polkicon should be expressed either in 'px', 'rem' or 'em'");let t,n;if("string"==typeof e)switch(n=e.replace(/[0-9.]/g,""),n){case"px":t=parseFloat(e);break;case"rem":t=10*parseFloat(e)}else"number"==typeof e&&(t=e);var r,o;h(n?""+("px"===n?t+"px":t/10+"rem"):t),t<12&&(r=n||"number",o="px"===n?"12px":"rem"===n?"1.2rem":12,console.warn(`Polkicon: 'Size' expressed in '${r}' cannot be less than ${o}. Will be resized to minimum size.`)),t<32?(y("0rem 0.5rem"),m("0.5rem")):t>=32&&t<64?(y("1rem 0.5rem"),m("1rem")):t>=64&&t<100?(y("2rem 1rem"),m("1.5rem")):t>=100&&(y("3rem 1rem"),m("2rem"))}),[e]);const b=qe.useCallback((()=>{n&&(async e=>{try{await navigator.clipboard.writeText(e),u(!0),f(i)}catch{u(!0),f("Failed!")}})(t)}),[n,t,i]);qe.useEffect((()=>{n&&c&&setTimeout((()=>{u(!1)}),o)}),[n,c,o]);const{c:w,r:x,rroot3o2:k,ro2:E,rroot3o4:S,ro4:C,r3o4:_,z:A,rot:N,scheme:P,palette:j}=(e=>{const t=ZL(new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])),n=tI(e);let r=n.isValid?n.publicKey:e;if(!("object"==typeof r&&r&&r instanceof Uint8Array&&32==r.length))return{};r=Uint8Array.from(ZL(r)).map(((e,n)=>(e+256-t[n])%256));const o=22.4,i=o*Math.sqrt(3)/2,s=o*Math.sqrt(3)/4,a=3*o/4,l=Object.keys(rI).map((e=>rI[e].freq)).reduce(((e,t)=>e+t)),c=Math.floor(70*r[29]/256+26)%80+30,u=Math.floor((r[30]+256*r[31])%l),d=oI(u),f=Array.from(r).map(((e,t)=>{const n=(e+t%28*58)%256;if(0==n)return"#444";if(255==n)return"transparent";const r=Math.floor(n%64*360/64),o=[53,15,35,75][Math.floor(n/64)];return`hsl(${r}, ${c}%, ${o}%)`}));return{c:32,r:o,rroot3o2:i,ro2:11.2,rroot3o4:s,ro4:5.6,r3o4:a,z:5,rot:r[28]%6*3,scheme:d,palette:f}})(t),$=tI(t).isValid?null==(l=null==P?void 0:P.colors)?void 0:l.map(((e,t)=>j[null==P?void 0:P.colors[t<18?(t+N)%18:18]])):[];let O=0;return $?nt.jsxs("div",{onClick:n?b:void 0,style:n?{cursor:c?"none":"copy",position:"relative",display:"flex",justifyContent:"center",alignItems:"center"}:{display:"flex",justifyContent:"center",alignItems:"center"},children:[nt.jsxs("svg",{id:Math.random().toString(36).substring(2,9),className:a,style:s,width:p,height:p,viewBox:"0 0 64 64",children:[nt.jsx("circle",{cx:32,cy:32,r:32,fill:r}),nt.jsx("circle",{cx:w,cy:w-x,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w-E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w-_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-k,cy:w-E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w-C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-k,cy:w,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-k,cy:w+E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w+C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w+_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w+x,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w+E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w+_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+k,cy:w+E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w+C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+k,cy:w,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+k,cy:w-E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w-C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w-_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w,r:A,fill:$[O++]})]}),n&&c&&nt.jsx("p",{style:{fontSize:v,fontWeight:"bold",padding:g,width:p,height:p,position:"absolute",borderRadius:"55rem",color:"white",background:"green",opacity:"80%",alignItems:"center",justifyContent:"center",display:"flex"},children:d})]}):null},sI=()=>{const{accounts:e,selectAccount:t,selectedAccount:n}=YS(),[,r]=DS(),[o,i]=qe.useState(!1);return qe.useEffect((()=>{!(null==n?void 0:n.address)&&e.length>0&&(t(e[0]),i(!1))}),[e,t,null==n?void 0:n.address]),nt.jsxs(nt.Fragment,{children:[nt.jsx("div",{className:"flex w-full justify-center",children:nt.jsxs("div",{className:"flex w-full",children:[!e.length&&nt.jsx(aC,{onClick:()=>i(!0),className:"w-full text-lg font-bold bg-pink-700 hover:bg-blue-600",children:"Connect Wallet"}),!!e.length&&nt.jsxs(vL,{children:[nt.jsx(mL,{asChild:!0,children:nt.jsxs(aC,{variant:"outline",size:"default",className:"cursor-pointer overflow-hidden w-full flex items-center justify-center gap-2",children:[nt.jsx(iI,{size:36,address:(null==n?void 0:n.address)||"",className:"mr-2",outerColor:"transparent"}),null==n?void 0:n.name,nt.jsx(cC,{className:"ml-2 h-4 w-4",isOpen:!1})]})}),nt.jsxs(gL,{className:"max-h-[calc(100vh-5rem)] overflow-auto",children:[e.map(((n,r)=>nt.jsxs(qe.Fragment,{children:[nt.jsxs(yL,{className:"cursor-pointer",onClick:()=>t(n),children:[nt.jsx(iI,{size:28,address:n.address||"",className:"mr-2",outerColor:"transparent"}),n.name]},n.address),r!==e.length-1&&nt.jsx(bL,{})]},n.address))),nt.jsx(yL,{className:"cursor-pointer",onClick:()=>{i(!0)},children:"Show wallets"},"show"),nt.jsx(yL,{className:"cursor-pointer",onClick:()=>{r(),t(void 0)},children:"Disconnect"},"logout")]})]})]})}),nt.jsx(SL,{open:o,onClose:()=>i(!1)})]})};function aI(){return nt.jsx("div",{className:"flex items-center justify-center mt-6",children:nt.jsxs(up,{className:"w-full max-w-xl",children:[nt.jsx(dp,{children:nt.jsxs(fp,{className:"text-4xl text-center flex items-center justify-center gap-2",children:[nt.jsx(lI,{}),nt.jsxs("span",{children:["CLI: ",nt.jsx("span",{className:"font-light",children:"Signing Portal"})]})]})}),nt.jsx(pp,{className:"grid gap-4",children:nt.jsx(PS,{config:hj,children:nt.jsx(KS,{children:nt.jsxs("div",{className:"overflow-auto break-words whitespace-pre-wrap",children:[nt.jsx(sI,{}),nt.jsx(wC,{})]})})})})]})})}const lI=()=>nt.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"100",height:"100",fill:"none",viewBox:"0 0 512 512",children:[nt.jsxs("g",{clipPath:"url(#clip0_873_174)",children:[nt.jsx("rect",{width:"512",height:"512",fill:"#1C0533",rx:"256"}),nt.jsx("rect",{width:"512",height:"512",fill:"url(#paint0_radial_873_174)",fillOpacity:"0.8",rx:"256"}),nt.jsx("rect",{width:"512",height:"512",fill:"url(#paint1_radial_873_174)",fillOpacity:"0.6",rx:"256"}),nt.jsx("mask",{id:"mask0_873_174",width:"1428",height:"1351",x:"-429",y:"-502",maskUnits:"userSpaceOnUse",style:{maskType:"alpha"},children:nt.jsx("path",{fill:"#D9D9D9",d:"m127.637-501.289 871.274 824.192-14.25 13.48-871.273-824.192zM88.96-464.701l871.272 824.192-14.249 13.48L74.71-451.221zM50.281-428.113l871.273 824.192-14.249 13.48L36.032-414.633zM11.603-391.525l871.273 824.192-14.249 13.48L-2.646-378.045zM-27.075-354.937l871.273 824.192-14.249 13.48-871.273-824.192zM-65.753-318.349 805.52 505.843l-14.249 13.48-871.273-824.192zM-104.431-281.761l871.273 824.192-14.249 13.48-871.273-824.192zM-143.109-245.173l871.273 824.192-14.249 13.48-871.273-824.192zM-181.787-208.585l871.273 824.192-14.249 13.48-871.273-824.192zM-220.465-171.997l871.273 824.192-14.249 13.48-871.273-824.192zM-259.143-135.409 612.13 688.783l-14.249 13.48-871.273-824.192zM-297.821-98.821 573.452 725.37l-14.249 13.48L-312.07-85.341zM-336.499-62.233l871.273 824.192-14.249 13.48-871.273-824.192zM-375.177-25.645l871.273 824.192-14.249 13.479-871.273-824.191zM-413.855 10.943l871.273 824.192-14.249 13.48-871.273-824.192z"})}),nt.jsx("g",{mask:"url(#mask0_873_174)",children:nt.jsx("path",{fill:"#E6007A",d:"M511.169 254.929C511.169 396.905 396.739 512 255.584 512 114.428 512-.001 396.905-.001 254.929S114.428-2.142 255.584-2.142c141.155 0 255.585 115.095 255.585 257.071"})}),nt.jsx("path",{fill:"#2B0532",d:"M183.804 160.44c-12.752-9.296-27.346-13.272-41.94-13.272h-89.63L5.63 367.567h117.053l9.399-44.802a86.4 86.4 0 0 0 28.32-8.371 104 104 0 0 0 4.133-2.155 97.7 97.7 0 0 0 14.779 24.342c8.258 9.967 18.342 17.785 29.802 23.435l.003.002.397.195.4.186c12.688 5.89 26.181 8.385 39.713 8.385 13.588 0 26.877-2.295 39.662-6.916a127 127 0 0 0 17.54-7.88l-2.871 13.579h117.052l9.4-44.802a86.4 86.4 0 0 0 28.32-8.371 101.4 101.4 0 0 0 28.939-21.348l.01-.011.159-.168.154-.167.003-.003c8.092-8.766 14.613-18.702 19.34-29.722 4.865-11.344 7.542-23.439 7.542-36.022 0-12.506-2.649-24.626-7.879-35.976-5.493-12.224-13.86-22.642-24.867-30.537-12.752-9.296-27.346-13.272-41.94-13.272h-89.631l-5.24 24.781c-7.026-6.908-15.096-12.504-24.009-16.758-12.764-6.233-26.404-8.834-40.034-8.834-13.589 0-26.878 2.295-39.662 6.916-12.145 4.39-23.444 10.507-33.804 18.268a133 133 0 0 0-7.099 5.68 74.6 74.6 0 0 0-16.911-16.781"}),nt.jsx("path",{fill:"#E6007A",fillRule:"evenodd",d:"m229.569 331.163.063.031.064.03c7.896 3.665 16.711 5.426 26.323 5.426 9.96 0 19.59-1.672 28.858-5.022a94.6 94.6 0 0 0 25.4-13.857c7.69-5.873 14.403-12.621 20.12-20.24l.016-.022.016-.022c5.708-7.75 10.156-16.036 13.319-24.849 3.326-9.027 5.005-18.25 5.005-27.639 0-8.458-1.303-16.601-3.928-24.399-2.514-8.002-6.373-15.177-11.585-21.468-5.277-6.369-11.756-11.363-19.365-14.972-7.797-3.835-16.58-5.669-26.205-5.669-9.961 0-19.591 1.673-28.858 5.022-9.165 3.313-17.687 7.924-25.551 13.819-7.719 5.752-14.51 12.512-20.362 20.265l-.01.014a97.4 97.4 0 0 0-13.544 24.867l-.015.04-.014.041c-3.181 9.016-4.784 18.219-4.784 27.583 0 8.482 1.311 16.702 3.94 24.634l.005.016.005.016c2.653 7.855 6.575 14.936 11.766 21.201 5.269 6.361 11.733 11.413 19.321 15.154m58.163-55.148.01-.015c2.998-4.343 5.197-8.955 6.622-13.852 1.467-5.04 2.176-9.872 2.176-14.513 0-4.335-.697-8.177-2.012-11.592l-.041-.109-.038-.11c-1.121-3.279-2.866-5.693-5.197-7.475-2.026-1.548-5.053-2.613-9.646-2.613-5.335 0-9.796 1.243-13.579 3.569l-.053.032-.053.031c-4.052 2.378-7.426 5.537-10.156 9.541a52.3 52.3 0 0 0-6.661 13.745c-1.454 5.016-2.157 9.826-2.157 14.447 0 4.358.638 8.321 1.862 11.932 1.246 3.306 3.081 5.852 5.484 7.803 2.047 1.543 5.008 2.569 9.387 2.569 5.495 0 10.017-1.255 13.78-3.568 4.107-2.524 7.515-5.779 10.262-9.808zM171.542 186.53c-6.663-4.89-14.525-7.228-23.288-7.228H84.491L51.478 335.434h51.685l9.273-44.204h15.658c8.698 0 16.935-1.893 24.636-5.69a69.4 69.4 0 0 0 19.864-14.652l.025-.027.025-.027c5.695-6.169 10.18-13.036 13.418-20.587 3.261-7.602 4.93-15.381 4.93-23.294 0-7.901-1.665-15.457-4.983-22.605-3.247-7.286-8.082-13.265-14.467-17.818m-35.967 55.601.023-.03c.765-1.001 1.462-2.391 1.969-4.328l.037-.142.044-.141c.574-1.827.929-4.221.929-7.293 0-2.333-.311-3.426-.466-3.769a4 4 0 0 0-.222-.434l-.027-.045-.013-.01h-11.5l-3.905 18.655h10.249c.063-.013.306-.078.757-.35.511-.309 1.235-.919 2.103-2.084zM469.872 186.53c-6.663-4.89-14.525-7.228-23.289-7.228h-63.762l-33.014 156.132h51.686l9.273-44.204h15.658c8.698 0 16.935-1.893 24.636-5.69a69.4 69.4 0 0 0 19.863-14.652l.026-.027.024-.027c5.696-6.169 10.18-13.036 13.419-20.587 3.26-7.602 4.93-15.381 4.93-23.294 0-7.901-1.665-15.457-4.983-22.605-3.247-7.286-8.082-13.265-14.467-17.818m-35.967 55.601.023-.03c.765-1.001 1.462-2.391 1.968-4.328l.038-.142.044-.141c.574-1.827.929-4.221.929-7.293 0-2.333-.311-3.426-.466-3.769a4 4 0 0 0-.222-.434l-.027-.045-.014-.01h-11.5l-3.904 18.655h10.249c.063-.013.306-.078.756-.35.511-.309 1.235-.919 2.104-2.084z",clipRule:"evenodd"}),nt.jsxs("mask",{id:"path-6-outside-1_873_174",width:"435",height:"156",x:"35.823",y:"166.788",fill:"#000",maskUnits:"userSpaceOnUse",children:[nt.jsx("path",{fill:"#fff",d:"M35.823 166.788h435v156h-435z"}),nt.jsx("path",{fillRule:"evenodd",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318m-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391m-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852m133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd"})]}),nt.jsx("path",{fill:"#fff",fillRule:"evenodd",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318m-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391m-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852m133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd"}),nt.jsx("path",{stroke:"#fff",strokeWidth:"0.3",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318Zm-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391Zm-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852Zm133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd",mask:"url(#path-6-outside-1_873_174)"})]}),nt.jsxs("defs",{children:[nt.jsxs("radialGradient",{id:"paint0_radial_873_174",cx:"0",cy:"0",r:"1",gradientTransform:"matrix(229.99965 647.20057 -371.3092 131.95444 -62.8 -86.4)",gradientUnits:"userSpaceOnUse",children:[nt.jsx("stop",{stopColor:"#6335EC"}),nt.jsx("stop",{offset:"1",stopOpacity:"0"})]}),nt.jsxs("radialGradient",{id:"paint1_radial_873_174",cx:"0",cy:"0",r:"1",gradientTransform:"rotate(-116.448 530.068 140.453)scale(1055.25 662.094)",gradientUnits:"userSpaceOnUse",children:[nt.jsx("stop",{stopColor:"#E6007A"}),nt.jsx("stop",{offset:"1",stopOpacity:"0"})]}),nt.jsx("clipPath",{id:"clip0_873_174",children:nt.jsx("rect",{width:"512",height:"512",fill:"#fff",rx:"256"})})]})]});rt.createRoot(document.getElementById("root")).render(nt.jsx(Ke.StrictMode,{children:nt.jsx(aI,{})})); + From 7a724b34509c8c3332c02fdcd46d4ba1f223b65f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 27 Feb 2025 18:28:49 +0100 Subject: [PATCH 056/117] refactor: remove display command information (separate PR) --- crates/pop-cli/src/commands/up/parachain.rs | 51 --------------------- 1 file changed, 51 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 26d83745f..5ba111123 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -56,7 +56,6 @@ impl UpChainCommand { return Ok(()); }, }; - chain_config.display(); match chain_config.register_parachain(cli).await { Ok(_) => cli.success("Chain deployed successfully")?, Err(e) => cli.outro_cancel(format!("{}", e))?, @@ -166,18 +165,6 @@ impl UpChain { } Ok(xt.encode_call_data(&chain.client.metadata())?) } - - fn display(&self) -> String { - let mut full_message = "pop up".to_string(); - full_message.push_str(&format!(" --id {}", self.id)); - full_message.push_str(&format!(" --genesis-state {}", self.genesis_state.display())); - full_message.push_str(&format!(" --genesis-code {}", self.genesis_code.display())); - full_message.push_str(&format!(" --relay-url {}", self.chain.url)); - if let ProxyConfig::Address(addr) = &self.proxy { - full_message.push_str(&format!(" --proxy {}", addr)); - } - full_message - } } // Reserves a parachain ID by submitting an extrinsic. @@ -507,44 +494,6 @@ mod tests { Ok(()) } - #[tokio::test] - async fn display_works() -> Result<()> { - let mut cli = MockCli::new(); - let chain = configure_chain( - "Enter the relay chain node URL to deploy your parachain", - DEFAULT_URL, - &Some(Url::parse(POLKADOT_NETWORK_URL)?), - &mut cli, - ) - .await?; - // Create a temporary files to act as genesis_state and genesis_code files. - let temp_dir = tempdir()?; - let genesis_state_path = temp_dir.path().join("genesis_state"); - let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); - - let up_chain = UpChain { - id: 2000, - genesis_state: genesis_state_path, - genesis_code: genesis_code_path, - chain, - proxy: ProxyConfig::Address( - "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), - ), - }; - assert_eq!( - up_chain.display(), - format!( - "pop up --id {} --genesis-state {} --genesis-code {} --relay-url {} --proxy {}", - up_chain.id, - up_chain.genesis_state.display(), - up_chain.genesis_code.display(), - up_chain.chain.url, - "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)" - ) - ); - Ok(()) - } - // Creates temporary files to act as `genesis_state` and `genesis_code` files. fn create_temp_genesis_files() -> Result<(PathBuf, PathBuf)> { let temp_dir = tempdir()?; // Create a temporary directory From f6b26d69867d9beb7be53e01f1626d60b1a38811 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 27 Feb 2025 18:39:53 +0100 Subject: [PATCH 057/117] test: fix resolve_proxy_address_works --- crates/pop-cli/src/commands/up/parachain.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 5ba111123..4f8f243f1 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -304,7 +304,10 @@ mod tests { ); cli.verify()?; - cli = MockCli::new().expect_confirm("Do you want to use a proxy for registration?", false); + cli = MockCli::new().expect_confirm( + "Would you like to use a proxy for registration? This is considered a best practice.", + false, + ); let proxy_address = UpChainCommand::default().resolve_proxy_address(&chain, &mut cli)?; assert_eq!(proxy_address, ProxyConfig::None); cli.verify()?; From b34e67fb1796e34e392fa6d7d3d7a02462e23654 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 28 Feb 2025 11:00:37 +0100 Subject: [PATCH 058/117] refactor: rename and more clear messages for the user to prompt proxied account --- crates/pop-cli/src/commands/up/parachain.rs | 42 +++++++++++---------- crates/pop-parachains/src/call/mod.rs | 6 +-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 4f8f243f1..f2d30dd57 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -39,10 +39,10 @@ pub struct UpChainCommand { /// Websocket endpoint of the relay chain. #[arg(long)] pub(crate) relay_url: Option, - /// Proxy address for registration. Specify the address type, e.g. - /// `Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)`. + /// Proxied account address. The proxy can act on behalf of this account. + /// Specify the address type, e.g. `Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)`. #[arg(long = "proxy")] - pub(crate) proxy_address: Option, + pub(crate) proxied_address: Option, } impl UpChainCommand { @@ -73,18 +73,19 @@ impl UpChainCommand { ) .await?; - let proxy = self.resolve_proxy_address(&chain, cli)?; + let proxy = self.resolve_proxied_address(&chain, cli)?; let para_id = self.resolve_parachain_id(&chain, &proxy, cli).await?; let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; Ok(UpChain { id: para_id, genesis_state, genesis_code, chain, proxy }) } - // Retrieves the proxy address, prompting the user if none is specified. - fn resolve_proxy_address(&self, chain: &Chain, cli: &mut impl Cli) -> Result { - if let Some(addr) = &self.proxy_address { + // Retrieves the proxied address, prompting the user if none is specified. + fn resolve_proxied_address(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + if let Some(addr) = &self.proxied_address { return Ok(ProxyConfig::Address(addr.clone())); } if cli.confirm("Would you like to use a proxy for registration? This is considered a best practice.").interact()? { + cli.info("Enter the account the proxy will represent.")?; let proxy = find_dispatchable_by_name(&chain.pallets, "Proxy", "proxy")?; let address = prompt_for_param(cli, &proxy.params[0])?; Ok(ProxyConfig::Address(address)) @@ -245,7 +246,7 @@ mod tests { id: Some(2000), genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), - proxy_address: Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()), + proxied_address: Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()), ..Default::default() } .prepare_chain_for_registration(&mut cli) @@ -265,9 +266,10 @@ mod tests { } #[tokio::test] - async fn resolve_proxy_address_works() -> Result<()> { + async fn resolve_proxied_address_works() -> Result<()> { let mut cli = MockCli::new() .expect_confirm("Would you like to use a proxy for registration? This is considered a best practice.", true) + .expect_info("Enter the account the proxy will represent.") .expect_select( "Select the value for the parameter: real", Some(true), @@ -295,9 +297,9 @@ mod tests { &mut cli, ) .await?; - let proxy_address = UpChainCommand::default().resolve_proxy_address(&chain, &mut cli)?; + let proxied_address = UpChainCommand::default().resolve_proxied_address(&chain, &mut cli)?; assert_eq!( - proxy_address, + proxied_address, ProxyConfig::Address( "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string() ) @@ -308,18 +310,18 @@ mod tests { "Would you like to use a proxy for registration? This is considered a best practice.", false, ); - let proxy_address = UpChainCommand::default().resolve_proxy_address(&chain, &mut cli)?; - assert_eq!(proxy_address, ProxyConfig::None); + let proxied_address = UpChainCommand::default().resolve_proxied_address(&chain, &mut cli)?; + assert_eq!(proxied_address, ProxyConfig::None); cli.verify()?; cli = MockCli::new(); - let proxy_address = UpChainCommand { - proxy_address: Some("Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string()), + let proxied_address = UpChainCommand { + proxied_address: Some("Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string()), ..Default::default() } - .resolve_proxy_address(&chain, &mut cli)?; + .resolve_proxied_address(&chain, &mut cli)?; assert_eq!( - proxy_address, + proxied_address, ProxyConfig::Address( "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string() ) @@ -371,7 +373,7 @@ mod tests { genesis_code: Some(genesis_code.clone()), relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: None, - proxy_address: None, + proxied_address: None, } .execute(&mut cli) .await?; @@ -423,7 +425,7 @@ mod tests { genesis_code: None, relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: Some(project_path.clone()), - proxy_address: None, + proxied_address: None, } .execute(&mut cli) .await?; @@ -447,7 +449,7 @@ mod tests { genesis_code: Some(genesis_code.clone()), relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: None, - proxy_address: None, + proxied_address: None, } .execute(&mut cli) .await?; diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 24d862409..0867079bf 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -50,12 +50,12 @@ pub fn construct_sudo_extrinsic(xt: DynamicPayload) -> DynamicPayload { /// /// # Arguments /// * `pallets`: List of pallets available within the chain's runtime. -/// * `proxy_account` - The proxied account that will execute the extrinsic. +/// * `proxied_account` - The account on whose behalf the proxy will act. /// * `xt`: The extrinsic representing the dispatchable function call to be dispatched using the /// proxy. pub fn construct_proxy_extrinsic( pallets: &[Pallet], - proxy_account: String, + proxied_account: String, xt: DynamicPayload, ) -> Result { let proxy_function = find_dispatchable_by_name(pallets, "Proxy", "proxy")?; @@ -66,7 +66,7 @@ pub fn construct_proxy_extrinsic( let required_params: Vec = proxy_function.params.iter().take(2).cloned().collect(); let parsed_args: Vec = metadata::parse_dispatchable_arguments( &required_params, - vec![proxy_account, "None()".to_string()], + vec![proxied_account, "None()".to_string()], )?; Ok(subxt::dynamic::tx( From dc12061968edb29bfaa3a17dc87642df326df1a4 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 28 Feb 2025 13:32:18 +0100 Subject: [PATCH 059/117] chore: update index.html with latest changes --- crates/pop-cli/src/assets/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/assets/index.html b/crates/pop-cli/src/assets/index.html index 668758c5e..aa4ec5319 100644 --- a/crates/pop-cli/src/assets/index.html +++ b/crates/pop-cli/src/assets/index.html @@ -7,7 +7,7 @@ href="data:image/svg+xml,%3Csvg width='512' height='512' viewBox='0 0 512 512' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_873_174)'%3E%3Crect width='512' height='512' rx='256' fill='%231C0533'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint0_radial_873_174)' fill-opacity='0.8'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint1_radial_873_174)' fill-opacity='0.6'/%3E%3Cmask id='mask0_873_174' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='-429' y='-502' width='1428' height='1351'%3E%3Cpath d='M127.637 -501.289L998.911 322.903L984.661 336.383L113.388 -487.809L127.637 -501.289Z' fill='%23D9D9D9'/%3E%3Cpath d='M88.9594 -464.701L960.232 359.491L945.983 372.971L74.7096 -451.221L88.9594 -464.701Z' fill='%23D9D9D9'/%3E%3Cpath d='M50.2814 -428.113L921.554 396.079L907.305 409.559L36.0316 -414.633L50.2814 -428.113Z' fill='%23D9D9D9'/%3E%3Cpath d='M11.6034 -391.525L882.876 432.667L868.627 446.147L-2.64642 -378.045L11.6034 -391.525Z' fill='%23D9D9D9'/%3E%3Cpath d='M-27.0746 -354.937L844.198 469.255L829.949 482.735L-41.3244 -341.457L-27.0746 -354.937Z' fill='%23D9D9D9'/%3E%3Cpath d='M-65.7526 -318.349L805.52 505.843L791.271 519.323L-80.0024 -304.869L-65.7526 -318.349Z' fill='%23D9D9D9'/%3E%3Cpath d='M-104.431 -281.761L766.842 542.431L752.593 555.911L-118.68 -268.281L-104.431 -281.761Z' fill='%23D9D9D9'/%3E%3Cpath d='M-143.109 -245.173L728.164 579.019L713.915 592.499L-157.358 -231.693L-143.109 -245.173Z' fill='%23D9D9D9'/%3E%3Cpath d='M-181.787 -208.585L689.486 615.607L675.237 629.087L-196.036 -195.105L-181.787 -208.585Z' fill='%23D9D9D9'/%3E%3Cpath d='M-220.465 -171.997L650.808 652.195L636.559 665.675L-234.714 -158.517L-220.465 -171.997Z' fill='%23D9D9D9'/%3E%3Cpath d='M-259.143 -135.409L612.13 688.783L597.881 702.263L-273.392 -121.929L-259.143 -135.409Z' fill='%23D9D9D9'/%3E%3Cpath d='M-297.821 -98.8211L573.452 725.371L559.203 738.851L-312.07 -85.3413L-297.821 -98.8211Z' fill='%23D9D9D9'/%3E%3Cpath d='M-336.499 -62.2331L534.774 761.959L520.525 775.439L-350.748 -48.7533L-336.499 -62.2331Z' fill='%23D9D9D9'/%3E%3Cpath d='M-375.177 -25.6452L496.096 798.547L481.847 812.026L-389.426 -12.1654L-375.177 -25.6452Z' fill='%23D9D9D9'/%3E%3Cpath d='M-413.855 10.9428L457.418 835.135L443.169 848.615L-428.104 24.4225L-413.855 10.9428Z' fill='%23D9D9D9'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_873_174)'%3E%3Cpath d='M511.169 254.929C511.169 396.905 396.739 512 255.584 512C114.428 512 -0.000976562 396.905 -0.000976562 254.929C-0.000976562 112.953 114.428 -2.14209 255.584 -2.14209C396.739 -2.14209 511.169 112.953 511.169 254.929Z' fill='%23E6007A'/%3E%3C/g%3E%3Cpath d='M183.804 160.44C171.052 151.144 156.458 147.168 141.864 147.168H52.2334L5.63086 367.567H122.684L132.083 322.765C141.879 321.622 151.384 318.84 160.403 314.394C161.8 313.705 163.178 312.986 164.536 312.239C168.249 321.014 173.177 329.173 179.315 336.581C187.573 346.548 197.657 354.366 209.117 360.016L209.12 360.018L209.517 360.213L209.917 360.399C222.605 366.289 236.098 368.784 249.63 368.784C263.218 368.784 276.507 366.489 289.292 361.868C295.36 359.675 301.212 357.046 306.832 353.988L303.961 367.567H421.013L430.413 322.765C440.209 321.622 449.714 318.84 458.733 314.394C469.61 309.03 479.317 301.877 487.672 293.046L487.682 293.035L487.841 292.867L487.995 292.7L487.998 292.697C496.09 283.931 502.611 273.995 507.338 262.975C512.203 251.631 514.88 239.536 514.88 226.953C514.88 214.447 512.231 202.327 507.001 190.977C501.508 178.753 493.141 168.335 482.134 160.44C469.382 151.144 454.788 147.168 440.194 147.168H350.563L345.323 171.949C338.297 165.041 330.227 159.445 321.314 155.191C308.55 148.958 294.91 146.357 281.28 146.357C267.691 146.357 254.402 148.652 241.618 153.273C229.473 157.663 218.174 163.78 207.814 171.541C205.377 173.358 203.011 175.252 200.715 177.221C196.062 170.823 190.408 165.177 183.804 160.44Z' fill='%232B0532'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M229.569 331.163L229.632 331.194L229.696 331.224C237.592 334.889 246.407 336.65 256.019 336.65C265.979 336.65 275.609 334.978 284.877 331.628C294.055 328.31 302.528 323.689 310.277 317.771C317.967 311.898 324.68 305.15 330.397 297.531L330.413 297.509L330.429 297.487C336.137 289.737 340.585 281.451 343.748 272.638C347.074 263.611 348.753 254.388 348.753 244.999C348.753 236.541 347.45 228.398 344.825 220.6C342.311 212.598 338.452 205.423 333.24 199.132C327.963 192.763 321.484 187.769 313.875 184.16C306.078 180.325 297.295 178.491 287.67 178.491C277.709 178.491 268.079 180.164 258.812 183.513C249.647 186.826 241.125 191.437 233.261 197.332C225.542 203.084 218.751 209.844 212.899 217.597L212.889 217.611C207.16 225.246 202.642 233.54 199.345 242.478L199.33 242.518L199.316 242.559C196.135 251.575 194.532 260.778 194.532 270.142C194.532 278.624 195.843 286.844 198.472 294.776L198.477 294.792L198.482 294.808C201.135 302.663 205.057 309.744 210.248 316.009C215.517 322.37 221.981 327.422 229.569 331.163ZM287.732 276.015L287.742 276C290.74 271.657 292.939 267.045 294.364 262.148C295.831 257.108 296.54 252.276 296.54 247.635C296.54 243.3 295.843 239.458 294.528 236.043L294.487 235.934L294.449 235.824C293.328 232.545 291.583 230.131 289.252 228.349C287.226 226.801 284.199 225.736 279.606 225.736C274.271 225.736 269.81 226.979 266.027 229.305L265.974 229.337L265.921 229.368C261.869 231.746 258.495 234.905 255.765 238.909C252.893 243.123 250.673 247.698 249.104 252.654C247.65 257.67 246.947 262.48 246.947 267.101C246.947 271.459 247.585 275.422 248.809 279.033C250.055 282.339 251.89 284.885 254.293 286.836C256.34 288.379 259.301 289.405 263.68 289.405C269.175 289.405 273.697 288.15 277.46 285.837C281.567 283.313 284.975 280.058 287.722 276.029L287.732 276.015Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M171.542 186.53C164.879 181.64 157.017 179.302 148.254 179.302H84.4909L51.4775 335.434H103.163L112.436 291.23H128.094C136.792 291.23 145.029 289.337 152.73 285.54C160.225 281.844 166.856 276.954 172.594 270.888L172.619 270.861L172.644 270.834C178.339 264.665 182.824 257.798 186.062 250.247C189.323 242.645 190.992 234.866 190.992 226.953C190.992 219.052 189.327 211.496 186.009 204.348C182.762 197.062 177.927 191.083 171.542 186.53ZM135.575 242.131L135.598 242.101C136.363 241.1 137.06 239.71 137.567 237.773L137.604 237.631L137.648 237.49C138.222 235.663 138.577 233.269 138.577 230.197C138.577 227.864 138.266 226.771 138.111 226.428C138.024 226.235 137.948 226.093 137.889 225.994L137.862 225.949L137.849 225.939H126.349L122.444 244.594H132.693C132.756 244.581 132.999 244.516 133.45 244.244C133.961 243.935 134.685 243.325 135.553 242.16L135.575 242.131Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M469.872 186.53C463.209 181.64 455.347 179.302 446.583 179.302H382.821L349.807 335.434H401.493L410.766 291.23H426.424C435.122 291.23 443.359 289.337 451.06 285.54C458.555 281.844 465.186 276.954 470.923 270.888L470.949 270.861L470.973 270.834C476.669 264.665 481.153 257.798 484.392 250.247C487.652 242.645 489.322 234.866 489.322 226.953C489.322 219.052 487.657 211.496 484.339 204.348C481.092 197.062 476.257 191.083 469.872 186.53ZM433.905 242.131L433.928 242.101C434.693 241.1 435.39 239.71 435.896 237.773L435.934 237.631L435.978 237.49C436.552 235.663 436.907 233.269 436.907 230.197C436.907 227.864 436.596 226.771 436.441 226.428C436.354 226.235 436.278 226.093 436.219 225.994L436.192 225.949L436.178 225.939H424.678L420.774 244.594H431.023C431.086 244.581 431.329 244.516 431.779 244.244C432.29 243.935 433.014 243.325 433.883 242.16L433.905 242.131Z' fill='%23E6007A'/%3E%3Cmask id='path-6-outside-1_873_174' maskUnits='userSpaceOnUse' x='35.8232' y='166.788' width='435' height='156' fill='black'%3E%3Crect fill='white' x='35.8232' y='166.788' width='435' height='156'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z'/%3E%3C/mask%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' stroke='white' stroke-width='0.3' mask='url(%23path-6-outside-1_873_174)'/%3E%3C/g%3E%3Cdefs%3E%3CradialGradient id='paint0_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(-62.8 -86.4) rotate(70.436) scale(686.854 394.059)'%3E%3Cstop stop-color='%236335EC'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='paint1_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(640.4 677.6) rotate(-116.448) scale(1055.25 662.094)'%3E%3Cstop stop-color='%23E6007A'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='clip0_873_174'%3E%3Crect width='512' height='512' rx='256' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A" /> Pop CLI Signing Portal - - + From 56ce3af6673d2e9f62c7739e5f23663f055ef525 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 28 Feb 2025 14:30:43 +0100 Subject: [PATCH 060/117] test: fix format --- crates/pop-cli/src/commands/up/parachain.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index f2d30dd57..30fda1449 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -246,7 +246,9 @@ mod tests { id: Some(2000), genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), - proxied_address: Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()), + proxied_address: Some( + "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), + ), ..Default::default() } .prepare_chain_for_registration(&mut cli) @@ -297,7 +299,8 @@ mod tests { &mut cli, ) .await?; - let proxied_address = UpChainCommand::default().resolve_proxied_address(&chain, &mut cli)?; + let proxied_address = + UpChainCommand::default().resolve_proxied_address(&chain, &mut cli)?; assert_eq!( proxied_address, ProxyConfig::Address( @@ -310,13 +313,16 @@ mod tests { "Would you like to use a proxy for registration? This is considered a best practice.", false, ); - let proxied_address = UpChainCommand::default().resolve_proxied_address(&chain, &mut cli)?; + let proxied_address = + UpChainCommand::default().resolve_proxied_address(&chain, &mut cli)?; assert_eq!(proxied_address, ProxyConfig::None); cli.verify()?; cli = MockCli::new(); let proxied_address = UpChainCommand { - proxied_address: Some("Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string()), + proxied_address: Some( + "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string(), + ), ..Default::default() } .resolve_proxied_address(&chain, &mut cli)?; From 849282027ff3e36bec217ac96add09704041ee30 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Mon, 3 Mar 2025 10:16:22 +0100 Subject: [PATCH 061/117] fix: remove onboard.rs empty file (#433) --- crates/pop-parachains/src/utils/onboard.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 crates/pop-parachains/src/utils/onboard.rs diff --git a/crates/pop-parachains/src/utils/onboard.rs b/crates/pop-parachains/src/utils/onboard.rs deleted file mode 100644 index e69de29bb..000000000 From 14e8146c1081d529210eedc41c477e8d9f506455 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 9 Feb 2025 11:50:01 +0100 Subject: [PATCH 062/117] test: detects_project_type_correctly --- crates/pop-cli/src/commands/up/mod.rs | 33 ++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 877618ba1..d16bd52e3 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -71,7 +71,13 @@ impl Command { cmd.execute().await?; return Ok("contract"); } - cli.warning("No contract detected. Ensure you are in a valid project directory.")?; + if pop_parachains::is_supported(project_path.as_deref())? { + cli.warning("Parachain deployment is currently not implemented.")?; + return Ok("parachain"); + } + cli.warning( + "No contract or parachain detected. Ensure you are in a valid project directory.", + )?; Ok("") } } @@ -83,6 +89,7 @@ mod tests { use cli::MockCli; use duct::cmd; use pop_contracts::{mock_build_process, new_environment}; + use pop_parachains::{instantiate_template_dir, Config, Parachain}; use std::env; use url::Url; @@ -126,6 +133,25 @@ mod tests { cli.verify() } + #[tokio::test] + async fn detects_parachain_correctly() -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let name = "parachain"; + let project_path = temp_dir.path().join(name); + let config = Config { + symbol: "DOT".to_string(), + decimals: 18, + initial_endowment: "1000000".to_string(), + }; + instantiate_template_dir(&Parachain::Standard, &project_path, None, config)?; + + let args = create_up_args(project_path)?; + let mut cli = + MockCli::new().expect_warning("Parachain deployment is currently not implemented."); + assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); + cli.verify() + } + #[tokio::test] async fn detects_rust_project_correctly() -> anyhow::Result<()> { let temp_dir = tempfile::tempdir()?; @@ -135,8 +161,9 @@ mod tests { let args = create_up_args(project_path)?; cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; - let mut cli = MockCli::new() - .expect_warning("No contract detected. Ensure you are in a valid project directory."); + let mut cli = MockCli::new().expect_warning( + "No contract or parachain detected. Ensure you are in a valid project directory.", + ); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, ""); cli.verify() } From 79fdb42b5983333a75a79d2379f1c56ba9805ea1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 9 Feb 2025 12:14:28 +0100 Subject: [PATCH 063/117] feat: parachain deployment in pop up --- crates/pop-cli/src/commands/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index bdf09c4ca..0ce460f55 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -55,9 +55,9 @@ fn about_build() -> &'static str { /// Help message for the up command. fn about_up() -> &'static str { #[cfg(all(feature = "parachain", feature = "contract"))] - return "Deploy a smart contract or launch a local network."; + return "Deploy a parachain, deploy a smart contract or launch a local network."; #[cfg(all(feature = "parachain", not(feature = "contract")))] - return "Launch a local network."; + return "Deploy a parachain or launch a local network."; #[cfg(all(feature = "contract", not(feature = "parachain")))] return "Deploy a smart contract."; } From a78463f2e2d5df17162898698495a38d2f0194fd Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 9 Feb 2025 19:21:13 +0100 Subject: [PATCH 064/117] fix: parachain feature --- crates/pop-cli/src/commands/up/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index d16bd52e3..5d409065a 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -71,6 +71,7 @@ impl Command { cmd.execute().await?; return Ok("contract"); } + #[cfg(feature = "parachain")] if pop_parachains::is_supported(project_path.as_deref())? { cli.warning("Parachain deployment is currently not implemented.")?; return Ok("parachain"); From a3c5b349e908785010d5d4a5b2ff8afca1cd3185 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 17:40:48 +0100 Subject: [PATCH 065/117] docs: improve comments --- crates/pop-cli/src/commands/mod.rs | 2 +- crates/pop-cli/src/commands/up/contract.rs | 5 +++-- crates/pop-cli/src/commands/up/network.rs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 0ce460f55..0f597183c 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -52,7 +52,7 @@ fn about_build() -> &'static str { return "Build a smart contract or Rust package."; } -/// Help message for the up command. +/// Help message for the `up` command. fn about_up() -> &'static str { #[cfg(all(feature = "parachain", feature = "contract"))] return "Deploy a parachain, deploy a smart contract or launch a local network."; diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 3cc3f146c..d4807404e 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -87,7 +87,8 @@ pub struct UpContractCommand { /// confirmation. #[clap(short = 'y', long)] pub(crate) skip_confirm: bool, - // Deprecation flag, used to specify whether the deprecation warning is shown. + // Deprecation flag, used to specify whether the deprecation warning is shown (will be removed + // in v0.8.0). #[clap(skip)] pub(crate) valid: bool, } @@ -98,7 +99,7 @@ impl UpContractCommand { Cli.intro("Deploy a smart contract")?; // Show warning if specified as deprecated. if !self.valid { - Cli.warning("DEPRECATION: Please use `pop up` (or simply `pop u`) in future...")?; + Cli.warning("DEPRECATION: Please use `pop up` (or simply `pop u`) in the future...")?; } // Check if build exists in the specified "Contract build directory" if !has_contract_been_built(self.path.as_deref()) { diff --git a/crates/pop-cli/src/commands/up/network.rs b/crates/pop-cli/src/commands/up/network.rs index 8d29c0a12..f45d5f45c 100644 --- a/crates/pop-cli/src/commands/up/network.rs +++ b/crates/pop-cli/src/commands/up/network.rs @@ -48,7 +48,8 @@ pub(crate) struct ZombienetCommand { /// Automatically source all needed binaries required without prompting for confirmation. #[clap(short = 'y', long)] skip_confirm: bool, - // Deprecation flag, used to specify whether the deprecation warning is shown. + // Deprecation flag, used to specify whether the deprecation warning is shown (will be removed + // in v0.8.0). #[clap(skip)] pub(crate) valid: bool, } @@ -63,7 +64,7 @@ impl ZombienetCommand { // Show warning if specified as deprecated. if !self.valid { log::warning( - "DEPRECATION: Please use `pop up network` (or simply `pop up n`) in future...", + "DEPRECATION: Please use `pop up network` (or simply `pop up n`) in the future...", )?; } From 708cd42bbbc97c7ff6f34b64c001cae635ae8017 Mon Sep 17 00:00:00 2001 From: Alex Bean Date: Thu, 27 Feb 2025 09:39:11 +0100 Subject: [PATCH 066/117] feat: replace index.html with summary costs in ui (#430) --- crates/pop-cli/src/assets/index.html | 200 +++++++++++++-------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/crates/pop-cli/src/assets/index.html b/crates/pop-cli/src/assets/index.html index 2b4430f8b..628cb6127 100644 --- a/crates/pop-cli/src/assets/index.html +++ b/crates/pop-cli/src/assets/index.html @@ -7,7 +7,7 @@ href="data:image/svg+xml,%3Csvg width='512' height='512' viewBox='0 0 512 512' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_873_174)'%3E%3Crect width='512' height='512' rx='256' fill='%231C0533'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint0_radial_873_174)' fill-opacity='0.8'/%3E%3Crect width='512' height='512' rx='256' fill='url(%23paint1_radial_873_174)' fill-opacity='0.6'/%3E%3Cmask id='mask0_873_174' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='-429' y='-502' width='1428' height='1351'%3E%3Cpath d='M127.637 -501.289L998.911 322.903L984.661 336.383L113.388 -487.809L127.637 -501.289Z' fill='%23D9D9D9'/%3E%3Cpath d='M88.9594 -464.701L960.232 359.491L945.983 372.971L74.7096 -451.221L88.9594 -464.701Z' fill='%23D9D9D9'/%3E%3Cpath d='M50.2814 -428.113L921.554 396.079L907.305 409.559L36.0316 -414.633L50.2814 -428.113Z' fill='%23D9D9D9'/%3E%3Cpath d='M11.6034 -391.525L882.876 432.667L868.627 446.147L-2.64642 -378.045L11.6034 -391.525Z' fill='%23D9D9D9'/%3E%3Cpath d='M-27.0746 -354.937L844.198 469.255L829.949 482.735L-41.3244 -341.457L-27.0746 -354.937Z' fill='%23D9D9D9'/%3E%3Cpath d='M-65.7526 -318.349L805.52 505.843L791.271 519.323L-80.0024 -304.869L-65.7526 -318.349Z' fill='%23D9D9D9'/%3E%3Cpath d='M-104.431 -281.761L766.842 542.431L752.593 555.911L-118.68 -268.281L-104.431 -281.761Z' fill='%23D9D9D9'/%3E%3Cpath d='M-143.109 -245.173L728.164 579.019L713.915 592.499L-157.358 -231.693L-143.109 -245.173Z' fill='%23D9D9D9'/%3E%3Cpath d='M-181.787 -208.585L689.486 615.607L675.237 629.087L-196.036 -195.105L-181.787 -208.585Z' fill='%23D9D9D9'/%3E%3Cpath d='M-220.465 -171.997L650.808 652.195L636.559 665.675L-234.714 -158.517L-220.465 -171.997Z' fill='%23D9D9D9'/%3E%3Cpath d='M-259.143 -135.409L612.13 688.783L597.881 702.263L-273.392 -121.929L-259.143 -135.409Z' fill='%23D9D9D9'/%3E%3Cpath d='M-297.821 -98.8211L573.452 725.371L559.203 738.851L-312.07 -85.3413L-297.821 -98.8211Z' fill='%23D9D9D9'/%3E%3Cpath d='M-336.499 -62.2331L534.774 761.959L520.525 775.439L-350.748 -48.7533L-336.499 -62.2331Z' fill='%23D9D9D9'/%3E%3Cpath d='M-375.177 -25.6452L496.096 798.547L481.847 812.026L-389.426 -12.1654L-375.177 -25.6452Z' fill='%23D9D9D9'/%3E%3Cpath d='M-413.855 10.9428L457.418 835.135L443.169 848.615L-428.104 24.4225L-413.855 10.9428Z' fill='%23D9D9D9'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_873_174)'%3E%3Cpath d='M511.169 254.929C511.169 396.905 396.739 512 255.584 512C114.428 512 -0.000976562 396.905 -0.000976562 254.929C-0.000976562 112.953 114.428 -2.14209 255.584 -2.14209C396.739 -2.14209 511.169 112.953 511.169 254.929Z' fill='%23E6007A'/%3E%3C/g%3E%3Cpath d='M183.804 160.44C171.052 151.144 156.458 147.168 141.864 147.168H52.2334L5.63086 367.567H122.684L132.083 322.765C141.879 321.622 151.384 318.84 160.403 314.394C161.8 313.705 163.178 312.986 164.536 312.239C168.249 321.014 173.177 329.173 179.315 336.581C187.573 346.548 197.657 354.366 209.117 360.016L209.12 360.018L209.517 360.213L209.917 360.399C222.605 366.289 236.098 368.784 249.63 368.784C263.218 368.784 276.507 366.489 289.292 361.868C295.36 359.675 301.212 357.046 306.832 353.988L303.961 367.567H421.013L430.413 322.765C440.209 321.622 449.714 318.84 458.733 314.394C469.61 309.03 479.317 301.877 487.672 293.046L487.682 293.035L487.841 292.867L487.995 292.7L487.998 292.697C496.09 283.931 502.611 273.995 507.338 262.975C512.203 251.631 514.88 239.536 514.88 226.953C514.88 214.447 512.231 202.327 507.001 190.977C501.508 178.753 493.141 168.335 482.134 160.44C469.382 151.144 454.788 147.168 440.194 147.168H350.563L345.323 171.949C338.297 165.041 330.227 159.445 321.314 155.191C308.55 148.958 294.91 146.357 281.28 146.357C267.691 146.357 254.402 148.652 241.618 153.273C229.473 157.663 218.174 163.78 207.814 171.541C205.377 173.358 203.011 175.252 200.715 177.221C196.062 170.823 190.408 165.177 183.804 160.44Z' fill='%232B0532'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M229.569 331.163L229.632 331.194L229.696 331.224C237.592 334.889 246.407 336.65 256.019 336.65C265.979 336.65 275.609 334.978 284.877 331.628C294.055 328.31 302.528 323.689 310.277 317.771C317.967 311.898 324.68 305.15 330.397 297.531L330.413 297.509L330.429 297.487C336.137 289.737 340.585 281.451 343.748 272.638C347.074 263.611 348.753 254.388 348.753 244.999C348.753 236.541 347.45 228.398 344.825 220.6C342.311 212.598 338.452 205.423 333.24 199.132C327.963 192.763 321.484 187.769 313.875 184.16C306.078 180.325 297.295 178.491 287.67 178.491C277.709 178.491 268.079 180.164 258.812 183.513C249.647 186.826 241.125 191.437 233.261 197.332C225.542 203.084 218.751 209.844 212.899 217.597L212.889 217.611C207.16 225.246 202.642 233.54 199.345 242.478L199.33 242.518L199.316 242.559C196.135 251.575 194.532 260.778 194.532 270.142C194.532 278.624 195.843 286.844 198.472 294.776L198.477 294.792L198.482 294.808C201.135 302.663 205.057 309.744 210.248 316.009C215.517 322.37 221.981 327.422 229.569 331.163ZM287.732 276.015L287.742 276C290.74 271.657 292.939 267.045 294.364 262.148C295.831 257.108 296.54 252.276 296.54 247.635C296.54 243.3 295.843 239.458 294.528 236.043L294.487 235.934L294.449 235.824C293.328 232.545 291.583 230.131 289.252 228.349C287.226 226.801 284.199 225.736 279.606 225.736C274.271 225.736 269.81 226.979 266.027 229.305L265.974 229.337L265.921 229.368C261.869 231.746 258.495 234.905 255.765 238.909C252.893 243.123 250.673 247.698 249.104 252.654C247.65 257.67 246.947 262.48 246.947 267.101C246.947 271.459 247.585 275.422 248.809 279.033C250.055 282.339 251.89 284.885 254.293 286.836C256.34 288.379 259.301 289.405 263.68 289.405C269.175 289.405 273.697 288.15 277.46 285.837C281.567 283.313 284.975 280.058 287.722 276.029L287.732 276.015Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M171.542 186.53C164.879 181.64 157.017 179.302 148.254 179.302H84.4909L51.4775 335.434H103.163L112.436 291.23H128.094C136.792 291.23 145.029 289.337 152.73 285.54C160.225 281.844 166.856 276.954 172.594 270.888L172.619 270.861L172.644 270.834C178.339 264.665 182.824 257.798 186.062 250.247C189.323 242.645 190.992 234.866 190.992 226.953C190.992 219.052 189.327 211.496 186.009 204.348C182.762 197.062 177.927 191.083 171.542 186.53ZM135.575 242.131L135.598 242.101C136.363 241.1 137.06 239.71 137.567 237.773L137.604 237.631L137.648 237.49C138.222 235.663 138.577 233.269 138.577 230.197C138.577 227.864 138.266 226.771 138.111 226.428C138.024 226.235 137.948 226.093 137.889 225.994L137.862 225.949L137.849 225.939H126.349L122.444 244.594H132.693C132.756 244.581 132.999 244.516 133.45 244.244C133.961 243.935 134.685 243.325 135.553 242.16L135.575 242.131Z' fill='%23E6007A'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M469.872 186.53C463.209 181.64 455.347 179.302 446.583 179.302H382.821L349.807 335.434H401.493L410.766 291.23H426.424C435.122 291.23 443.359 289.337 451.06 285.54C458.555 281.844 465.186 276.954 470.923 270.888L470.949 270.861L470.973 270.834C476.669 264.665 481.153 257.798 484.392 250.247C487.652 242.645 489.322 234.866 489.322 226.953C489.322 219.052 487.657 211.496 484.339 204.348C481.092 197.062 476.257 191.083 469.872 186.53ZM433.905 242.131L433.928 242.101C434.693 241.1 435.39 239.71 435.896 237.773L435.934 237.631L435.978 237.49C436.552 235.663 436.907 233.269 436.907 230.197C436.907 227.864 436.596 226.771 436.441 226.428C436.354 226.235 436.278 226.093 436.219 225.994L436.192 225.949L436.178 225.939H424.678L420.774 244.594H431.023C431.086 244.581 431.329 244.516 431.779 244.244C432.29 243.935 433.014 243.325 433.883 242.16L433.905 242.131Z' fill='%23E6007A'/%3E%3Cmask id='path-6-outside-1_873_174' maskUnits='userSpaceOnUse' x='35.8232' y='166.788' width='435' height='156' fill='black'%3E%3Crect fill='white' x='35.8232' y='166.788' width='435' height='156'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z'/%3E%3C/mask%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M68.9791 168.599H131.11C139.505 168.599 146.942 170.833 153.22 175.445C159.314 179.787 163.927 185.486 167.032 192.461C170.229 199.344 171.832 206.612 171.832 214.222C171.832 221.841 170.226 229.347 167.067 236.713C163.922 244.045 159.566 250.719 154.022 256.724L154.006 256.742L153.989 256.759C148.417 262.651 141.98 267.398 134.699 270.989C127.28 274.646 119.347 276.472 110.95 276.472H93.6573L84.3839 320.675H36.8232L68.9791 168.599ZM90.3873 272.416H110.95C118.745 272.416 126.07 270.727 132.924 267.347C139.778 263.968 145.826 259.507 151.068 253.964C156.309 248.287 160.408 242.001 163.365 235.107C166.322 228.213 167.8 221.251 167.8 214.222C167.8 207.193 166.322 200.501 163.365 194.148C160.543 187.795 156.376 182.658 150.866 178.738C145.356 174.682 138.77 172.655 131.11 172.655H72.2437L41.8028 316.62H81.1139L90.3873 272.416ZM120.037 230.626L120.052 230.607C121.01 229.354 121.81 227.706 122.372 225.559L122.397 225.463L122.427 225.37C123.078 223.296 123.449 220.685 123.449 217.466C123.449 215.01 123.13 213.583 122.802 212.859C122.362 211.884 122.041 211.655 122.042 211.653L121.876 211.558L121.71 211.439C121.399 211.215 121.2 211.18 121.03 211.18H107.569L102.815 233.89H115.587C115.879 233.89 116.444 233.794 117.343 233.252C118.131 232.776 119.04 231.963 120.022 230.646L120.037 230.626ZM97.8464 237.946L104.297 207.125H121.03C122.105 207.125 123.113 207.463 124.054 208.139C124.995 208.68 125.801 209.693 126.473 211.18C127.145 212.667 127.481 214.763 127.481 217.466C127.481 220.981 127.078 224.022 126.271 226.591C125.599 229.159 124.591 231.322 123.247 233.079C122.038 234.701 120.761 235.918 119.417 236.729C118.073 237.54 116.796 237.946 115.587 237.946H97.8464ZM213.354 316.632L213.312 316.611C205.969 312.991 199.733 308.113 194.652 301.981C189.624 295.911 185.822 289.05 183.247 281.426L183.24 281.404C180.681 273.683 179.404 265.679 179.404 257.411C179.404 248.285 180.966 239.311 184.072 230.507L184.081 230.479L184.091 230.452C187.319 221.7 191.743 213.579 197.354 206.101L197.361 206.092C203.099 198.491 209.753 191.867 217.318 186.229C225.025 180.452 233.372 175.936 242.35 172.691C251.395 169.421 260.794 167.788 270.526 167.788C279.899 167.788 288.376 169.574 295.858 173.257C303.215 176.744 309.461 181.561 314.547 187.699C319.59 193.786 323.327 200.732 325.764 208.5C328.321 216.086 329.593 224.016 329.593 232.268C329.593 241.412 327.958 250.4 324.71 259.212C321.616 267.835 317.262 275.95 311.665 283.549L311.655 283.564L311.644 283.578C306.04 291.046 299.46 297.663 291.914 303.425C284.329 309.218 276.037 313.741 267.051 316.989C258.006 320.259 248.606 321.892 238.875 321.892C229.51 321.892 220.99 320.177 213.397 316.652L213.354 316.632ZM320.924 257.817C324.016 249.436 325.561 240.92 325.561 232.268C325.561 224.428 324.352 216.926 321.932 209.761C319.648 202.461 316.153 195.973 311.449 190.295C306.746 184.618 300.966 180.157 294.112 176.913C287.258 173.533 279.396 171.843 270.526 171.843C261.252 171.843 252.315 173.398 243.713 176.507C235.112 179.616 227.115 183.942 219.723 189.484C212.466 194.891 206.082 201.245 200.572 208.544C195.196 215.709 190.962 223.482 187.871 231.863C184.915 240.244 183.436 248.76 183.436 257.411C183.436 265.252 184.646 272.822 187.065 280.121C189.484 287.286 193.046 293.707 197.75 299.384C202.453 305.062 208.233 309.59 215.087 312.97C222.075 316.214 230.005 317.836 238.875 317.836C248.148 317.836 257.086 316.282 265.687 313.173C274.289 310.064 282.218 305.738 289.475 300.196C296.733 294.653 303.05 288.3 308.425 281.135C313.801 273.836 317.968 266.063 320.924 257.817ZM272.24 264.445L272.254 264.426C275.373 259.908 277.667 255.099 279.155 249.987C280.67 244.779 281.412 239.756 281.412 234.904C281.412 230.347 280.678 226.254 279.264 222.58L279.237 222.508L279.212 222.434C277.972 218.806 276.002 216.048 273.327 214.004C270.857 212.115 267.361 210.978 262.462 210.978C256.8 210.978 251.967 212.302 247.832 214.844L247.796 214.865L247.761 214.886C243.447 217.417 239.854 220.785 236.959 225.032C233.968 229.418 231.66 234.18 230.031 239.332C228.525 244.523 227.787 249.532 227.787 254.37C227.787 258.944 228.459 263.14 229.768 266.989C231.136 270.636 233.189 273.504 235.911 275.706C238.394 277.588 241.822 278.702 246.536 278.702C252.349 278.702 257.246 277.37 261.367 274.836C265.718 272.162 269.334 268.708 272.24 264.445ZM233.432 278.905C230.072 276.201 227.586 272.687 225.973 268.361C224.495 264.035 223.755 259.372 223.755 254.37C223.755 249.098 224.562 243.691 226.175 238.149C227.922 232.606 230.408 227.469 233.634 222.738C236.859 218.007 240.891 214.222 245.729 211.383C250.568 208.409 256.145 206.922 262.462 206.922C267.972 206.922 272.407 208.206 275.767 210.775C279.127 213.343 281.546 216.79 283.024 221.116C284.637 225.307 285.444 229.903 285.444 234.904C285.444 240.176 284.637 245.583 283.024 251.126C281.412 256.668 278.925 261.872 275.565 266.739C272.34 271.47 268.308 275.323 263.47 278.297C258.631 281.271 252.987 282.757 246.536 282.757C241.16 282.757 236.792 281.473 233.432 278.905ZM367.309 168.599H429.439C437.835 168.599 445.272 170.833 451.55 175.445C457.644 179.787 462.256 185.485 465.361 192.46C468.559 199.344 470.162 206.611 470.162 214.222C470.162 221.841 468.556 229.347 465.397 236.713C462.252 244.045 457.896 250.719 452.352 256.724L452.335 256.742L452.319 256.759C446.747 262.651 440.31 267.398 433.028 270.989C425.61 274.646 417.677 276.472 409.28 276.472H391.987L382.714 320.675H335.153L367.309 168.599ZM388.717 272.416H409.28C417.075 272.416 424.4 270.727 431.254 267.347C438.108 263.968 444.156 259.507 449.397 253.964C454.639 248.287 458.738 242.001 461.695 235.107C464.651 228.213 466.13 221.251 466.13 214.222C466.13 207.193 464.651 200.501 461.695 194.148C458.872 187.795 454.706 182.658 449.196 178.738C443.686 174.682 437.1 172.655 429.439 172.655H370.574L340.133 316.62H379.444L388.717 272.416ZM418.366 230.626L418.381 230.607C419.34 229.354 420.14 227.706 420.702 225.559L420.727 225.463L420.756 225.37C421.408 223.296 421.779 220.685 421.779 217.466C421.779 215.01 421.46 213.583 421.132 212.859C420.692 211.884 420.371 211.655 420.372 211.653L420.206 211.558L420.04 211.439C419.729 211.215 419.53 211.18 419.36 211.18H405.899L401.145 233.89H413.917C414.209 233.89 414.774 233.794 415.673 233.252C416.461 232.776 417.369 231.963 418.352 230.646L418.366 230.626ZM396.176 237.946L402.627 207.125H419.36C420.435 207.125 421.443 207.463 422.384 208.139C423.324 208.68 424.131 209.693 424.803 211.18C425.475 212.667 425.811 214.763 425.811 217.466C425.811 220.981 425.408 224.022 424.601 226.591C423.929 229.159 422.921 231.322 421.577 233.079C420.368 234.701 419.091 235.918 417.747 236.729C416.403 237.54 415.126 237.946 413.917 237.946H396.176Z' stroke='white' stroke-width='0.3' mask='url(%23path-6-outside-1_873_174)'/%3E%3C/g%3E%3Cdefs%3E%3CradialGradient id='paint0_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(-62.8 -86.4) rotate(70.436) scale(686.854 394.059)'%3E%3Cstop stop-color='%236335EC'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='paint1_radial_873_174' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(640.4 677.6) rotate(-116.448) scale(1055.25 662.094)'%3E%3Cstop stop-color='%23E6007A'/%3E%3Cstop offset='1' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='clip0_873_174'%3E%3Crect width='512' height='512' rx='256' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A" /> Pop CLI Signing Portal - - + `],K);pj=function(e,t,n,r){var o,i=arguments.length,s=i<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(i<3?o(s):i>3?o(t,n,s):o(t,n))||s);return i>3&&s&&Object.defineProperty(t,n,s),s}([TA("dc-connection-button")],pj);const hj={chains:{},wallets:[new class extends YE{constructor(e){super(),X(this,"options"),this.options=e}async getWallets(){return await lS,(()=>{const{injectedWeb3:e}=window;return e?Object.keys(e):[]})().map((e=>new aS(e,this.options)))}}]};function vj(e,t,{checkForDefaultPrevented:n=!0}={}){return function(r){if(null==e||e(r),!1===n||!r.defaultPrevented)return null==t?void 0:t(r)}}function mj(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function gj(...e){return t=>{let n=!1;const r=e.map((e=>{const r=mj(e,t);return n||"function"!=typeof r||(n=!0),r}));if(n)return()=>{for(let t=0;t{const t=n.map((e=>qe.createContext(e)));return function(n){const r=(null==n?void 0:n[e])||t;return qe.useMemo((()=>({[`__scope${e}`]:{...n,[e]:r}})),[n,r])}};return r.scopeName=e,[function(t,r){const o=qe.createContext(r),i=n.length;n=[...n,r];const s=t=>{var n;const{scope:r,children:s,...a}=t,l=(null==(n=null==r?void 0:r[e])?void 0:n[i])||o,c=qe.useMemo((()=>a),Object.values(a));return nt.jsx(l.Provider,{value:c,children:s})};return s.displayName=t+"Provider",[s,function(n,s){var a;const l=(null==(a=null==s?void 0:s[e])?void 0:a[i])||o,c=qe.useContext(l);if(c)return c;if(void 0!==r)return r;throw new Error(`\`${n}\` must be used within \`${t}\``)}]},wj(r,...t)]}function wj(...e){const t=e[0];if(1===e.length)return t;const n=()=>{const n=e.map((e=>({useScope:e(),scopeName:e.scopeName})));return function(e){const r=n.reduce(((t,{useScope:n,scopeName:r})=>({...t,...n(e)[`__scope${r}`]})),{});return qe.useMemo((()=>({[`__scope${t.scopeName}`]:r})),[r])}};return n.scopeName=t.scopeName,n}function xj(e){const t=qe.useRef(e);return qe.useEffect((()=>{t.current=e})),qe.useMemo((()=>(...e)=>{var n;return null==(n=t.current)?void 0:n.call(t,...e)}),[])}function kj({prop:e,defaultProp:t,onChange:n=()=>{}}){const[r,o]=function({defaultProp:e,onChange:t}){const n=qe.useState(e),[r]=n,o=qe.useRef(r),i=xj(t);return qe.useEffect((()=>{o.current!==r&&(i(r),o.current=r)}),[r,o,i]),n}({defaultProp:t,onChange:n}),i=void 0!==e,s=i?e:r,a=xj(n);return[s,qe.useCallback((t=>{if(i){const n="function"==typeof t?t(e):t;n!==e&&a(n)}else o(t)}),[i,e,o,a])]}!function(e){NA.next(e.wallets)}({wallets:hj.wallets});var Ej=qe.forwardRef(((e,t)=>{const{children:n,...r}=e,o=qe.Children.toArray(n),i=o.find(_j);if(i){const e=i.props.children,n=o.map((t=>t===i?qe.Children.count(e)>1?qe.Children.only(null):qe.isValidElement(e)?e.props.children:null:t));return nt.jsx(Sj,{...r,ref:t,children:qe.isValidElement(e)?qe.cloneElement(e,void 0,n):null})}return nt.jsx(Sj,{...r,ref:t,children:n})}));Ej.displayName="Slot";var Sj=qe.forwardRef(((e,t)=>{const{children:n,...r}=e;if(qe.isValidElement(n)){const e=function(e){var t,n;let r=null==(t=Object.getOwnPropertyDescriptor(e.props,"ref"))?void 0:t.get,o=r&&"isReactWarning"in r&&r.isReactWarning;if(o)return e.ref;if(r=null==(n=Object.getOwnPropertyDescriptor(e,"ref"))?void 0:n.get,o=r&&"isReactWarning"in r&&r.isReactWarning,o)return e.props.ref;return e.props.ref||e.ref}(n);return qe.cloneElement(n,{...Aj(r,n.props),ref:t?gj(t,e):e})}return qe.Children.count(n)>1?qe.Children.only(null):null}));Sj.displayName="SlotClone";var Cj=({children:e})=>nt.jsx(nt.Fragment,{children:e});function _j(e){return qe.isValidElement(e)&&e.type===Cj}function Aj(e,t){const n={...t};for(const r in t){const o=e[r],i=t[r];/^on[A-Z]/.test(r)?o&&i?n[r]=(...e)=>{i(...e),o(...e)}:o&&(n[r]=o):"style"===r?n[r]={...o,...i}:"className"===r&&(n[r]=[o,i].filter(Boolean).join(" "))}return{...e,...n}}var Nj=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","span","svg","ul"].reduce(((e,t)=>{const n=qe.forwardRef(((e,n)=>{const{asChild:r,...o}=e,i=r?Ej:t;return"undefined"!=typeof window&&(window[Symbol.for("radix-ui")]=!0),nt.jsx(i,{...o,ref:n})}));return n.displayName=`Primitive.${t}`,{...e,[t]:n}}),{});function Pj(e,t){e&&df.flushSync((()=>e.dispatchEvent(t)))}function jj(e){const t=e+"CollectionProvider",[n,r]=bj(t),[o,i]=n(t,{collectionRef:{current:null},itemMap:new Map}),s=e=>{const{scope:t,children:n}=e,r=Ke.useRef(null),i=Ke.useRef(new Map).current;return nt.jsx(o,{scope:t,itemMap:i,collectionRef:r,children:n})};s.displayName=t;const a=e+"CollectionSlot",l=Ke.forwardRef(((e,t)=>{const{scope:n,children:r}=e,o=yj(t,i(a,n).collectionRef);return nt.jsx(Ej,{ref:o,children:r})}));l.displayName=a;const c=e+"CollectionItemSlot",u="data-radix-collection-item",d=Ke.forwardRef(((e,t)=>{const{scope:n,children:r,...o}=e,s=Ke.useRef(null),a=yj(t,s),l=i(c,n);return Ke.useEffect((()=>(l.itemMap.set(s,{ref:s,...o}),()=>{l.itemMap.delete(s)}))),nt.jsx(Ej,{[u]:"",ref:a,children:r})}));return d.displayName=c,[{Provider:s,Slot:l,ItemSlot:d},function(t){const n=i(e+"CollectionConsumer",t),r=Ke.useCallback((()=>{const e=n.collectionRef.current;if(!e)return[];const t=Array.from(e.querySelectorAll(`[${u}]`)),r=Array.from(n.itemMap.values()).sort(((e,n)=>t.indexOf(e.ref.current)-t.indexOf(n.ref.current)));return r}),[n.collectionRef,n.itemMap]);return r},r]}var $j=qe.createContext(void 0);function Oj(e){const t=qe.useContext($j);return e||t||"ltr"}var Rj,Mj="dismissableLayer.update",Tj="dismissableLayer.pointerDownOutside",Lj="dismissableLayer.focusOutside",Ij=qe.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),zj=qe.forwardRef(((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:o,onFocusOutside:i,onInteractOutside:s,onDismiss:a,...l}=e,c=qe.useContext(Ij),[u,d]=qe.useState(null),f=(null==u?void 0:u.ownerDocument)??(null==globalThis?void 0:globalThis.document),[,p]=qe.useState({}),h=yj(t,(e=>d(e))),v=Array.from(c.layers),[m]=[...c.layersWithOutsidePointerEventsDisabled].slice(-1),g=v.indexOf(m),y=u?v.indexOf(u):-1,b=c.layersWithOutsidePointerEventsDisabled.size>0,w=y>=g,x=function(e,t=(null==globalThis?void 0:globalThis.document)){const n=xj(e),r=qe.useRef(!1),o=qe.useRef((()=>{}));return qe.useEffect((()=>{const e=e=>{if(e.target&&!r.current){let r=function(){Bj(Tj,n,i,{discrete:!0})};const i={originalEvent:e};"touch"===e.pointerType?(t.removeEventListener("click",o.current),o.current=r,t.addEventListener("click",o.current,{once:!0})):r()}else t.removeEventListener("click",o.current);r.current=!1},i=window.setTimeout((()=>{t.addEventListener("pointerdown",e)}),0);return()=>{window.clearTimeout(i),t.removeEventListener("pointerdown",e),t.removeEventListener("click",o.current)}}),[t,n]),{onPointerDownCapture:()=>r.current=!0}}((e=>{const t=e.target,n=[...c.branches].some((e=>e.contains(t)));w&&!n&&(null==o||o(e),null==s||s(e),e.defaultPrevented||null==a||a())}),f),k=function(e,t=(null==globalThis?void 0:globalThis.document)){const n=xj(e),r=qe.useRef(!1);return qe.useEffect((()=>{const e=e=>{if(e.target&&!r.current){Bj(Lj,n,{originalEvent:e},{discrete:!1})}};return t.addEventListener("focusin",e),()=>t.removeEventListener("focusin",e)}),[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}((e=>{const t=e.target;[...c.branches].some((e=>e.contains(t)))||(null==i||i(e),null==s||s(e),e.defaultPrevented||null==a||a())}),f);return function(e,t=(null==globalThis?void 0:globalThis.document)){const n=xj(e);qe.useEffect((()=>{const e=e=>{"Escape"===e.key&&n(e)};return t.addEventListener("keydown",e,{capture:!0}),()=>t.removeEventListener("keydown",e,{capture:!0})}),[n,t])}((e=>{y===c.layers.size-1&&(null==r||r(e),!e.defaultPrevented&&a&&(e.preventDefault(),a()))}),f),qe.useEffect((()=>{if(u)return n&&(0===c.layersWithOutsidePointerEventsDisabled.size&&(Rj=f.body.style.pointerEvents,f.body.style.pointerEvents="none"),c.layersWithOutsidePointerEventsDisabled.add(u)),c.layers.add(u),Dj(),()=>{n&&1===c.layersWithOutsidePointerEventsDisabled.size&&(f.body.style.pointerEvents=Rj)}}),[u,f,n,c]),qe.useEffect((()=>()=>{u&&(c.layers.delete(u),c.layersWithOutsidePointerEventsDisabled.delete(u),Dj())}),[u,c]),qe.useEffect((()=>{const e=()=>p({});return document.addEventListener(Mj,e),()=>document.removeEventListener(Mj,e)}),[]),nt.jsx(Nj.div,{...l,ref:h,style:{pointerEvents:b?w?"auto":"none":void 0,...e.style},onFocusCapture:vj(e.onFocusCapture,k.onFocusCapture),onBlurCapture:vj(e.onBlurCapture,k.onBlurCapture),onPointerDownCapture:vj(e.onPointerDownCapture,x.onPointerDownCapture)})}));zj.displayName="DismissableLayer";function Dj(){const e=new CustomEvent(Mj);document.dispatchEvent(e)}function Bj(e,t,n,{discrete:r}){const o=n.originalEvent.target,i=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&o.addEventListener(e,t,{once:!0}),r?Pj(o,i):o.dispatchEvent(i)}qe.forwardRef(((e,t)=>{const n=qe.useContext(Ij),r=qe.useRef(null),o=yj(t,r);return qe.useEffect((()=>{const e=r.current;if(e)return n.branches.add(e),()=>{n.branches.delete(e)}}),[n.branches]),nt.jsx(Nj.div,{...e,ref:o})})).displayName="DismissableLayerBranch";var Uj=0;function Fj(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var Hj="focusScope.autoFocusOnMount",Vj="focusScope.autoFocusOnUnmount",Wj={bubbles:!1,cancelable:!0},qj=qe.forwardRef(((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:i,...s}=e,[a,l]=qe.useState(null),c=xj(o),u=xj(i),d=qe.useRef(null),f=yj(t,(e=>l(e))),p=qe.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;qe.useEffect((()=>{if(r){let e=function(e){if(p.paused||!a)return;const t=e.target;a.contains(t)?d.current=t:Gj(d.current,{select:!0})},t=function(e){if(p.paused||!a)return;const t=e.relatedTarget;null!==t&&(a.contains(t)||Gj(d.current,{select:!0}))},n=function(e){if(document.activeElement===document.body)for(const t of e)t.removedNodes.length>0&&Gj(a)};document.addEventListener("focusin",e),document.addEventListener("focusout",t);const r=new MutationObserver(n);return a&&r.observe(a,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",e),document.removeEventListener("focusout",t),r.disconnect()}}}),[r,a,p.paused]),qe.useEffect((()=>{if(a){Jj.add(p);const t=document.activeElement;if(!a.contains(t)){const n=new CustomEvent(Hj,Wj);a.addEventListener(Hj,c),a.dispatchEvent(n),n.defaultPrevented||(!function(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(Gj(r,{select:t}),document.activeElement!==n)return}((e=Kj(a),e.filter((e=>"A"!==e.tagName))),{select:!0}),document.activeElement===t&&Gj(a))}return()=>{a.removeEventListener(Hj,c),setTimeout((()=>{const e=new CustomEvent(Vj,Wj);a.addEventListener(Vj,u),a.dispatchEvent(e),e.defaultPrevented||Gj(t??document.body,{select:!0}),a.removeEventListener(Vj,u),Jj.remove(p)}),0)}}var e}),[a,c,u,p]);const h=qe.useCallback((e=>{if(!n&&!r)return;if(p.paused)return;const t="Tab"===e.key&&!e.altKey&&!e.ctrlKey&&!e.metaKey,o=document.activeElement;if(t&&o){const t=e.currentTarget,[r,i]=function(e){const t=Kj(e),n=Yj(t,e),r=Yj(t.reverse(),e);return[n,r]}(t);r&&i?e.shiftKey||o!==i?e.shiftKey&&o===r&&(e.preventDefault(),n&&Gj(i,{select:!0})):(e.preventDefault(),n&&Gj(r,{select:!0})):o===t&&e.preventDefault()}}),[n,r,p.paused]);return nt.jsx(Nj.div,{tabIndex:-1,...s,ref:f,onKeyDown:h})}));function Kj(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:e=>{const t="INPUT"===e.tagName&&"hidden"===e.type;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function Yj(e,t){for(const n of e)if(!Qj(n,{upTo:t}))return n}function Qj(e,{upTo:t}){if("hidden"===getComputedStyle(e).visibility)return!0;for(;e;){if(void 0!==t&&e===t)return!1;if("none"===getComputedStyle(e).display)return!0;e=e.parentElement}return!1}function Gj(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&function(e){return e instanceof HTMLInputElement&&"select"in e}(e)&&t&&e.select()}}qj.displayName="FocusScope";var Jj=function(){let e=[];return{add(t){const n=e[0];t!==n&&(null==n||n.pause()),e=Xj(e,t),e.unshift(t)},remove(t){var n;e=Xj(e,t),null==(n=e[0])||n.resume()}}}();function Xj(e,t){const n=[...e],r=n.indexOf(t);return-1!==r&&n.splice(r,1),n}var Zj=Boolean(null==globalThis?void 0:globalThis.document)?qe.useLayoutEffect:()=>{},e$=Ye["useId".toString()]||(()=>{}),t$=0;function n$(e){const[t,n]=qe.useState(e$());return Zj((()=>{n((e=>e??String(t$++)))}),[e]),t?`radix-${t}`:""}const r$=["top","right","bottom","left"],o$=Math.min,i$=Math.max,s$=Math.round,a$=Math.floor,l$=e=>({x:e,y:e}),c$={left:"right",right:"left",bottom:"top",top:"bottom"},u$={start:"end",end:"start"};function d$(e,t,n){return i$(e,o$(t,n))}function f$(e,t){return"function"==typeof e?e(t):e}function p$(e){return e.split("-")[0]}function h$(e){return e.split("-")[1]}function v$(e){return"x"===e?"y":"x"}function m$(e){return"y"===e?"height":"width"}function g$(e){return["top","bottom"].includes(p$(e))?"y":"x"}function y$(e){return v$(g$(e))}function b$(e){return e.replace(/start|end/g,(e=>u$[e]))}function w$(e){return e.replace(/left|right|bottom|top/g,(e=>c$[e]))}function x$(e){return"number"!=typeof e?function(e){return{top:0,right:0,bottom:0,left:0,...e}}(e):{top:e,right:e,bottom:e,left:e}}function k$(e){const{x:t,y:n,width:r,height:o}=e;return{width:r,height:o,top:n,left:t,right:t+r,bottom:n+o,x:t,y:n}}function E$(e,t,n){let{reference:r,floating:o}=e;const i=g$(t),s=y$(t),a=m$(s),l=p$(t),c="y"===i,u=r.x+r.width/2-o.width/2,d=r.y+r.height/2-o.height/2,f=r[a]/2-o[a]/2;let p;switch(l){case"top":p={x:u,y:r.y-o.height};break;case"bottom":p={x:u,y:r.y+r.height};break;case"right":p={x:r.x+r.width,y:d};break;case"left":p={x:r.x-o.width,y:d};break;default:p={x:r.x,y:r.y}}switch(h$(t)){case"start":p[s]-=f*(n&&c?-1:1);break;case"end":p[s]+=f*(n&&c?-1:1)}return p}async function S$(e,t){var n;void 0===t&&(t={});const{x:r,y:o,platform:i,rects:s,elements:a,strategy:l}=e,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:d="floating",altBoundary:f=!1,padding:p=0}=f$(t,e),h=x$(p),v=a[f?"floating"===d?"reference":"floating":d],m=k$(await i.getClippingRect({element:null==(n=await(null==i.isElement?void 0:i.isElement(v)))||n?v:v.contextElement||await(null==i.getDocumentElement?void 0:i.getDocumentElement(a.floating)),boundary:c,rootBoundary:u,strategy:l})),g="floating"===d?{x:r,y:o,width:s.floating.width,height:s.floating.height}:s.reference,y=await(null==i.getOffsetParent?void 0:i.getOffsetParent(a.floating)),b=await(null==i.isElement?void 0:i.isElement(y))&&await(null==i.getScale?void 0:i.getScale(y))||{x:1,y:1},w=k$(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:a,rect:g,offsetParent:y,strategy:l}):g);return{top:(m.top-w.top+h.top)/b.y,bottom:(w.bottom-m.bottom+h.bottom)/b.y,left:(m.left-w.left+h.left)/b.x,right:(w.right-m.right+h.right)/b.x}}function C$(e,t){return{top:e.top-t.height,right:e.right-t.width,bottom:e.bottom-t.height,left:e.left-t.width}}function _$(e){return r$.some((t=>e[t]>=0))}function A$(){return"undefined"!=typeof window}function N$(e){return $$(e)?(e.nodeName||"").toLowerCase():"#document"}function P$(e){var t;return(null==e||null==(t=e.ownerDocument)?void 0:t.defaultView)||window}function j$(e){var t;return null==(t=($$(e)?e.ownerDocument:e.document)||window.document)?void 0:t.documentElement}function $$(e){return!!A$()&&(e instanceof Node||e instanceof P$(e).Node)}function O$(e){return!!A$()&&(e instanceof Element||e instanceof P$(e).Element)}function R$(e){return!!A$()&&(e instanceof HTMLElement||e instanceof P$(e).HTMLElement)}function M$(e){return!(!A$()||"undefined"==typeof ShadowRoot)&&(e instanceof ShadowRoot||e instanceof P$(e).ShadowRoot)}function T$(e){const{overflow:t,overflowX:n,overflowY:r,display:o}=U$(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&!["inline","contents"].includes(o)}function L$(e){return["table","td","th"].includes(N$(e))}function I$(e){return[":popover-open",":modal"].some((t=>{try{return e.matches(t)}catch(n){return!1}}))}function z$(e){const t=D$(),n=O$(e)?U$(e):e;return"none"!==n.transform||"none"!==n.perspective||!!n.containerType&&"normal"!==n.containerType||!t&&!!n.backdropFilter&&"none"!==n.backdropFilter||!t&&!!n.filter&&"none"!==n.filter||["transform","perspective","filter"].some((e=>(n.willChange||"").includes(e)))||["paint","layout","strict","content"].some((e=>(n.contain||"").includes(e)))}function D$(){return!("undefined"==typeof CSS||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function B$(e){return["html","body","#document"].includes(N$(e))}function U$(e){return P$(e).getComputedStyle(e)}function F$(e){return O$(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function H$(e){if("html"===N$(e))return e;const t=e.assignedSlot||e.parentNode||M$(e)&&e.host||j$(e);return M$(t)?t.host:t}function V$(e){const t=H$(e);return B$(t)?e.ownerDocument?e.ownerDocument.body:e.body:R$(t)&&T$(t)?t:V$(t)}function W$(e,t,n){var r;void 0===t&&(t=[]),void 0===n&&(n=!0);const o=V$(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),s=P$(o);if(i){const e=q$(s);return t.concat(s,s.visualViewport||[],T$(o)?o:[],e&&n?W$(e):[])}return t.concat(o,W$(o,[],n))}function q$(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function K$(e){const t=U$(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const o=R$(e),i=o?e.offsetWidth:n,s=o?e.offsetHeight:r,a=s$(n)!==i||s$(r)!==s;return a&&(n=i,r=s),{width:n,height:r,$:a}}function Y$(e){return O$(e)?e:e.contextElement}function Q$(e){const t=Y$(e);if(!R$(t))return l$(1);const n=t.getBoundingClientRect(),{width:r,height:o,$:i}=K$(t);let s=(i?s$(n.width):n.width)/r,a=(i?s$(n.height):n.height)/o;return s&&Number.isFinite(s)||(s=1),a&&Number.isFinite(a)||(a=1),{x:s,y:a}}const G$=l$(0);function J$(e){const t=P$(e);return D$()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:G$}function X$(e,t,n,r){void 0===t&&(t=!1),void 0===n&&(n=!1);const o=e.getBoundingClientRect(),i=Y$(e);let s=l$(1);t&&(r?O$(r)&&(s=Q$(r)):s=Q$(e));const a=function(e,t,n){return void 0===t&&(t=!1),!(!n||t&&n!==P$(e))&&t}(i,n,r)?J$(i):l$(0);let l=(o.left+a.x)/s.x,c=(o.top+a.y)/s.y,u=o.width/s.x,d=o.height/s.y;if(i){const e=P$(i),t=r&&O$(r)?P$(r):r;let n=e,o=q$(n);for(;o&&r&&t!==n;){const e=Q$(o),t=o.getBoundingClientRect(),r=U$(o),i=t.left+(o.clientLeft+parseFloat(r.paddingLeft))*e.x,s=t.top+(o.clientTop+parseFloat(r.paddingTop))*e.y;l*=e.x,c*=e.y,u*=e.x,d*=e.y,l+=i,c+=s,n=P$(o),o=q$(n)}}return k$({width:u,height:d,x:l,y:c})}function Z$(e,t){const n=F$(e).scrollLeft;return t?t.left+n:X$(j$(e)).left+n}function eO(e,t,n){void 0===n&&(n=!1);const r=e.getBoundingClientRect();return{x:r.left+t.scrollLeft-(n?0:Z$(e,r)),y:r.top+t.scrollTop}}function tO(e,t,n){let r;if("viewport"===t)r=function(e,t){const n=P$(e),r=j$(e),o=n.visualViewport;let i=r.clientWidth,s=r.clientHeight,a=0,l=0;if(o){i=o.width,s=o.height;const e=D$();(!e||e&&"fixed"===t)&&(a=o.offsetLeft,l=o.offsetTop)}return{width:i,height:s,x:a,y:l}}(e,n);else if("document"===t)r=function(e){const t=j$(e),n=F$(e),r=e.ownerDocument.body,o=i$(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),i=i$(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight);let s=-n.scrollLeft+Z$(e);const a=-n.scrollTop;return"rtl"===U$(r).direction&&(s+=i$(t.clientWidth,r.clientWidth)-o),{width:o,height:i,x:s,y:a}}(j$(e));else if(O$(t))r=function(e,t){const n=X$(e,!0,"fixed"===t),r=n.top+e.clientTop,o=n.left+e.clientLeft,i=R$(e)?Q$(e):l$(1);return{width:e.clientWidth*i.x,height:e.clientHeight*i.y,x:o*i.x,y:r*i.y}}(t,n);else{const n=J$(e);r={x:t.x-n.x,y:t.y-n.y,width:t.width,height:t.height}}return k$(r)}function nO(e,t){const n=H$(e);return!(n===t||!O$(n)||B$(n))&&("fixed"===U$(n).position||nO(n,t))}function rO(e,t,n){const r=R$(t),o=j$(t),i="fixed"===n,s=X$(e,!0,i,t);let a={scrollLeft:0,scrollTop:0};const l=l$(0);if(r||!r&&!i)if(("body"!==N$(t)||T$(o))&&(a=F$(t)),r){const e=X$(t,!0,i,t);l.x=e.x+t.clientLeft,l.y=e.y+t.clientTop}else o&&(l.x=Z$(o));const c=!o||r||i?l$(0):eO(o,a);return{x:s.left+a.scrollLeft-l.x-c.x,y:s.top+a.scrollTop-l.y-c.y,width:s.width,height:s.height}}function oO(e){return"static"===U$(e).position}function iO(e,t){if(!R$(e)||"fixed"===U$(e).position)return null;if(t)return t(e);let n=e.offsetParent;return j$(e)===n&&(n=n.ownerDocument.body),n}function sO(e,t){const n=P$(e);if(I$(e))return n;if(!R$(e)){let t=H$(e);for(;t&&!B$(t);){if(O$(t)&&!oO(t))return t;t=H$(t)}return n}let r=iO(e,t);for(;r&&L$(r)&&oO(r);)r=iO(r,t);return r&&B$(r)&&oO(r)&&!z$(r)?n:r||function(e){let t=H$(e);for(;R$(t)&&!B$(t);){if(z$(t))return t;if(I$(t))return null;t=H$(t)}return null}(e)||n}const aO={convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{elements:t,rect:n,offsetParent:r,strategy:o}=e;const i="fixed"===o,s=j$(r),a=!!t&&I$(t.floating);if(r===s||a&&i)return n;let l={scrollLeft:0,scrollTop:0},c=l$(1);const u=l$(0),d=R$(r);if((d||!d&&!i)&&(("body"!==N$(r)||T$(s))&&(l=F$(r)),R$(r))){const e=X$(r);c=Q$(r),u.x=e.x+r.clientLeft,u.y=e.y+r.clientTop}const f=!s||d||i?l$(0):eO(s,l,!0);return{width:n.width*c.x,height:n.height*c.y,x:n.x*c.x-l.scrollLeft*c.x+u.x+f.x,y:n.y*c.y-l.scrollTop*c.y+u.y+f.y}},getDocumentElement:j$,getClippingRect:function(e){let{element:t,boundary:n,rootBoundary:r,strategy:o}=e;const i=[..."clippingAncestors"===n?I$(t)?[]:function(e,t){const n=t.get(e);if(n)return n;let r=W$(e,[],!1).filter((e=>O$(e)&&"body"!==N$(e))),o=null;const i="fixed"===U$(e).position;let s=i?H$(e):e;for(;O$(s)&&!B$(s);){const t=U$(s),n=z$(s);n||"fixed"!==t.position||(o=null),(i?!n&&!o:!n&&"static"===t.position&&o&&["absolute","fixed"].includes(o.position)||T$(s)&&!n&&nO(e,s))?r=r.filter((e=>e!==s)):o=t,s=H$(s)}return t.set(e,r),r}(t,this._c):[].concat(n),r],s=i[0],a=i.reduce(((e,n)=>{const r=tO(t,n,o);return e.top=i$(r.top,e.top),e.right=o$(r.right,e.right),e.bottom=o$(r.bottom,e.bottom),e.left=i$(r.left,e.left),e}),tO(t,s,o));return{width:a.right-a.left,height:a.bottom-a.top,x:a.left,y:a.top}},getOffsetParent:sO,getElementRects:async function(e){const t=this.getOffsetParent||sO,n=this.getDimensions,r=await n(e.floating);return{reference:rO(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}},getClientRects:function(e){return Array.from(e.getClientRects())},getDimensions:function(e){const{width:t,height:n}=K$(e);return{width:t,height:n}},getScale:Q$,isElement:O$,isRTL:function(e){return"rtl"===U$(e).direction}};function lO(e,t,n,r){void 0===r&&(r={});const{ancestorScroll:o=!0,ancestorResize:i=!0,elementResize:s="function"==typeof ResizeObserver,layoutShift:a="function"==typeof IntersectionObserver,animationFrame:l=!1}=r,c=Y$(e),u=o||i?[...c?W$(c):[],...W$(t)]:[];u.forEach((e=>{o&&e.addEventListener("scroll",n,{passive:!0}),i&&e.addEventListener("resize",n)}));const d=c&&a?function(e,t){let n,r=null;const o=j$(e);function i(){var e;clearTimeout(n),null==(e=r)||e.disconnect(),r=null}return function s(a,l){void 0===a&&(a=!1),void 0===l&&(l=1),i();const{left:c,top:u,width:d,height:f}=e.getBoundingClientRect();if(a||t(),!d||!f)return;const p={rootMargin:-a$(u)+"px "+-a$(o.clientWidth-(c+d))+"px "+-a$(o.clientHeight-(u+f))+"px "+-a$(c)+"px",threshold:i$(0,o$(1,l))||1};let h=!0;function v(e){const t=e[0].intersectionRatio;if(t!==l){if(!h)return s();t?s(!1,t):n=setTimeout((()=>{s(!1,1e-7)}),1e3)}h=!1}try{r=new IntersectionObserver(v,{...p,root:o.ownerDocument})}catch(m){r=new IntersectionObserver(v,p)}r.observe(e)}(!0),i}(c,n):null;let f,p=-1,h=null;s&&(h=new ResizeObserver((e=>{let[r]=e;r&&r.target===c&&h&&(h.unobserve(t),cancelAnimationFrame(p),p=requestAnimationFrame((()=>{var e;null==(e=h)||e.observe(t)}))),n()})),c&&!l&&h.observe(c),h.observe(t));let v=l?X$(e):null;return l&&function t(){const r=X$(e);!v||r.x===v.x&&r.y===v.y&&r.width===v.width&&r.height===v.height||n();v=r,f=requestAnimationFrame(t)}(),n(),()=>{var e;u.forEach((e=>{o&&e.removeEventListener("scroll",n),i&&e.removeEventListener("resize",n)})),null==d||d(),null==(e=h)||e.disconnect(),h=null,l&&cancelAnimationFrame(f)}}const cO=function(e){return void 0===e&&(e=0),{name:"offset",options:e,async fn(t){var n,r;const{x:o,y:i,placement:s,middlewareData:a}=t,l=await async function(e,t){const{placement:n,platform:r,elements:o}=e,i=await(null==r.isRTL?void 0:r.isRTL(o.floating)),s=p$(n),a=h$(n),l="y"===g$(n),c=["left","top"].includes(s)?-1:1,u=i&&l?-1:1,d=f$(t,e);let{mainAxis:f,crossAxis:p,alignmentAxis:h}="number"==typeof d?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return a&&"number"==typeof h&&(p="end"===a?-1*h:h),l?{x:p*u,y:f*c}:{x:f*c,y:p*u}}(t,e);return s===(null==(n=a.offset)?void 0:n.placement)&&null!=(r=a.arrow)&&r.alignmentOffset?{}:{x:o+l.x,y:i+l.y,data:{...l,placement:s}}}}},uO=function(e){return void 0===e&&(e={}),{name:"shift",options:e,async fn(t){const{x:n,y:r,placement:o}=t,{mainAxis:i=!0,crossAxis:s=!1,limiter:a={fn:e=>{let{x:t,y:n}=e;return{x:t,y:n}}},...l}=f$(e,t),c={x:n,y:r},u=await S$(t,l),d=g$(p$(o)),f=v$(d);let p=c[f],h=c[d];if(i){const e="y"===f?"bottom":"right";p=d$(p+u["y"===f?"top":"left"],p,p-u[e])}if(s){const e="y"===d?"bottom":"right";h=d$(h+u["y"===d?"top":"left"],h,h-u[e])}const v=a.fn({...t,[f]:p,[d]:h});return{...v,data:{x:v.x-n,y:v.y-r,enabled:{[f]:i,[d]:s}}}}}},dO=function(e){return void 0===e&&(e={}),{name:"flip",options:e,async fn(t){var n,r;const{placement:o,middlewareData:i,rects:s,initialPlacement:a,platform:l,elements:c}=t,{mainAxis:u=!0,crossAxis:d=!0,fallbackPlacements:f,fallbackStrategy:p="bestFit",fallbackAxisSideDirection:h="none",flipAlignment:v=!0,...m}=f$(e,t);if(null!=(n=i.arrow)&&n.alignmentOffset)return{};const g=p$(o),y=g$(a),b=p$(a)===a,w=await(null==l.isRTL?void 0:l.isRTL(c.floating)),x=f||(b||!v?[w$(a)]:function(e){const t=w$(e);return[b$(e),t,b$(t)]}(a)),k="none"!==h;!f&&k&&x.push(...function(e,t,n,r){const o=h$(e);let i=function(e,t,n){const r=["left","right"],o=["right","left"],i=["top","bottom"],s=["bottom","top"];switch(e){case"top":case"bottom":return n?t?o:r:t?r:o;case"left":case"right":return t?i:s;default:return[]}}(p$(e),"start"===n,r);return o&&(i=i.map((e=>e+"-"+o)),t&&(i=i.concat(i.map(b$)))),i}(a,v,h,w));const E=[a,...x],S=await S$(t,m),C=[];let _=(null==(r=i.flip)?void 0:r.overflows)||[];if(u&&C.push(S[g]),d){const e=function(e,t,n){void 0===n&&(n=!1);const r=h$(e),o=y$(e),i=m$(o);let s="x"===o?r===(n?"end":"start")?"right":"left":"start"===r?"bottom":"top";return t.reference[i]>t.floating[i]&&(s=w$(s)),[s,w$(s)]}(o,s,w);C.push(S[e[0]],S[e[1]])}if(_=[..._,{placement:o,overflows:C}],!C.every((e=>e<=0))){var A,N;const e=((null==(A=i.flip)?void 0:A.index)||0)+1,t=E[e];if(t)return{data:{index:e,overflows:_},reset:{placement:t}};let n=null==(N=_.filter((e=>e.overflows[0]<=0)).sort(((e,t)=>e.overflows[1]-t.overflows[1]))[0])?void 0:N.placement;if(!n)switch(p){case"bestFit":{var P;const e=null==(P=_.filter((e=>{if(k){const t=g$(e.placement);return t===y||"y"===t}return!0})).map((e=>[e.placement,e.overflows.filter((e=>e>0)).reduce(((e,t)=>e+t),0)])).sort(((e,t)=>e[1]-t[1]))[0])?void 0:P[0];e&&(n=e);break}case"initialPlacement":n=a}if(o!==n)return{reset:{placement:n}}}return{}}}},fO=function(e){return void 0===e&&(e={}),{name:"size",options:e,async fn(t){var n,r;const{placement:o,rects:i,platform:s,elements:a}=t,{apply:l=()=>{},...c}=f$(e,t),u=await S$(t,c),d=p$(o),f=h$(o),p="y"===g$(o),{width:h,height:v}=i.floating;let m,g;"top"===d||"bottom"===d?(m=d,g=f===(await(null==s.isRTL?void 0:s.isRTL(a.floating))?"start":"end")?"left":"right"):(g=d,m="end"===f?"top":"bottom");const y=v-u.top-u.bottom,b=h-u.left-u.right,w=o$(v-u[m],y),x=o$(h-u[g],b),k=!t.middlewareData.shift;let E=w,S=x;if(null!=(n=t.middlewareData.shift)&&n.enabled.x&&(S=b),null!=(r=t.middlewareData.shift)&&r.enabled.y&&(E=y),k&&!f){const e=i$(u.left,0),t=i$(u.right,0),n=i$(u.top,0),r=i$(u.bottom,0);p?S=h-2*(0!==e||0!==t?e+t:i$(u.left,u.right)):E=v-2*(0!==n||0!==r?n+r:i$(u.top,u.bottom))}await l({...t,availableWidth:S,availableHeight:E});const C=await s.getDimensions(a.floating);return h!==C.width||v!==C.height?{reset:{rects:!0}}:{}}}},pO=function(e){return void 0===e&&(e={}),{name:"hide",options:e,async fn(t){const{rects:n}=t,{strategy:r="referenceHidden",...o}=f$(e,t);switch(r){case"referenceHidden":{const e=C$(await S$(t,{...o,elementContext:"reference"}),n.reference);return{data:{referenceHiddenOffsets:e,referenceHidden:_$(e)}}}case"escaped":{const e=C$(await S$(t,{...o,altBoundary:!0}),n.floating);return{data:{escapedOffsets:e,escaped:_$(e)}}}default:return{}}}}},hO=e=>({name:"arrow",options:e,async fn(t){const{x:n,y:r,placement:o,rects:i,platform:s,elements:a,middlewareData:l}=t,{element:c,padding:u=0}=f$(e,t)||{};if(null==c)return{};const d=x$(u),f={x:n,y:r},p=y$(o),h=m$(p),v=await s.getDimensions(c),m="y"===p,g=m?"top":"left",y=m?"bottom":"right",b=m?"clientHeight":"clientWidth",w=i.reference[h]+i.reference[p]-f[p]-i.floating[h],x=f[p]-i.reference[p],k=await(null==s.getOffsetParent?void 0:s.getOffsetParent(c));let E=k?k[b]:0;E&&await(null==s.isElement?void 0:s.isElement(k))||(E=a.floating[b]||i.floating[h]);const S=w/2-x/2,C=E/2-v[h]/2-1,_=o$(d[g],C),A=o$(d[y],C),N=_,P=E-v[h]-A,j=E/2-v[h]/2+S,$=d$(N,j,P),O=!l.arrow&&null!=h$(o)&&j!==$&&i.reference[h]/2-(jn&&(p=n)}if(c){var g,y;const e="y"===f?"width":"height",t=["top","left"].includes(p$(o)),n=i.reference[d]-i.floating[e]+(t&&(null==(g=s.offset)?void 0:g[d])||0)+(t?0:m.crossAxis),r=i.reference[d]+i.reference[e]+(t?0:(null==(y=s.offset)?void 0:y[d])||0)-(t?m.crossAxis:0);hr&&(h=r)}return{[f]:p,[d]:h}}}},mO=(e,t,n)=>{const r=new Map,o={platform:aO,...n},i={...o.platform,_c:r};return(async(e,t,n)=>{const{placement:r="bottom",strategy:o="absolute",middleware:i=[],platform:s}=n,a=i.filter(Boolean),l=await(null==s.isRTL?void 0:s.isRTL(t));let c=await s.getElementRects({reference:e,floating:t,strategy:o}),{x:u,y:d}=E$(c,r,l),f=r,p={},h=0;for(let v=0;v{t.current=e})),t}const kO=e=>({name:"arrow",options:e,fn(t){const{element:n,padding:r}="function"==typeof e?e(t):e;return n&&(o=n,{}.hasOwnProperty.call(o,"current"))?null!=n.current?hO({element:n.current,padding:r}).fn(t):{}:n?hO({element:n,padding:r}).fn(t):{};var o}}),EO=(e,t)=>({...cO(e),options:[e,t]}),SO=(e,t)=>({...uO(e),options:[e,t]}),CO=(e,t)=>({...vO(e),options:[e,t]}),_O=(e,t)=>({...dO(e),options:[e,t]}),AO=(e,t)=>({...fO(e),options:[e,t]}),NO=(e,t)=>({...pO(e),options:[e,t]}),PO=(e,t)=>({...kO(e),options:[e,t]});var jO=qe.forwardRef(((e,t)=>{const{children:n,width:r=10,height:o=5,...i}=e;return nt.jsx(Nj.svg,{...i,ref:t,width:r,height:o,viewBox:"0 0 30 10",preserveAspectRatio:"none",children:e.asChild?n:nt.jsx("polygon",{points:"0,0 30,0 15,10"})})}));jO.displayName="Arrow";var $O=jO;var OO="Popper",[RO,MO]=bj(OO),[TO,LO]=RO(OO),IO=e=>{const{__scopePopper:t,children:n}=e,[r,o]=qe.useState(null);return nt.jsx(TO,{scope:t,anchor:r,onAnchorChange:o,children:n})};IO.displayName=OO;var zO="PopperAnchor",DO=qe.forwardRef(((e,t)=>{const{__scopePopper:n,virtualRef:r,...o}=e,i=LO(zO,n),s=qe.useRef(null),a=yj(t,s);return qe.useEffect((()=>{i.onAnchorChange((null==r?void 0:r.current)||s.current)})),r?null:nt.jsx(Nj.div,{...o,ref:a})}));DO.displayName=zO;var BO="PopperContent",[UO,FO]=RO(BO),HO=qe.forwardRef(((e,t)=>{var n,r,o,i,s,a;const{__scopePopper:l,side:c="bottom",sideOffset:u=0,align:d="center",alignOffset:f=0,arrowPadding:p=0,avoidCollisions:h=!0,collisionBoundary:v=[],collisionPadding:m=0,sticky:g="partial",hideWhenDetached:y=!1,updatePositionStrategy:b="optimized",onPlaced:w,...x}=e,k=LO(BO,l),[E,S]=qe.useState(null),C=yj(t,(e=>S(e))),[_,A]=qe.useState(null),N=function(e){const[t,n]=qe.useState(void 0);return Zj((()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});const t=new ResizeObserver((t=>{if(!Array.isArray(t))return;if(!t.length)return;const r=t[0];let o,i;if("borderBoxSize"in r){const e=r.borderBoxSize,t=Array.isArray(e)?e[0]:e;o=t.inlineSize,i=t.blockSize}else o=e.offsetWidth,i=e.offsetHeight;n({width:o,height:i})}));return t.observe(e,{box:"border-box"}),()=>t.unobserve(e)}n(void 0)}),[e]),t}(_),P=(null==N?void 0:N.width)??0,j=(null==N?void 0:N.height)??0,$=c+("center"!==d?"-"+d:""),O="number"==typeof m?m:{top:0,right:0,bottom:0,left:0,...m},R=Array.isArray(v)?v:[v],M=R.length>0,T={padding:O,boundary:R.filter(KO),altBoundary:M},{refs:L,floatingStyles:I,placement:z,isPositioned:D,middlewareData:B}=function(e){void 0===e&&(e={});const{placement:t="bottom",strategy:n="absolute",middleware:r=[],platform:o,elements:{reference:i,floating:s}={},transform:a=!0,whileElementsMounted:l,open:c}=e,[u,d]=qe.useState({x:0,y:0,strategy:n,placement:t,middlewareData:{},isPositioned:!1}),[f,p]=qe.useState(r);yO(f,r)||p(r);const[h,v]=qe.useState(null),[m,g]=qe.useState(null),y=qe.useCallback((e=>{e!==k.current&&(k.current=e,v(e))}),[]),b=qe.useCallback((e=>{e!==E.current&&(E.current=e,g(e))}),[]),w=i||h,x=s||m,k=qe.useRef(null),E=qe.useRef(null),S=qe.useRef(u),C=null!=l,_=xO(l),A=xO(o),N=xO(c),P=qe.useCallback((()=>{if(!k.current||!E.current)return;const e={placement:t,strategy:n,middleware:f};A.current&&(e.platform=A.current),mO(k.current,E.current,e).then((e=>{const t={...e,isPositioned:!1!==N.current};j.current&&!yO(S.current,t)&&(S.current=t,df.flushSync((()=>{d(t)})))}))}),[f,t,n,A,N]);gO((()=>{!1===c&&S.current.isPositioned&&(S.current.isPositioned=!1,d((e=>({...e,isPositioned:!1}))))}),[c]);const j=qe.useRef(!1);gO((()=>(j.current=!0,()=>{j.current=!1})),[]),gO((()=>{if(w&&(k.current=w),x&&(E.current=x),w&&x){if(_.current)return _.current(w,x,P);P()}}),[w,x,P,_,C]);const $=qe.useMemo((()=>({reference:k,floating:E,setReference:y,setFloating:b})),[y,b]),O=qe.useMemo((()=>({reference:w,floating:x})),[w,x]),R=qe.useMemo((()=>{const e={position:n,left:0,top:0};if(!O.floating)return e;const t=wO(O.floating,u.x),r=wO(O.floating,u.y);return a?{...e,transform:"translate("+t+"px, "+r+"px)",...bO(O.floating)>=1.5&&{willChange:"transform"}}:{position:n,left:t,top:r}}),[n,a,O.floating,u.x,u.y]);return qe.useMemo((()=>({...u,update:P,refs:$,elements:O,floatingStyles:R})),[u,P,$,O,R])}({strategy:"fixed",placement:$,whileElementsMounted:(...e)=>lO(...e,{animationFrame:"always"===b}),elements:{reference:k.anchor},middleware:[EO({mainAxis:u+j,alignmentAxis:f}),h&&SO({mainAxis:!0,crossAxis:!1,limiter:"partial"===g?CO():void 0,...T}),h&&_O({...T}),AO({...T,apply:({elements:e,rects:t,availableWidth:n,availableHeight:r})=>{const{width:o,height:i}=t.reference,s=e.floating.style;s.setProperty("--radix-popper-available-width",`${n}px`),s.setProperty("--radix-popper-available-height",`${r}px`),s.setProperty("--radix-popper-anchor-width",`${o}px`),s.setProperty("--radix-popper-anchor-height",`${i}px`)}}),_&&PO({element:_,padding:p}),YO({arrowWidth:P,arrowHeight:j}),y&&NO({strategy:"referenceHidden",...T})]}),[U,F]=QO(z),H=xj(w);Zj((()=>{D&&(null==H||H())}),[D,H]);const V=null==(n=B.arrow)?void 0:n.x,W=null==(r=B.arrow)?void 0:r.y,q=0!==(null==(o=B.arrow)?void 0:o.centerOffset),[K,Y]=qe.useState();return Zj((()=>{E&&Y(window.getComputedStyle(E).zIndex)}),[E]),nt.jsx("div",{ref:L.setFloating,"data-radix-popper-content-wrapper":"",style:{...I,transform:D?I.transform:"translate(0, -200%)",minWidth:"max-content",zIndex:K,"--radix-popper-transform-origin":[null==(i=B.transformOrigin)?void 0:i.x,null==(s=B.transformOrigin)?void 0:s.y].join(" "),...(null==(a=B.hide)?void 0:a.referenceHidden)&&{visibility:"hidden",pointerEvents:"none"}},dir:e.dir,children:nt.jsx(UO,{scope:l,placedSide:U,onArrowChange:A,arrowX:V,arrowY:W,shouldHideArrow:q,children:nt.jsx(Nj.div,{"data-side":U,"data-align":F,...x,ref:C,style:{...x.style,animation:D?void 0:"none"}})})})}));HO.displayName=BO;var VO="PopperArrow",WO={top:"bottom",right:"left",bottom:"top",left:"right"},qO=qe.forwardRef((function(e,t){const{__scopePopper:n,...r}=e,o=FO(VO,n),i=WO[o.placedSide];return nt.jsx("span",{ref:o.onArrowChange,style:{position:"absolute",left:o.arrowX,top:o.arrowY,[i]:0,transformOrigin:{top:"",right:"0 0",bottom:"center 0",left:"100% 0"}[o.placedSide],transform:{top:"translateY(100%)",right:"translateY(50%) rotate(90deg) translateX(-50%)",bottom:"rotate(180deg)",left:"translateY(50%) rotate(-90deg) translateX(50%)"}[o.placedSide],visibility:o.shouldHideArrow?"hidden":void 0},children:nt.jsx($O,{...r,ref:t,style:{...r.style,display:"block"}})})}));function KO(e){return null!==e}qO.displayName=VO;var YO=e=>({name:"transformOrigin",options:e,fn(t){var n,r,o;const{placement:i,rects:s,middlewareData:a}=t,l=0!==(null==(n=a.arrow)?void 0:n.centerOffset),c=l?0:e.arrowWidth,u=l?0:e.arrowHeight,[d,f]=QO(i),p={start:"0%",center:"50%",end:"100%"}[f],h=((null==(r=a.arrow)?void 0:r.x)??0)+c/2,v=((null==(o=a.arrow)?void 0:o.y)??0)+u/2;let m="",g="";return"bottom"===d?(m=l?p:`${h}px`,g=-u+"px"):"top"===d?(m=l?p:`${h}px`,g=`${s.floating.height+u}px`):"right"===d?(m=-u+"px",g=l?p:`${v}px`):"left"===d&&(m=`${s.floating.width+u}px`,g=l?p:`${v}px`),{data:{x:m,y:g}}}});function QO(e){const[t,n="center"]=e.split("-");return[t,n]}var GO=IO,JO=DO,XO=HO,ZO=qO,eR=qe.forwardRef(((e,t)=>{var n;const{container:r,...o}=e,[i,s]=qe.useState(!1);Zj((()=>s(!0)),[]);const a=r||i&&(null==(n=null==globalThis?void 0:globalThis.document)?void 0:n.body);return a?ff.createPortal(nt.jsx(Nj.div,{...o,ref:t}),a):null}));eR.displayName="Portal";var tR=e=>{const{present:t,children:n}=e,r=function(e){const[t,n]=qe.useState(),r=qe.useRef({}),o=qe.useRef(e),i=qe.useRef("none"),s=e?"mounted":"unmounted",[a,l]=function(e,t){return qe.useReducer(((e,n)=>t[e][n]??e),e)}(s,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return qe.useEffect((()=>{const e=nR(r.current);i.current="mounted"===a?e:"none"}),[a]),Zj((()=>{const t=r.current,n=o.current;if(n!==e){const r=i.current,s=nR(t);if(e)l("MOUNT");else if("none"===s||"none"===(null==t?void 0:t.display))l("UNMOUNT");else{l(n&&r!==s?"ANIMATION_OUT":"UNMOUNT")}o.current=e}}),[e,l]),Zj((()=>{if(t){let e;const n=t.ownerDocument.defaultView??window,s=i=>{const s=nR(r.current).includes(i.animationName);if(i.target===t&&s&&(l("ANIMATION_END"),!o.current)){const r=t.style.animationFillMode;t.style.animationFillMode="forwards",e=n.setTimeout((()=>{"forwards"===t.style.animationFillMode&&(t.style.animationFillMode=r)}))}},a=e=>{e.target===t&&(i.current=nR(r.current))};return t.addEventListener("animationstart",a),t.addEventListener("animationcancel",s),t.addEventListener("animationend",s),()=>{n.clearTimeout(e),t.removeEventListener("animationstart",a),t.removeEventListener("animationcancel",s),t.removeEventListener("animationend",s)}}l("ANIMATION_END")}),[t,l]),{isPresent:["mounted","unmountSuspended"].includes(a),ref:qe.useCallback((e=>{e&&(r.current=getComputedStyle(e)),n(e)}),[])}}(t),o="function"==typeof n?n({present:r.isPresent}):qe.Children.only(n),i=yj(r.ref,function(e){var t,n;let r=null==(t=Object.getOwnPropertyDescriptor(e.props,"ref"))?void 0:t.get,o=r&&"isReactWarning"in r&&r.isReactWarning;if(o)return e.ref;if(r=null==(n=Object.getOwnPropertyDescriptor(e,"ref"))?void 0:n.get,o=r&&"isReactWarning"in r&&r.isReactWarning,o)return e.props.ref;return e.props.ref||e.ref}(o));return"function"==typeof n||r.isPresent?qe.cloneElement(o,{ref:i}):null};function nR(e){return(null==e?void 0:e.animationName)||"none"}tR.displayName="Presence";var rR="rovingFocusGroup.onEntryFocus",oR={bubbles:!1,cancelable:!0},iR="RovingFocusGroup",[sR,aR,lR]=jj(iR),[cR,uR]=bj(iR,[lR]),[dR,fR]=cR(iR),pR=qe.forwardRef(((e,t)=>nt.jsx(sR.Provider,{scope:e.__scopeRovingFocusGroup,children:nt.jsx(sR.Slot,{scope:e.__scopeRovingFocusGroup,children:nt.jsx(hR,{...e,ref:t})})})));pR.displayName=iR;var hR=qe.forwardRef(((e,t)=>{const{__scopeRovingFocusGroup:n,orientation:r,loop:o=!1,dir:i,currentTabStopId:s,defaultCurrentTabStopId:a,onCurrentTabStopIdChange:l,onEntryFocus:c,preventScrollOnEntryFocus:u=!1,...d}=e,f=qe.useRef(null),p=yj(t,f),h=Oj(i),[v=null,m]=kj({prop:s,defaultProp:a,onChange:l}),[g,y]=qe.useState(!1),b=xj(c),w=aR(n),x=qe.useRef(!1),[k,E]=qe.useState(0);return qe.useEffect((()=>{const e=f.current;if(e)return e.addEventListener(rR,b),()=>e.removeEventListener(rR,b)}),[b]),nt.jsx(dR,{scope:n,orientation:r,dir:h,loop:o,currentTabStopId:v,onItemFocus:qe.useCallback((e=>m(e)),[m]),onItemShiftTab:qe.useCallback((()=>y(!0)),[]),onFocusableItemAdd:qe.useCallback((()=>E((e=>e+1))),[]),onFocusableItemRemove:qe.useCallback((()=>E((e=>e-1))),[]),children:nt.jsx(Nj.div,{tabIndex:g||0===k?-1:0,"data-orientation":r,...d,ref:p,style:{outline:"none",...e.style},onMouseDown:vj(e.onMouseDown,(()=>{x.current=!0})),onFocus:vj(e.onFocus,(e=>{const t=!x.current;if(e.target===e.currentTarget&&t&&!g){const t=new CustomEvent(rR,oR);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){const e=w().filter((e=>e.focusable));yR([e.find((e=>e.active)),e.find((e=>e.id===v)),...e].filter(Boolean).map((e=>e.ref.current)),u)}}x.current=!1})),onBlur:vj(e.onBlur,(()=>y(!1)))})})})),vR="RovingFocusGroupItem",mR=qe.forwardRef(((e,t)=>{const{__scopeRovingFocusGroup:n,focusable:r=!0,active:o=!1,tabStopId:i,...s}=e,a=n$(),l=i||a,c=fR(vR,n),u=c.currentTabStopId===l,d=aR(n),{onFocusableItemAdd:f,onFocusableItemRemove:p}=c;return qe.useEffect((()=>{if(r)return f(),()=>p()}),[r,f,p]),nt.jsx(sR.ItemSlot,{scope:n,id:l,focusable:r,active:o,children:nt.jsx(Nj.span,{tabIndex:u?0:-1,"data-orientation":c.orientation,...s,ref:t,onMouseDown:vj(e.onMouseDown,(e=>{r?c.onItemFocus(l):e.preventDefault()})),onFocus:vj(e.onFocus,(()=>c.onItemFocus(l))),onKeyDown:vj(e.onKeyDown,(e=>{if("Tab"===e.key&&e.shiftKey)return void c.onItemShiftTab();if(e.target!==e.currentTarget)return;const t=function(e,t,n){const r=function(e,t){return"rtl"!==t?e:"ArrowLeft"===e?"ArrowRight":"ArrowRight"===e?"ArrowLeft":e}(e.key,n);return"vertical"===t&&["ArrowLeft","ArrowRight"].includes(r)||"horizontal"===t&&["ArrowUp","ArrowDown"].includes(r)?void 0:gR[r]}(e,c.orientation,c.dir);if(void 0!==t){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let o=d().filter((e=>e.focusable)).map((e=>e.ref.current));if("last"===t)o.reverse();else if("prev"===t||"next"===t){"prev"===t&&o.reverse();const i=o.indexOf(e.currentTarget);o=c.loop?(r=i+1,(n=o).map(((e,t)=>n[(r+t)%n.length]))):o.slice(i+1)}setTimeout((()=>yR(o)))}var n,r}))})})}));mR.displayName=vR;var gR={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function yR(e,t=!1){const n=document.activeElement;for(const r of e){if(r===n)return;if(r.focus({preventScroll:t}),document.activeElement!==n)return}}var bR=pR,wR=mR,xR=new WeakMap,kR=new WeakMap,ER={},SR=0,CR=function(e){return e&&(e.host||CR(e.parentNode))},_R=function(e,t,n,r){var o=function(e,t){return t.map((function(t){if(e.contains(t))return t;var n=CR(t);return n&&e.contains(n)?n:(console.error("aria-hidden",t,"in not contained inside",e,". Doing nothing"),null)})).filter((function(e){return Boolean(e)}))}(t,Array.isArray(e)?e:[e]);ER[n]||(ER[n]=new WeakMap);var i=ER[n],s=[],a=new Set,l=new Set(o),c=function(e){e&&!a.has(e)&&(a.add(e),c(e.parentNode))};o.forEach(c);var u=function(e){e&&!l.has(e)&&Array.prototype.forEach.call(e.children,(function(e){if(a.has(e))u(e);else try{var t=e.getAttribute(r),o=null!==t&&"false"!==t,l=(xR.get(e)||0)+1,c=(i.get(e)||0)+1;xR.set(e,l),i.set(e,c),s.push(e),1===l&&o&&kR.set(e,!0),1===c&&e.setAttribute(n,"true"),o||e.setAttribute(r,"true")}catch(d){console.error("aria-hidden: cannot operate on ",e,d)}}))};return u(t),a.clear(),SR++,function(){s.forEach((function(e){var t=xR.get(e)-1,o=i.get(e)-1;xR.set(e,t),i.set(e,o),t||(kR.has(e)||e.removeAttribute(r),kR.delete(e)),o||e.removeAttribute(n)})),--SR||(xR=new WeakMap,xR=new WeakMap,kR=new WeakMap,ER={})}},AR=function(e,t,n){void 0===n&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=function(e){return"undefined"==typeof document?null:(Array.isArray(e)?e[0]:e).ownerDocument.body}(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live]"))),_R(r,o,n,"aria-hidden")):function(){return null}},NR="right-scroll-bar-position",PR="width-before-scroll-bar";function jR(e,t){return"function"==typeof e?e(t):e&&(e.current=t),e}var $R="undefined"!=typeof window?qe.useLayoutEffect:qe.useEffect,OR=new WeakMap;function RR(e,t){var n,r,o,i=(n=null,r=function(t){return e.forEach((function(e){return jR(e,t)}))},(o=qe.useState((function(){return{value:n,callback:r,facade:{get current(){return o.value},set current(e){var t=o.value;t!==e&&(o.value=e,o.callback(e,t))}}}}))[0]).callback=r,o.facade);return $R((function(){var t=OR.get(i);if(t){var n=new Set(t),r=new Set(e),o=i.current;n.forEach((function(e){r.has(e)||jR(e,null)})),r.forEach((function(e){n.has(e)||jR(e,o)}))}OR.set(i,e)}),[e]),i}function MR(e){return e}var TR=function(e){var t=e.sideCar,n=Yy(e,["sideCar"]);if(!t)throw new Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw new Error("Sidecar medium not found");return qe.createElement(r,Ky({},n))};TR.isSideCarExport=!0;var LR=function(e){void 0===e&&(e={});var t=function(e,t){void 0===t&&(t=MR);var n=[],r=!1;return{read:function(){if(r)throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return n.length?n[n.length-1]:e},useMedium:function(e){var o=t(e,r);return n.push(o),function(){n=n.filter((function(e){return e!==o}))}},assignSyncMedium:function(e){for(r=!0;n.length;){var t=n;n=[],t.forEach(e)}n={push:function(t){return e(t)},filter:function(){return n}}},assignMedium:function(e){r=!0;var t=[];if(n.length){var o=n;n=[],o.forEach(e),t=n}var i=function(){var n=t;t=[],n.forEach(e)},s=function(){return Promise.resolve().then(i)};s(),n={push:function(e){t.push(e),s()},filter:function(e){return t=t.filter(e),n}}}}}(null);return t.options=Ky({async:!0,ssr:!1},e),t}(),IR=function(){},zR=qe.forwardRef((function(e,t){var n=qe.useRef(null),r=qe.useState({onScrollCapture:IR,onWheelCapture:IR,onTouchMoveCapture:IR}),o=r[0],i=r[1],s=e.forwardProps,a=e.children,l=e.className,c=e.removeScrollBar,u=e.enabled,d=e.shards,f=e.sideCar,p=e.noIsolation,h=e.inert,v=e.allowPinchZoom,m=e.as,g=void 0===m?"div":m,y=e.gapMode,b=Yy(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noIsolation","inert","allowPinchZoom","as","gapMode"]),w=f,x=RR([n,t]),k=Ky(Ky({},b),o);return qe.createElement(qe.Fragment,null,u&&qe.createElement(w,{sideCar:LR,removeScrollBar:c,shards:d,noIsolation:p,inert:h,setCallbacks:i,allowPinchZoom:!!v,lockRef:n,gapMode:y}),s?qe.cloneElement(qe.Children.only(a),Ky(Ky({},k),{ref:x})):qe.createElement(g,Ky({},k,{className:l,ref:x}),a))}));zR.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},zR.classNames={fullWidth:PR,zeroRight:NR};function DR(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=function(){if("undefined"!=typeof __webpack_nonce__)return __webpack_nonce__}();return t&&e.setAttribute("nonce",t),e}var BR=function(){var e=0,t=null;return{add:function(n){var r,o;0==e&&(t=DR())&&(o=n,(r=t).styleSheet?r.styleSheet.cssText=o:r.appendChild(document.createTextNode(o)),function(e){(document.head||document.getElementsByTagName("head")[0]).appendChild(e)}(t)),e++},remove:function(){! --e&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},UR=function(){var e,t=(e=BR(),function(t,n){qe.useEffect((function(){return e.add(t),function(){e.remove()}}),[t&&n])});return function(e){var n=e.styles,r=e.dynamic;return t(n,r),null}},FR={left:0,top:0,right:0,gap:0},HR=function(e){return parseInt(e||"",10)||0},VR=function(e){if(void 0===e&&(e="margin"),"undefined"==typeof window)return FR;var t=function(e){var t=window.getComputedStyle(document.body),n=t["padding"===e?"paddingLeft":"marginLeft"],r=t["padding"===e?"paddingTop":"marginTop"],o=t["padding"===e?"paddingRight":"marginRight"];return[HR(n),HR(r),HR(o)]}(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},WR=UR(),qR="data-scroll-locked",KR=function(e,t,n,r){var o=e.left,i=e.top,s=e.right,a=e.gap;return void 0===n&&(n="margin"),"\n .".concat("with-scroll-bars-hidden"," {\n overflow: hidden ").concat(r,";\n padding-right: ").concat(a,"px ").concat(r,";\n }\n body[").concat(qR,"] {\n overflow: hidden ").concat(r,";\n overscroll-behavior: contain;\n ").concat([t&&"position: relative ".concat(r,";"),"margin"===n&&"\n padding-left: ".concat(o,"px;\n padding-top: ").concat(i,"px;\n padding-right: ").concat(s,"px;\n margin-left:0;\n margin-top:0;\n margin-right: ").concat(a,"px ").concat(r,";\n "),"padding"===n&&"padding-right: ".concat(a,"px ").concat(r,";")].filter(Boolean).join(""),"\n }\n \n .").concat(NR," {\n right: ").concat(a,"px ").concat(r,";\n }\n \n .").concat(PR," {\n margin-right: ").concat(a,"px ").concat(r,";\n }\n \n .").concat(NR," .").concat(NR," {\n right: 0 ").concat(r,";\n }\n \n .").concat(PR," .").concat(PR," {\n margin-right: 0 ").concat(r,";\n }\n \n body[").concat(qR,"] {\n ").concat("--removed-body-scroll-bar-size",": ").concat(a,"px;\n }\n")},YR=function(){var e=parseInt(document.body.getAttribute(qR)||"0",10);return isFinite(e)?e:0},QR=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,o=void 0===r?"margin":r;qe.useEffect((function(){return document.body.setAttribute(qR,(YR()+1).toString()),function(){var e=YR()-1;e<=0?document.body.removeAttribute(qR):document.body.setAttribute(qR,e.toString())}}),[]);var i=qe.useMemo((function(){return VR(o)}),[o]);return qe.createElement(WR,{styles:KR(i,!t,o,n?"":"!important")})},GR=!1;if("undefined"!=typeof window)try{var JR=Object.defineProperty({},"passive",{get:function(){return GR=!0,!0}});window.addEventListener("test",JR,JR),window.removeEventListener("test",JR,JR)}catch(cI){GR=!1}var XR=!!GR&&{passive:!1},ZR=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return"hidden"!==n[t]&&!(n.overflowY===n.overflowX&&!function(e){return"TEXTAREA"===e.tagName}(e)&&"visible"===n[t])},eM=function(e,t){var n=t.ownerDocument,r=t;do{if("undefined"!=typeof ShadowRoot&&r instanceof ShadowRoot&&(r=r.host),tM(e,r)){var o=nM(e,r);if(o[1]>o[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},tM=function(e,t){return"v"===e?function(e){return ZR(e,"overflowY")}(t):function(e){return ZR(e,"overflowX")}(t)},nM=function(e,t){return"v"===e?[(n=t).scrollTop,n.scrollHeight,n.clientHeight]:function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]}(t);var n},rM=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},oM=function(e){return[e.deltaX,e.deltaY]},iM=function(e){return e&&"current"in e?e.current:e},sM=function(e){return"\n .block-interactivity-".concat(e," {pointer-events: none;}\n .allow-interactivity-").concat(e," {pointer-events: all;}\n")},aM=0,lM=[];function cM(e){for(var t=null;null!==e;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const uM=(dM=function(e){var t=qe.useRef([]),n=qe.useRef([0,0]),r=qe.useRef(),o=qe.useState(aM++)[0],i=qe.useState(UR)[0],s=qe.useRef(e);qe.useEffect((function(){s.current=e}),[e]),qe.useEffect((function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var t=Zy([e.lockRef.current],(e.shards||[]).map(iM),!0).filter(Boolean);return t.forEach((function(e){return e.classList.add("allow-interactivity-".concat(o))})),function(){document.body.classList.remove("block-interactivity-".concat(o)),t.forEach((function(e){return e.classList.remove("allow-interactivity-".concat(o))}))}}}),[e.inert,e.lockRef.current,e.shards]);var a=qe.useCallback((function(e,t){if("touches"in e&&2===e.touches.length||"wheel"===e.type&&e.ctrlKey)return!s.current.allowPinchZoom;var o,i=rM(e),a=n.current,l="deltaX"in e?e.deltaX:a[0]-i[0],c="deltaY"in e?e.deltaY:a[1]-i[1],u=e.target,d=Math.abs(l)>Math.abs(c)?"h":"v";if("touches"in e&&"h"===d&&"range"===u.type)return!1;var f=eM(d,u);if(!f)return!0;if(f?o=d:(o="v"===d?"h":"v",f=eM(d,u)),!f)return!1;if(!r.current&&"changedTouches"in e&&(l||c)&&(r.current=o),!o)return!0;var p=r.current||o;return function(e,t,n,r,o){var i=function(e,t){return"h"===e&&"rtl"===t?-1:1}(e,window.getComputedStyle(t).direction),s=i*r,a=n.target,l=t.contains(a),c=!1,u=s>0,d=0,f=0;do{var p=nM(e,a),h=p[0],v=p[1]-p[2]-i*h;(h||v)&&tM(e,a)&&(d+=v,f+=h),a=a instanceof ShadowRoot?a.host:a.parentNode}while(!l&&a!==document.body||l&&(t.contains(a)||t===a));return u&&(Math.abs(d)<1||!o)?c=!0:u||!(Math.abs(f)<1)&&o||(c=!0),c}(p,t,e,"h"===p?l:c,!0)}),[]),l=qe.useCallback((function(e){var n=e;if(lM.length&&lM[lM.length-1]===i){var r="deltaY"in n?oM(n):rM(n),o=t.current.filter((function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&(t=e.delta,o=r,t[0]===o[0]&&t[1]===o[1]);var t,o}))[0];if(o&&o.should)n.cancelable&&n.preventDefault();else if(!o){var l=(s.current.shards||[]).map(iM).filter(Boolean).filter((function(e){return e.contains(n.target)}));(l.length>0?a(n,l[0]):!s.current.noIsolation)&&n.cancelable&&n.preventDefault()}}}),[]),c=qe.useCallback((function(e,n,r,o){var i={name:e,delta:n,target:r,should:o,shadowParent:cM(r)};t.current.push(i),setTimeout((function(){t.current=t.current.filter((function(e){return e!==i}))}),1)}),[]),u=qe.useCallback((function(e){n.current=rM(e),r.current=void 0}),[]),d=qe.useCallback((function(t){c(t.type,oM(t),t.target,a(t,e.lockRef.current))}),[]),f=qe.useCallback((function(t){c(t.type,rM(t),t.target,a(t,e.lockRef.current))}),[]);qe.useEffect((function(){return lM.push(i),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener("wheel",l,XR),document.addEventListener("touchmove",l,XR),document.addEventListener("touchstart",u,XR),function(){lM=lM.filter((function(e){return e!==i})),document.removeEventListener("wheel",l,XR),document.removeEventListener("touchmove",l,XR),document.removeEventListener("touchstart",u,XR)}}),[]);var p=e.removeScrollBar,h=e.inert;return qe.createElement(qe.Fragment,null,h?qe.createElement(i,{styles:sM(o)}):null,p?qe.createElement(QR,{gapMode:e.gapMode}):null)},LR.useMedium(dM),TR);var dM,fM=qe.forwardRef((function(e,t){return qe.createElement(zR,Ky({},e,{ref:t,sideCar:uM}))}));fM.classNames=zR.classNames;var pM=["Enter"," "],hM=["ArrowUp","PageDown","End"],vM=["ArrowDown","PageUp","Home",...hM],mM={ltr:[...pM,"ArrowRight"],rtl:[...pM,"ArrowLeft"]},gM={ltr:["ArrowLeft"],rtl:["ArrowRight"]},yM="Menu",[bM,wM,xM]=jj(yM),[kM,EM]=bj(yM,[xM,MO,uR]),SM=MO(),CM=uR(),[_M,AM]=kM(yM),[NM,PM]=kM(yM),jM=e=>{const{__scopeMenu:t,open:n=!1,children:r,dir:o,onOpenChange:i,modal:s=!0}=e,a=SM(t),[l,c]=qe.useState(null),u=qe.useRef(!1),d=xj(i),f=Oj(o);return qe.useEffect((()=>{const e=()=>{u.current=!0,document.addEventListener("pointerdown",t,{capture:!0,once:!0}),document.addEventListener("pointermove",t,{capture:!0,once:!0})},t=()=>u.current=!1;return document.addEventListener("keydown",e,{capture:!0}),()=>{document.removeEventListener("keydown",e,{capture:!0}),document.removeEventListener("pointerdown",t,{capture:!0}),document.removeEventListener("pointermove",t,{capture:!0})}}),[]),nt.jsx(GO,{...a,children:nt.jsx(_M,{scope:t,open:n,onOpenChange:d,content:l,onContentChange:c,children:nt.jsx(NM,{scope:t,onClose:qe.useCallback((()=>d(!1)),[d]),isUsingKeyboardRef:u,dir:f,modal:s,children:r})})})};jM.displayName=yM;var $M=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e,o=SM(n);return nt.jsx(JO,{...o,...r,ref:t})}));$M.displayName="MenuAnchor";var OM="MenuPortal",[RM,MM]=kM(OM,{forceMount:void 0}),TM=e=>{const{__scopeMenu:t,forceMount:n,children:r,container:o}=e,i=AM(OM,t);return nt.jsx(RM,{scope:t,forceMount:n,children:nt.jsx(tR,{present:n||i.open,children:nt.jsx(eR,{asChild:!0,container:o,children:r})})})};TM.displayName=OM;var LM="MenuContent",[IM,zM]=kM(LM),DM=qe.forwardRef(((e,t)=>{const n=MM(LM,e.__scopeMenu),{forceMount:r=n.forceMount,...o}=e,i=AM(LM,e.__scopeMenu),s=PM(LM,e.__scopeMenu);return nt.jsx(bM.Provider,{scope:e.__scopeMenu,children:nt.jsx(tR,{present:r||i.open,children:nt.jsx(bM.Slot,{scope:e.__scopeMenu,children:s.modal?nt.jsx(BM,{...o,ref:t}):nt.jsx(UM,{...o,ref:t})})})})})),BM=qe.forwardRef(((e,t)=>{const n=AM(LM,e.__scopeMenu),r=qe.useRef(null),o=yj(t,r);return qe.useEffect((()=>{const e=r.current;if(e)return AR(e)}),[]),nt.jsx(FM,{...e,ref:o,trapFocus:n.open,disableOutsidePointerEvents:n.open,disableOutsideScroll:!0,onFocusOutside:vj(e.onFocusOutside,(e=>e.preventDefault()),{checkForDefaultPrevented:!1}),onDismiss:()=>n.onOpenChange(!1)})})),UM=qe.forwardRef(((e,t)=>{const n=AM(LM,e.__scopeMenu);return nt.jsx(FM,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>n.onOpenChange(!1)})})),FM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,loop:r=!1,trapFocus:o,onOpenAutoFocus:i,onCloseAutoFocus:s,disableOutsidePointerEvents:a,onEntryFocus:l,onEscapeKeyDown:c,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,disableOutsideScroll:h,...v}=e,m=AM(LM,n),g=PM(LM,n),y=SM(n),b=CM(n),w=wM(n),[x,k]=qe.useState(null),E=qe.useRef(null),S=yj(t,E,m.onContentChange),C=qe.useRef(0),_=qe.useRef(""),A=qe.useRef(0),N=qe.useRef(null),P=qe.useRef("right"),j=qe.useRef(0),$=h?fM:qe.Fragment,O=h?{as:Ej,allowPinchZoom:!0}:void 0,R=e=>{var t,n;const r=_.current+e,o=w().filter((e=>!e.disabled)),i=document.activeElement,s=null==(t=o.find((e=>e.ref.current===i)))?void 0:t.textValue,a=function(e,t,n){const r=t.length>1&&Array.from(t).every((e=>e===t[0])),o=r?t[0]:t,i=n?e.indexOf(n):-1;let s=(a=e,l=Math.max(i,0),a.map(((e,t)=>a[(l+t)%a.length])));var a,l;1===o.length&&(s=s.filter((e=>e!==n)));const c=s.find((e=>e.toLowerCase().startsWith(o.toLowerCase())));return c!==n?c:void 0}(o.map((e=>e.textValue)),r,s),l=null==(n=o.find((e=>e.textValue===a)))?void 0:n.ref.current;!function e(t){_.current=t,window.clearTimeout(C.current),""!==t&&(C.current=window.setTimeout((()=>e("")),1e3))}(r),l&&setTimeout((()=>l.focus()))};qe.useEffect((()=>()=>window.clearTimeout(C.current)),[]),qe.useEffect((()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??Fj()),document.body.insertAdjacentElement("beforeend",e[1]??Fj()),Uj++,()=>{1===Uj&&document.querySelectorAll("[data-radix-focus-guard]").forEach((e=>e.remove())),Uj--}}),[]);const M=qe.useCallback((e=>{var t,n;return P.current===(null==(t=N.current)?void 0:t.side)&&function(e,t){if(!t)return!1;const n={x:e.clientX,y:e.clientY};return function(e,t){const{x:n,y:r}=e;let o=!1;for(let i=0,s=t.length-1;ir!=c>r&&n<(l-e)*(r-a)/(c-a)+e&&(o=!o)}return o}(n,t)}(e,null==(n=N.current)?void 0:n.area)}),[]);return nt.jsx(IM,{scope:n,searchRef:_,onItemEnter:qe.useCallback((e=>{M(e)&&e.preventDefault()}),[M]),onItemLeave:qe.useCallback((e=>{var t;M(e)||(null==(t=E.current)||t.focus(),k(null))}),[M]),onTriggerLeave:qe.useCallback((e=>{M(e)&&e.preventDefault()}),[M]),pointerGraceTimerRef:A,onPointerGraceIntentChange:qe.useCallback((e=>{N.current=e}),[]),children:nt.jsx($,{...O,children:nt.jsx(qj,{asChild:!0,trapped:o,onMountAutoFocus:vj(i,(e=>{var t;e.preventDefault(),null==(t=E.current)||t.focus({preventScroll:!0})})),onUnmountAutoFocus:s,children:nt.jsx(zj,{asChild:!0,disableOutsidePointerEvents:a,onEscapeKeyDown:c,onPointerDownOutside:u,onFocusOutside:d,onInteractOutside:f,onDismiss:p,children:nt.jsx(bR,{asChild:!0,...b,dir:g.dir,orientation:"vertical",loop:r,currentTabStopId:x,onCurrentTabStopIdChange:k,onEntryFocus:vj(l,(e=>{g.isUsingKeyboardRef.current||e.preventDefault()})),preventScrollOnEntryFocus:!0,children:nt.jsx(XO,{role:"menu","aria-orientation":"vertical","data-state":hT(m.open),"data-radix-menu-content":"",dir:g.dir,...y,...v,ref:S,style:{outline:"none",...v.style},onKeyDown:vj(v.onKeyDown,(e=>{const t=e.target.closest("[data-radix-menu-content]")===e.currentTarget,n=e.ctrlKey||e.altKey||e.metaKey,r=1===e.key.length;t&&("Tab"===e.key&&e.preventDefault(),!n&&r&&R(e.key));const o=E.current;if(e.target!==o)return;if(!vM.includes(e.key))return;e.preventDefault();const i=w().filter((e=>!e.disabled)).map((e=>e.ref.current));hM.includes(e.key)&&i.reverse(),function(e){const t=document.activeElement;for(const n of e){if(n===t)return;if(n.focus(),document.activeElement!==t)return}}(i)})),onBlur:vj(e.onBlur,(e=>{e.currentTarget.contains(e.target)||(window.clearTimeout(C.current),_.current="")})),onPointerMove:vj(e.onPointerMove,gT((e=>{const t=e.target,n=j.current!==e.clientX;if(e.currentTarget.contains(t)&&n){const t=e.clientX>j.current?"right":"left";P.current=t,j.current=e.clientX}})))})})})})})})}));DM.displayName=LM;var HM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(Nj.div,{role:"group",...r,ref:t})}));HM.displayName="MenuGroup";var VM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(Nj.div,{...r,ref:t})}));VM.displayName="MenuLabel";var WM="MenuItem",qM="menu.itemSelect",KM=qe.forwardRef(((e,t)=>{const{disabled:n=!1,onSelect:r,...o}=e,i=qe.useRef(null),s=PM(WM,e.__scopeMenu),a=zM(WM,e.__scopeMenu),l=yj(t,i),c=qe.useRef(!1);return nt.jsx(YM,{...o,ref:l,disabled:n,onClick:vj(e.onClick,(()=>{const e=i.current;if(!n&&e){const t=new CustomEvent(qM,{bubbles:!0,cancelable:!0});e.addEventListener(qM,(e=>null==r?void 0:r(e)),{once:!0}),Pj(e,t),t.defaultPrevented?c.current=!1:s.onClose()}})),onPointerDown:t=>{var n;null==(n=e.onPointerDown)||n.call(e,t),c.current=!0},onPointerUp:vj(e.onPointerUp,(e=>{var t;c.current||null==(t=e.currentTarget)||t.click()})),onKeyDown:vj(e.onKeyDown,(e=>{const t=""!==a.searchRef.current;n||t&&" "===e.key||pM.includes(e.key)&&(e.currentTarget.click(),e.preventDefault())}))})}));KM.displayName=WM;var YM=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,disabled:r=!1,textValue:o,...i}=e,s=zM(WM,n),a=CM(n),l=qe.useRef(null),c=yj(t,l),[u,d]=qe.useState(!1),[f,p]=qe.useState("");return qe.useEffect((()=>{const e=l.current;e&&p((e.textContent??"").trim())}),[i.children]),nt.jsx(bM.ItemSlot,{scope:n,disabled:r,textValue:o??f,children:nt.jsx(wR,{asChild:!0,...a,focusable:!r,children:nt.jsx(Nj.div,{role:"menuitem","data-highlighted":u?"":void 0,"aria-disabled":r||void 0,"data-disabled":r?"":void 0,...i,ref:c,onPointerMove:vj(e.onPointerMove,gT((e=>{if(r)s.onItemLeave(e);else if(s.onItemEnter(e),!e.defaultPrevented){e.currentTarget.focus({preventScroll:!0})}}))),onPointerLeave:vj(e.onPointerLeave,gT((e=>s.onItemLeave(e)))),onFocus:vj(e.onFocus,(()=>d(!0))),onBlur:vj(e.onBlur,(()=>d(!1)))})})})})),QM=qe.forwardRef(((e,t)=>{const{checked:n=!1,onCheckedChange:r,...o}=e;return nt.jsx(rT,{scope:e.__scopeMenu,checked:n,children:nt.jsx(KM,{role:"menuitemcheckbox","aria-checked":vT(n)?"mixed":n,...o,ref:t,"data-state":mT(n),onSelect:vj(o.onSelect,(()=>null==r?void 0:r(!!vT(n)||!n)),{checkForDefaultPrevented:!1})})})}));QM.displayName="MenuCheckboxItem";var GM="MenuRadioGroup",[JM,XM]=kM(GM,{value:void 0,onValueChange:()=>{}}),ZM=qe.forwardRef(((e,t)=>{const{value:n,onValueChange:r,...o}=e,i=xj(r);return nt.jsx(JM,{scope:e.__scopeMenu,value:n,onValueChange:i,children:nt.jsx(HM,{...o,ref:t})})}));ZM.displayName=GM;var eT="MenuRadioItem",tT=qe.forwardRef(((e,t)=>{const{value:n,...r}=e,o=XM(eT,e.__scopeMenu),i=n===o.value;return nt.jsx(rT,{scope:e.__scopeMenu,checked:i,children:nt.jsx(KM,{role:"menuitemradio","aria-checked":i,...r,ref:t,"data-state":mT(i),onSelect:vj(r.onSelect,(()=>{var e;return null==(e=o.onValueChange)?void 0:e.call(o,n)}),{checkForDefaultPrevented:!1})})})}));tT.displayName=eT;var nT="MenuItemIndicator",[rT,oT]=kM(nT,{checked:!1}),iT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,forceMount:r,...o}=e,i=oT(nT,n);return nt.jsx(tR,{present:r||vT(i.checked)||!0===i.checked,children:nt.jsx(Nj.span,{...o,ref:t,"data-state":mT(i.checked)})})}));iT.displayName=nT;var sT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e;return nt.jsx(Nj.div,{role:"separator","aria-orientation":"horizontal",...r,ref:t})}));sT.displayName="MenuSeparator";var aT=qe.forwardRef(((e,t)=>{const{__scopeMenu:n,...r}=e,o=SM(n);return nt.jsx(ZO,{...o,...r,ref:t})}));aT.displayName="MenuArrow";var[lT,cT]=kM("MenuSub"),uT="MenuSubTrigger",dT=qe.forwardRef(((e,t)=>{const n=AM(uT,e.__scopeMenu),r=PM(uT,e.__scopeMenu),o=cT(uT,e.__scopeMenu),i=zM(uT,e.__scopeMenu),s=qe.useRef(null),{pointerGraceTimerRef:a,onPointerGraceIntentChange:l}=i,c={__scopeMenu:e.__scopeMenu},u=qe.useCallback((()=>{s.current&&window.clearTimeout(s.current),s.current=null}),[]);return qe.useEffect((()=>u),[u]),qe.useEffect((()=>{const e=a.current;return()=>{window.clearTimeout(e),l(null)}}),[a,l]),nt.jsx($M,{asChild:!0,...c,children:nt.jsx(YM,{id:o.triggerId,"aria-haspopup":"menu","aria-expanded":n.open,"aria-controls":o.contentId,"data-state":hT(n.open),...e,ref:gj(t,o.onTriggerChange),onClick:t=>{var r;null==(r=e.onClick)||r.call(e,t),e.disabled||t.defaultPrevented||(t.currentTarget.focus(),n.open||n.onOpenChange(!0))},onPointerMove:vj(e.onPointerMove,gT((t=>{i.onItemEnter(t),t.defaultPrevented||e.disabled||n.open||s.current||(i.onPointerGraceIntentChange(null),s.current=window.setTimeout((()=>{n.onOpenChange(!0),u()}),100))}))),onPointerLeave:vj(e.onPointerLeave,gT((e=>{var t,r;u();const o=null==(t=n.content)?void 0:t.getBoundingClientRect();if(o){const t=null==(r=n.content)?void 0:r.dataset.side,s="right"===t,l=s?-5:5,c=o[s?"left":"right"],u=o[s?"right":"left"];i.onPointerGraceIntentChange({area:[{x:e.clientX+l,y:e.clientY},{x:c,y:o.top},{x:u,y:o.top},{x:u,y:o.bottom},{x:c,y:o.bottom}],side:t}),window.clearTimeout(a.current),a.current=window.setTimeout((()=>i.onPointerGraceIntentChange(null)),300)}else{if(i.onTriggerLeave(e),e.defaultPrevented)return;i.onPointerGraceIntentChange(null)}}))),onKeyDown:vj(e.onKeyDown,(t=>{var o;const s=""!==i.searchRef.current;e.disabled||s&&" "===t.key||mM[r.dir].includes(t.key)&&(n.onOpenChange(!0),null==(o=n.content)||o.focus(),t.preventDefault())}))})})}));dT.displayName=uT;var fT="MenuSubContent",pT=qe.forwardRef(((e,t)=>{const n=MM(LM,e.__scopeMenu),{forceMount:r=n.forceMount,...o}=e,i=AM(LM,e.__scopeMenu),s=PM(LM,e.__scopeMenu),a=cT(fT,e.__scopeMenu),l=qe.useRef(null),c=yj(t,l);return nt.jsx(bM.Provider,{scope:e.__scopeMenu,children:nt.jsx(tR,{present:r||i.open,children:nt.jsx(bM.Slot,{scope:e.__scopeMenu,children:nt.jsx(FM,{id:a.contentId,"aria-labelledby":a.triggerId,...o,ref:c,align:"start",side:"rtl"===s.dir?"left":"right",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:e=>{var t;s.isUsingKeyboardRef.current&&(null==(t=l.current)||t.focus()),e.preventDefault()},onCloseAutoFocus:e=>e.preventDefault(),onFocusOutside:vj(e.onFocusOutside,(e=>{e.target!==a.trigger&&i.onOpenChange(!1)})),onEscapeKeyDown:vj(e.onEscapeKeyDown,(e=>{s.onClose(),e.preventDefault()})),onKeyDown:vj(e.onKeyDown,(e=>{var t;const n=e.currentTarget.contains(e.target),r=gM[s.dir].includes(e.key);n&&r&&(i.onOpenChange(!1),null==(t=a.trigger)||t.focus(),e.preventDefault())}))})})})})}));function hT(e){return e?"open":"closed"}function vT(e){return"indeterminate"===e}function mT(e){return vT(e)?"indeterminate":e?"checked":"unchecked"}function gT(e){return t=>"mouse"===t.pointerType?e(t):void 0}pT.displayName=fT;var yT=jM,bT=$M,wT=TM,xT=DM,kT=HM,ET=VM,ST=KM,CT=QM,_T=ZM,AT=tT,NT=iT,PT=sT,jT=aT,$T=dT,OT=pT,RT="DropdownMenu",[MT,TT]=bj(RT,[EM]),LT=EM(),[IT,zT]=MT(RT),DT=e=>{const{__scopeDropdownMenu:t,children:n,dir:r,open:o,defaultOpen:i,onOpenChange:s,modal:a=!0}=e,l=LT(t),c=qe.useRef(null),[u=!1,d]=kj({prop:o,defaultProp:i,onChange:s});return nt.jsx(IT,{scope:t,triggerId:n$(),triggerRef:c,contentId:n$(),open:u,onOpenChange:d,onOpenToggle:qe.useCallback((()=>d((e=>!e))),[d]),modal:a,children:nt.jsx(yT,{...l,open:u,onOpenChange:d,dir:r,modal:a,children:n})})};DT.displayName=RT;var BT="DropdownMenuTrigger",UT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,disabled:r=!1,...o}=e,i=zT(BT,n),s=LT(n);return nt.jsx(bT,{asChild:!0,...s,children:nt.jsx(Nj.button,{type:"button",id:i.triggerId,"aria-haspopup":"menu","aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":i.open?"open":"closed","data-disabled":r?"":void 0,disabled:r,...o,ref:gj(t,i.triggerRef),onPointerDown:vj(e.onPointerDown,(e=>{r||0!==e.button||!1!==e.ctrlKey||(i.onOpenToggle(),i.open||e.preventDefault())})),onKeyDown:vj(e.onKeyDown,(e=>{r||(["Enter"," "].includes(e.key)&&i.onOpenToggle(),"ArrowDown"===e.key&&i.onOpenChange(!0),["Enter"," ","ArrowDown"].includes(e.key)&&e.preventDefault())}))})})}));UT.displayName=BT;var FT=e=>{const{__scopeDropdownMenu:t,...n}=e,r=LT(t);return nt.jsx(wT,{...r,...n})};FT.displayName="DropdownMenuPortal";var HT="DropdownMenuContent",VT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=zT(HT,n),i=LT(n),s=qe.useRef(!1);return nt.jsx(xT,{id:o.contentId,"aria-labelledby":o.triggerId,...i,...r,ref:t,onCloseAutoFocus:vj(e.onCloseAutoFocus,(e=>{var t;s.current||null==(t=o.triggerRef.current)||t.focus(),s.current=!1,e.preventDefault()})),onInteractOutside:vj(e.onInteractOutside,(e=>{const t=e.detail.originalEvent,n=0===t.button&&!0===t.ctrlKey,r=2===t.button||n;o.modal&&!r||(s.current=!0)})),style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})}));VT.displayName=HT;qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(kT,{...o,...r,ref:t})})).displayName="DropdownMenuGroup";var WT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(ET,{...o,...r,ref:t})}));WT.displayName="DropdownMenuLabel";var qT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(ST,{...o,...r,ref:t})}));qT.displayName="DropdownMenuItem";var KT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(CT,{...o,...r,ref:t})}));KT.displayName="DropdownMenuCheckboxItem";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(_T,{...o,...r,ref:t})})).displayName="DropdownMenuRadioGroup";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(AT,{...o,...r,ref:t})})).displayName="DropdownMenuRadioItem";var YT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(NT,{...o,...r,ref:t})}));YT.displayName="DropdownMenuItemIndicator";var QT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(PT,{...o,...r,ref:t})}));QT.displayName="DropdownMenuSeparator";qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(jT,{...o,...r,ref:t})})).displayName="DropdownMenuArrow";var GT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx($T,{...o,...r,ref:t})}));GT.displayName="DropdownMenuSubTrigger";var JT=qe.forwardRef(((e,t)=>{const{__scopeDropdownMenu:n,...r}=e,o=LT(n);return nt.jsx(OT,{...o,...r,ref:t,style:{...e.style,"--radix-dropdown-menu-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-dropdown-menu-content-available-width":"var(--radix-popper-available-width)","--radix-dropdown-menu-content-available-height":"var(--radix-popper-available-height)","--radix-dropdown-menu-trigger-width":"var(--radix-popper-anchor-width)","--radix-dropdown-menu-trigger-height":"var(--radix-popper-anchor-height)"}})}));JT.displayName="DropdownMenuSubContent";var XT=DT,ZT=UT,eL=FT,tL=VT,nL=WT,rL=qT,oL=KT,iL=YT,sL=QT,aL=GT,lL=JT;const cL=(...e)=>e.filter(((e,t,n)=>Boolean(e)&&""!==e.trim()&&n.indexOf(e)===t)).join(" ").trim();var uL={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const dL=qe.forwardRef((({color:e="currentColor",size:t=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:o="",children:i,iconNode:s,...a},l)=>qe.createElement("svg",{ref:l,...uL,width:t,height:t,stroke:e,strokeWidth:r?24*Number(n)/Number(t):n,className:cL("lucide",o),...a},[...s.map((([e,t])=>qe.createElement(e,t))),...Array.isArray(i)?i:[i]]))),fL=(e,t)=>{const n=qe.forwardRef((({className:n,...r},o)=>{return qe.createElement(dL,{ref:o,iconNode:t,className:cL(`lucide-${i=e,i.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}`,n),...r});var i}));return n.displayName=`${e}`,n},pL=fL("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),hL=fL("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]),vL=XT,mL=ZT;qe.forwardRef((({className:e,inset:t,children:n,...r},o)=>nt.jsxs(aL,{ref:o,className:ap("flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",t&&"pl-8",e),...r,children:[n,nt.jsx(hL,{className:"ml-auto h-4 w-4"})]}))).displayName=aL.displayName;qe.forwardRef((({className:e,...t},n)=>nt.jsx(lL,{ref:n,className:ap("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...t}))).displayName=lL.displayName;const gL=qe.forwardRef((({className:e,sideOffset:t=4,...n},r)=>nt.jsx(eL,{children:nt.jsx(tL,{ref:r,sideOffset:t,className:ap("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})})));gL.displayName=tL.displayName;const yL=qe.forwardRef((({className:e,inset:t,...n},r)=>nt.jsx(rL,{ref:r,className:ap("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",t&&"pl-8",e),...n})));yL.displayName=rL.displayName;qe.forwardRef((({className:e,children:t,checked:n,...r},o)=>nt.jsxs(oL,{ref:o,className:ap("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),checked:n,...r,children:[nt.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:nt.jsx(iL,{children:nt.jsx(pL,{className:"h-4 w-4"})})}),t]}))).displayName=oL.displayName;qe.forwardRef((({className:e,inset:t,...n},r)=>nt.jsx(nL,{ref:r,className:ap("px-2 py-1.5 text-sm font-semibold",t&&"pl-8",e),...n}))).displayName=nL.displayName;const bL=qe.forwardRef((({className:e,...t},n)=>nt.jsx(sL,{ref:n,className:ap("-mx-1 my-1 h-px bg-muted",e),...t})));bL.displayName=sL.displayName;const wL=new Set(["children","localName","ref","style","className"]),xL=new WeakMap,kL=(e,t,n,r,o)=>{const i=null==o?void 0:o[t];void 0===i?(e[t]=n,null==n&&t in HTMLElement.prototype&&e.removeAttribute(t)):n!==r&&((e,t,n)=>{let r=xL.get(e);void 0===r&&xL.set(e,r=new Map);let o=r.get(t);void 0!==n?void 0===o?(r.set(t,o={handleEvent:n}),e.addEventListener(t,o)):o.handleEvent=n:void 0!==o&&(r.delete(t),e.removeEventListener(t,o))})(e,i,n)},EL=({react:e,tagName:t,elementClass:n,events:r,displayName:o})=>{const i=new Set(Object.keys(r??{})),s=e.forwardRef(((o,s)=>{const a=e.useRef(new Map),l=e.useRef(null),c={},u={};for(const[e,t]of Object.entries(o))wL.has(e)?c["className"===e?"class":e]=t:i.has(e)||e in n.prototype?u[e]=t:c[e]=t;return e.useLayoutEffect((()=>{if(null===l.current)return;const e=new Map;for(const t in u)kL(l.current,t,o[t],a.current.get(t),r),a.current.delete(t),e.set(t,o[t]);for(const[t,n]of a.current)kL(l.current,t,void 0,n,r);a.current=e})),e.useLayoutEffect((()=>{var e;null==(e=l.current)||e.removeAttribute("defer-hydration")}),[]),c.suppressHydrationWarning=!0,e.createElement(t,{...c,ref:e.useCallback((e=>{l.current=e,"function"==typeof s?s(e):null!==s&&(s.current=e)}),[s])})}));return s.displayName=o??n.name,s};EL({tagName:"dc-connection-button",elementClass:pj,react:Ke});const SL=EL({tagName:"dc-connection-dialog",elementClass:sj,react:Ke,events:{onClose:"close"}});function CL(...e){const t=e=>e,n=(e,t)=>n=>e(t(n));return{encode:e.map((e=>e.encode)).reduceRight(n,t),decode:e.map((e=>e.decode)).reduce(n,t)}}function _L(e){return{encode:t=>{if(!Array.isArray(t)||t.length&&"number"!=typeof t[0])throw new Error("alphabet.encode input should be an array of numbers");return t.map((t=>{if(t<0||t>=e.length)throw new Error(`Digit index outside alphabet: ${t} (alphabet: ${e.length})`);return e[t]}))},decode:t=>{if(!Array.isArray(t)||t.length&&"string"!=typeof t[0])throw new Error("alphabet.decode input should be array of strings");return t.map((t=>{if("string"!=typeof t)throw new Error(`alphabet.decode: not string element=${t}`);const n=e.indexOf(t);if(-1===n)throw new Error(`Unknown letter: "${t}". Allowed: ${e}`);return n}))}}}function AL(e=""){if("string"!=typeof e)throw new Error("join separator should be string");return{encode:t=>{if(!Array.isArray(t)||t.length&&"string"!=typeof t[0])throw new Error("join.encode input should be array of strings");for(let e of t)if("string"!=typeof e)throw new Error(`join.encode: non-string input=${e}`);return t.join(e)},decode:t=>{if("string"!=typeof t)throw new Error("join.decode input should be string");return t.split(e)}}}function NL(e,t,n){if(t<2)throw new Error(`convertRadix: wrong from=${t}, base cannot be less than 2`);if(n<2)throw new Error(`convertRadix: wrong to=${n}, base cannot be less than 2`);if(!Array.isArray(e))throw new Error("convertRadix: data should be array");if(!e.length)return[];let r=0;const o=[],i=Array.from(e);for(i.forEach((e=>{if(e<0||e>=t)throw new Error(`Wrong integer: ${e}`)}));;){let e=0,s=!0;for(let o=r;o{if(!function(e){return e instanceof Uint8Array||null!=e&&"object"==typeof e&&"Uint8Array"===e.constructor.name}(t))throw new Error("radix.encode input should be Uint8Array");return NL(Array.from(t),256,e)},decode:t=>{if(!Array.isArray(t)||t.length&&"number"!=typeof t[0])throw new Error("radix.decode input should be array of numbers");return Uint8Array.from(NL(t,e,256))}}}const jL=(e=>CL(PL(58),_L(e),AL("")))("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");function $L(e){if(!Number.isSafeInteger(e)||e<0)throw new Error(`positive integer expected, not ${e}`)}function OL(e,...t){if(!function(e){return e instanceof Uint8Array||null!=e&&"object"==typeof e&&"Uint8Array"===e.constructor.name}(e))throw new Error("Uint8Array expected");if(t.length>0&&!t.includes(e.length))throw new Error(`Uint8Array expected of length ${t}, not of length=${e.length}`)}function RL(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}const ML=e=>new Uint32Array(e.buffer,e.byteOffset,Math.floor(e.byteLength/4)),TL=68===new Uint8Array(new Uint32Array([287454020]).buffer)[0],LL=e=>e<<24&4278190080|e<<8&16711680|e>>>8&65280|e>>>24&255,IL=TL?e=>e:e=>LL(e);function zL(e){for(let t=0;te(n).update(DL(t)).digest(),n=e({});return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=t=>e(t),t}const FL=new Uint8Array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3,11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4,7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8,9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13,2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9,12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11,13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10,6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5,10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3]);class HL extends BL{constructor(e,t,n={},r,o,i){if(super(),this.blockLen=e,this.outputLen=t,this.length=0,this.pos=0,this.finished=!1,this.destroyed=!1,$L(e),$L(t),$L(r),t<0||t>r)throw new Error("outputLen bigger than keyLen");if(void 0!==n.key&&(n.key.length<1||n.key.length>r))throw new Error(`key must be up 1..${r} byte long or undefined`);if(void 0!==n.salt&&n.salt.length!==o)throw new Error(`salt must be ${o} byte long or undefined`);if(void 0!==n.personalization&&n.personalization.length!==i)throw new Error(`personalization must be ${i} byte long or undefined`);this.buffer32=ML(this.buffer=new Uint8Array(e))}update(e){RL(this);const{blockLen:t,buffer:n,buffer32:r}=this,o=(e=DL(e)).length,i=e.byteOffset,s=e.buffer;for(let a=0;ar[t]=IL(e)))}digest(){const{buffer:e,outputLen:t}=this;this.digestInto(e);const n=e.slice(0,t);return this.destroy(),n}_cloneInto(e){const{buffer:t,length:n,finished:r,destroyed:o,outputLen:i,pos:s}=this;return e||(e=new this.constructor({dkLen:i})),e.set(...this.get()),e.length=n,e.finished=r,e.destroyed=o,e.outputLen=i,e.buffer.set(t),e.pos=s,e}}const VL=BigInt(2**32-1),WL=BigInt(32);function qL(e,t=!1){return t?{h:Number(e&VL),l:Number(e>>WL&VL)}:{h:0|Number(e>>WL&VL),l:0|Number(e&VL)}}const KL={fromBig:qL,split:function(e,t=!1){let n=new Uint32Array(e.length),r=new Uint32Array(e.length);for(let o=0;oBigInt(e>>>0)<>>0),shrSH:(e,t,n)=>e>>>n,shrSL:(e,t,n)=>e<<32-n|t>>>n,rotrSH:(e,t,n)=>e>>>n|t<<32-n,rotrSL:(e,t,n)=>e<<32-n|t>>>n,rotrBH:(e,t,n)=>e<<64-n|t>>>n-32,rotrBL:(e,t,n)=>e>>>n-32|t<<64-n,rotr32H:(e,t)=>t,rotr32L:(e,t)=>e,rotlSH:(e,t,n)=>e<>>32-n,rotlSL:(e,t,n)=>t<>>32-n,rotlBH:(e,t,n)=>t<>>64-n,rotlBL:(e,t,n)=>e<>>64-n,add:function(e,t,n,r){const o=(t>>>0)+(r>>>0);return{h:e+n+(o/2**32|0)|0,l:0|o}},add3L:(e,t,n)=>(e>>>0)+(t>>>0)+(n>>>0),add3H:(e,t,n,r)=>t+n+r+(e/2**32|0)|0,add4L:(e,t,n,r)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0),add4H:(e,t,n,r,o)=>t+n+r+o+(e/2**32|0)|0,add5H:(e,t,n,r,o,i)=>t+n+r+o+i+(e/2**32|0)|0,add5L:(e,t,n,r,o)=>(e>>>0)+(t>>>0)+(n>>>0)+(r>>>0)+(o>>>0)},YL=new Uint32Array([4089235720,1779033703,2227873595,3144134277,4271175723,1013904242,1595750129,2773480762,2917565137,1359893119,725511199,2600822924,4215389547,528734635,327033209,1541459225]),QL=new Uint32Array(32);function GL(e,t,n,r,o,i){const s=o[i],a=o[i+1];let l=QL[2*e],c=QL[2*e+1],u=QL[2*t],d=QL[2*t+1],f=QL[2*n],p=QL[2*n+1],h=QL[2*r],v=QL[2*r+1],m=KL.add3L(l,u,s);c=KL.add3H(m,c,d,a),l=0|m,({Dh:v,Dl:h}={Dh:v^c,Dl:h^l}),({Dh:v,Dl:h}={Dh:KL.rotr32H(v,h),Dl:KL.rotr32L(v,h)}),({h:p,l:f}=KL.add(p,f,v,h)),({Bh:d,Bl:u}={Bh:d^p,Bl:u^f}),({Bh:d,Bl:u}={Bh:KL.rotrSH(d,u,24),Bl:KL.rotrSL(d,u,24)}),QL[2*e]=l,QL[2*e+1]=c,QL[2*t]=u,QL[2*t+1]=d,QL[2*n]=f,QL[2*n+1]=p,QL[2*r]=h,QL[2*r+1]=v}function JL(e,t,n,r,o,i){const s=o[i],a=o[i+1];let l=QL[2*e],c=QL[2*e+1],u=QL[2*t],d=QL[2*t+1],f=QL[2*n],p=QL[2*n+1],h=QL[2*r],v=QL[2*r+1],m=KL.add3L(l,u,s);c=KL.add3H(m,c,d,a),l=0|m,({Dh:v,Dl:h}={Dh:v^c,Dl:h^l}),({Dh:v,Dl:h}={Dh:KL.rotrSH(v,h,16),Dl:KL.rotrSL(v,h,16)}),({h:p,l:f}=KL.add(p,f,v,h)),({Bh:d,Bl:u}={Bh:d^p,Bl:u^f}),({Bh:d,Bl:u}={Bh:KL.rotrBH(d,u,63),Bl:KL.rotrBL(d,u,63)}),QL[2*e]=l,QL[2*e+1]=c,QL[2*t]=u,QL[2*t+1]=d,QL[2*n]=f,QL[2*n+1]=p,QL[2*r]=h,QL[2*r+1]=v}class XL extends HL{constructor(e={}){super(128,void 0===e.dkLen?64:e.dkLen,e,64,16,16),this.v0l=0|YL[0],this.v0h=0|YL[1],this.v1l=0|YL[2],this.v1h=0|YL[3],this.v2l=0|YL[4],this.v2h=0|YL[5],this.v3l=0|YL[6],this.v3h=0|YL[7],this.v4l=0|YL[8],this.v4h=0|YL[9],this.v5l=0|YL[10],this.v5h=0|YL[11],this.v6l=0|YL[12],this.v6h=0|YL[13],this.v7l=0|YL[14],this.v7h=0|YL[15];const t=e.key?e.key.length:0;if(this.v0l^=this.outputLen|t<<8|65536|1<<24,e.salt){const t=ML(DL(e.salt));this.v4l^=IL(t[0]),this.v4h^=IL(t[1]),this.v5l^=IL(t[2]),this.v5h^=IL(t[3])}if(e.personalization){const t=ML(DL(e.personalization));this.v6l^=IL(t[0]),this.v6h^=IL(t[1]),this.v7l^=IL(t[2]),this.v7h^=IL(t[3])}if(e.key){const t=new Uint8Array(this.blockLen);t.set(DL(e.key)),this.update(t)}}get(){let{v0l:e,v0h:t,v1l:n,v1h:r,v2l:o,v2h:i,v3l:s,v3h:a,v4l:l,v4h:c,v5l:u,v5h:d,v6l:f,v6h:p,v7l:h,v7h:v}=this;return[e,t,n,r,o,i,s,a,l,c,u,d,f,p,h,v]}set(e,t,n,r,o,i,s,a,l,c,u,d,f,p,h,v){this.v0l=0|e,this.v0h=0|t,this.v1l=0|n,this.v1h=0|r,this.v2l=0|o,this.v2h=0|i,this.v3l=0|s,this.v3h=0|a,this.v4l=0|l,this.v4h=0|c,this.v5l=0|u,this.v5h=0|d,this.v6l=0|f,this.v6h=0|p,this.v7l=0|h,this.v7h=0|v}compress(e,t,n){this.get().forEach(((e,t)=>QL[t]=e)),QL.set(YL,16);let{h:r,l:o}=KL.fromBig(BigInt(this.length));QL[24]=YL[8]^o,QL[25]=YL[9]^r,n&&(QL[28]=~QL[28],QL[29]=~QL[29]);let i=0;const s=FL;for(let a=0;a<12;a++)GL(0,4,8,12,e,t+2*s[i++]),JL(0,4,8,12,e,t+2*s[i++]),GL(1,5,9,13,e,t+2*s[i++]),JL(1,5,9,13,e,t+2*s[i++]),GL(2,6,10,14,e,t+2*s[i++]),JL(2,6,10,14,e,t+2*s[i++]),GL(3,7,11,15,e,t+2*s[i++]),JL(3,7,11,15,e,t+2*s[i++]),GL(0,5,10,15,e,t+2*s[i++]),JL(0,5,10,15,e,t+2*s[i++]),GL(1,6,11,12,e,t+2*s[i++]),JL(1,6,11,12,e,t+2*s[i++]),GL(2,7,8,13,e,t+2*s[i++]),JL(2,7,8,13,e,t+2*s[i++]),GL(3,4,9,14,e,t+2*s[i++]),JL(3,4,9,14,e,t+2*s[i++]);this.v0l^=QL[0]^QL[16],this.v0h^=QL[1]^QL[17],this.v1l^=QL[2]^QL[18],this.v1h^=QL[3]^QL[19],this.v2l^=QL[4]^QL[20],this.v2h^=QL[5]^QL[21],this.v3l^=QL[6]^QL[22],this.v3h^=QL[7]^QL[23],this.v4l^=QL[8]^QL[24],this.v4h^=QL[9]^QL[25],this.v5l^=QL[10]^QL[26],this.v5h^=QL[11]^QL[27],this.v6l^=QL[12]^QL[28],this.v6h^=QL[13]^QL[29],this.v7l^=QL[14]^QL[30],this.v7h^=QL[15]^QL[31],QL.fill(0)}destroy(){this.destroyed=!0,this.buffer32.fill(0),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}}const ZL=UL((e=>new XL(e))),eI=(new TextEncoder).encode("SS58PRE"),tI=e=>{try{const t=jL.decode(e),n=t.subarray(0,64&t[0]?2:1),r=t.subarray(n.length,t.length-2),o=t.subarray(n.length+r.length),i=ZL(Uint8Array.of(...eI,...n,...r),{dkLen:64}).subarray(0,2);return o[0]===i[0]&&o[1]===i[1]?{isValid:!0,ss58Format:nI(n),publicKey:r.slice()}:{isValid:!1}}catch{return{isValid:!1}}},nI=e=>{const t=new DataView(e.buffer,e.byteOffset,e.byteLength);return 1===t.byteLength?t.getUint8(0):t.getUint16(0)},rI={target:{colors:[0,28,0,0,28,0,0,28,0,0,28,0,0,28,0,0,28,0,1],freq:1},cube:{colors:[0,1,3,2,4,3,0,1,3,2,4,3,0,1,3,2,4,3,5],freq:20},quazar:{colors:[1,2,3,1,2,4,5,5,4,1,2,3,1,2,4,5,5,4,0],freq:16},flower:{colors:[0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,3],freq:32},cyclic:{colors:[0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,4,5,6],freq:32},vmirror:{colors:[0,1,2,3,4,5,3,4,2,0,1,6,7,8,9,7,8,6,10],freq:128},hmirror:{colors:[0,1,2,3,4,5,6,7,8,9,10,8,6,7,5,3,4,2,11],freq:128}},oI=e=>{let t=0;const n=Object.values(rI).find((n=>(t+=n.freq,e{var l;const[c,u]=qe.useState(!1),[d,f]=qe.useState(i),[p,h]=qe.useState(),[v,m]=qe.useState(),[g,y]=qe.useState();qe.useEffect((()=>{if("string"==typeof e&&!e.includes("px")&&!e.includes("rem"))throw new Error("Providing a string for 'size' in Polkicon should be expressed either in 'px', 'rem' or 'em'");let t,n;if("string"==typeof e)switch(n=e.replace(/[0-9.]/g,""),n){case"px":t=parseFloat(e);break;case"rem":t=10*parseFloat(e)}else"number"==typeof e&&(t=e);var r,o;h(n?""+("px"===n?t+"px":t/10+"rem"):t),t<12&&(r=n||"number",o="px"===n?"12px":"rem"===n?"1.2rem":12,console.warn(`Polkicon: 'Size' expressed in '${r}' cannot be less than ${o}. Will be resized to minimum size.`)),t<32?(y("0rem 0.5rem"),m("0.5rem")):t>=32&&t<64?(y("1rem 0.5rem"),m("1rem")):t>=64&&t<100?(y("2rem 1rem"),m("1.5rem")):t>=100&&(y("3rem 1rem"),m("2rem"))}),[e]);const b=qe.useCallback((()=>{n&&(async e=>{try{await navigator.clipboard.writeText(e),u(!0),f(i)}catch{u(!0),f("Failed!")}})(t)}),[n,t,i]);qe.useEffect((()=>{n&&c&&setTimeout((()=>{u(!1)}),o)}),[n,c,o]);const{c:w,r:x,rroot3o2:k,ro2:E,rroot3o4:S,ro4:C,r3o4:_,z:A,rot:N,scheme:P,palette:j}=(e=>{const t=ZL(new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])),n=tI(e);let r=n.isValid?n.publicKey:e;if(!("object"==typeof r&&r&&r instanceof Uint8Array&&32==r.length))return{};r=Uint8Array.from(ZL(r)).map(((e,n)=>(e+256-t[n])%256));const o=22.4,i=o*Math.sqrt(3)/2,s=o*Math.sqrt(3)/4,a=3*o/4,l=Object.keys(rI).map((e=>rI[e].freq)).reduce(((e,t)=>e+t)),c=Math.floor(70*r[29]/256+26)%80+30,u=Math.floor((r[30]+256*r[31])%l),d=oI(u),f=Array.from(r).map(((e,t)=>{const n=(e+t%28*58)%256;if(0==n)return"#444";if(255==n)return"transparent";const r=Math.floor(n%64*360/64),o=[53,15,35,75][Math.floor(n/64)];return`hsl(${r}, ${c}%, ${o}%)`}));return{c:32,r:o,rroot3o2:i,ro2:11.2,rroot3o4:s,ro4:5.6,r3o4:a,z:5,rot:r[28]%6*3,scheme:d,palette:f}})(t),$=tI(t).isValid?null==(l=null==P?void 0:P.colors)?void 0:l.map(((e,t)=>j[null==P?void 0:P.colors[t<18?(t+N)%18:18]])):[];let O=0;return $?nt.jsxs("div",{onClick:n?b:void 0,style:n?{cursor:c?"none":"copy",position:"relative",display:"flex",justifyContent:"center",alignItems:"center"}:{display:"flex",justifyContent:"center",alignItems:"center"},children:[nt.jsxs("svg",{id:Math.random().toString(36).substring(2,9),className:a,style:s,width:p,height:p,viewBox:"0 0 64 64",children:[nt.jsx("circle",{cx:32,cy:32,r:32,fill:r}),nt.jsx("circle",{cx:w,cy:w-x,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w-E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w-_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-k,cy:w-E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w-C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-k,cy:w,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-k,cy:w+E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w+C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w-S,cy:w+_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w+x,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w+E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w+_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+k,cy:w+E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w+C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+k,cy:w,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+k,cy:w-E,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w-C,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w+S,cy:w-_,r:A,fill:$[O++]}),nt.jsx("circle",{cx:w,cy:w,r:A,fill:$[O++]})]}),n&&c&&nt.jsx("p",{style:{fontSize:v,fontWeight:"bold",padding:g,width:p,height:p,position:"absolute",borderRadius:"55rem",color:"white",background:"green",opacity:"80%",alignItems:"center",justifyContent:"center",display:"flex"},children:d})]}):null},sI=()=>{const{accounts:e,selectAccount:t,selectedAccount:n}=YS(),[,r]=DS(),[o,i]=qe.useState(!1);return qe.useEffect((()=>{!(null==n?void 0:n.address)&&e.length>0&&(t(e[0]),i(!1))}),[e,t,null==n?void 0:n.address]),nt.jsxs(nt.Fragment,{children:[nt.jsx("div",{className:"flex w-full justify-center",children:nt.jsxs("div",{className:"flex w-full",children:[!e.length&&nt.jsx(aC,{onClick:()=>i(!0),className:"w-full text-lg font-bold bg-pink-700 hover:bg-blue-600",children:"Connect Wallet"}),!!e.length&&nt.jsxs(vL,{children:[nt.jsx(mL,{asChild:!0,children:nt.jsxs(aC,{variant:"outline",size:"default",className:"cursor-pointer overflow-hidden w-full flex items-center justify-center gap-2",children:[nt.jsx(iI,{size:36,address:(null==n?void 0:n.address)||"",className:"mr-2",outerColor:"transparent"}),null==n?void 0:n.name,nt.jsx(cC,{className:"ml-2 h-4 w-4",isOpen:!1})]})}),nt.jsxs(gL,{className:"max-h-[calc(100vh-5rem)] overflow-auto",children:[e.map(((n,r)=>nt.jsxs(qe.Fragment,{children:[nt.jsxs(yL,{className:"cursor-pointer",onClick:()=>t(n),children:[nt.jsx(iI,{size:28,address:n.address||"",className:"mr-2",outerColor:"transparent"}),n.name]},n.address),r!==e.length-1&&nt.jsx(bL,{})]},n.address))),nt.jsx(yL,{className:"cursor-pointer",onClick:()=>{i(!0)},children:"Show wallets"},"show"),nt.jsx(yL,{className:"cursor-pointer",onClick:()=>{r(),t(void 0)},children:"Disconnect"},"logout")]})]})]})}),nt.jsx(SL,{open:o,onClose:()=>i(!1)})]})};function aI(){return nt.jsx("div",{className:"flex items-center justify-center mt-6",children:nt.jsxs(up,{className:"w-full max-w-xl",children:[nt.jsx(dp,{children:nt.jsxs(fp,{className:"text-4xl text-center flex items-center justify-center gap-2",children:[nt.jsx(lI,{}),nt.jsxs("span",{children:["CLI: ",nt.jsx("span",{className:"font-light",children:"Signing Portal"})]})]})}),nt.jsx(pp,{className:"grid gap-4",children:nt.jsx(PS,{config:hj,children:nt.jsx(KS,{children:nt.jsxs("div",{className:"overflow-auto break-words whitespace-pre-wrap",children:[nt.jsx(sI,{}),nt.jsx(wC,{})]})})})})]})})}const lI=()=>nt.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"100",height:"100",fill:"none",viewBox:"0 0 512 512",children:[nt.jsxs("g",{clipPath:"url(#clip0_873_174)",children:[nt.jsx("rect",{width:"512",height:"512",fill:"#1C0533",rx:"256"}),nt.jsx("rect",{width:"512",height:"512",fill:"url(#paint0_radial_873_174)",fillOpacity:"0.8",rx:"256"}),nt.jsx("rect",{width:"512",height:"512",fill:"url(#paint1_radial_873_174)",fillOpacity:"0.6",rx:"256"}),nt.jsx("mask",{id:"mask0_873_174",width:"1428",height:"1351",x:"-429",y:"-502",maskUnits:"userSpaceOnUse",style:{maskType:"alpha"},children:nt.jsx("path",{fill:"#D9D9D9",d:"m127.637-501.289 871.274 824.192-14.25 13.48-871.273-824.192zM88.96-464.701l871.272 824.192-14.249 13.48L74.71-451.221zM50.281-428.113l871.273 824.192-14.249 13.48L36.032-414.633zM11.603-391.525l871.273 824.192-14.249 13.48L-2.646-378.045zM-27.075-354.937l871.273 824.192-14.249 13.48-871.273-824.192zM-65.753-318.349 805.52 505.843l-14.249 13.48-871.273-824.192zM-104.431-281.761l871.273 824.192-14.249 13.48-871.273-824.192zM-143.109-245.173l871.273 824.192-14.249 13.48-871.273-824.192zM-181.787-208.585l871.273 824.192-14.249 13.48-871.273-824.192zM-220.465-171.997l871.273 824.192-14.249 13.48-871.273-824.192zM-259.143-135.409 612.13 688.783l-14.249 13.48-871.273-824.192zM-297.821-98.821 573.452 725.37l-14.249 13.48L-312.07-85.341zM-336.499-62.233l871.273 824.192-14.249 13.48-871.273-824.192zM-375.177-25.645l871.273 824.192-14.249 13.479-871.273-824.191zM-413.855 10.943l871.273 824.192-14.249 13.48-871.273-824.192z"})}),nt.jsx("g",{mask:"url(#mask0_873_174)",children:nt.jsx("path",{fill:"#E6007A",d:"M511.169 254.929C511.169 396.905 396.739 512 255.584 512 114.428 512-.001 396.905-.001 254.929S114.428-2.142 255.584-2.142c141.155 0 255.585 115.095 255.585 257.071"})}),nt.jsx("path",{fill:"#2B0532",d:"M183.804 160.44c-12.752-9.296-27.346-13.272-41.94-13.272h-89.63L5.63 367.567h117.053l9.399-44.802a86.4 86.4 0 0 0 28.32-8.371 104 104 0 0 0 4.133-2.155 97.7 97.7 0 0 0 14.779 24.342c8.258 9.967 18.342 17.785 29.802 23.435l.003.002.397.195.4.186c12.688 5.89 26.181 8.385 39.713 8.385 13.588 0 26.877-2.295 39.662-6.916a127 127 0 0 0 17.54-7.88l-2.871 13.579h117.052l9.4-44.802a86.4 86.4 0 0 0 28.32-8.371 101.4 101.4 0 0 0 28.939-21.348l.01-.011.159-.168.154-.167.003-.003c8.092-8.766 14.613-18.702 19.34-29.722 4.865-11.344 7.542-23.439 7.542-36.022 0-12.506-2.649-24.626-7.879-35.976-5.493-12.224-13.86-22.642-24.867-30.537-12.752-9.296-27.346-13.272-41.94-13.272h-89.631l-5.24 24.781c-7.026-6.908-15.096-12.504-24.009-16.758-12.764-6.233-26.404-8.834-40.034-8.834-13.589 0-26.878 2.295-39.662 6.916-12.145 4.39-23.444 10.507-33.804 18.268a133 133 0 0 0-7.099 5.68 74.6 74.6 0 0 0-16.911-16.781"}),nt.jsx("path",{fill:"#E6007A",fillRule:"evenodd",d:"m229.569 331.163.063.031.064.03c7.896 3.665 16.711 5.426 26.323 5.426 9.96 0 19.59-1.672 28.858-5.022a94.6 94.6 0 0 0 25.4-13.857c7.69-5.873 14.403-12.621 20.12-20.24l.016-.022.016-.022c5.708-7.75 10.156-16.036 13.319-24.849 3.326-9.027 5.005-18.25 5.005-27.639 0-8.458-1.303-16.601-3.928-24.399-2.514-8.002-6.373-15.177-11.585-21.468-5.277-6.369-11.756-11.363-19.365-14.972-7.797-3.835-16.58-5.669-26.205-5.669-9.961 0-19.591 1.673-28.858 5.022-9.165 3.313-17.687 7.924-25.551 13.819-7.719 5.752-14.51 12.512-20.362 20.265l-.01.014a97.4 97.4 0 0 0-13.544 24.867l-.015.04-.014.041c-3.181 9.016-4.784 18.219-4.784 27.583 0 8.482 1.311 16.702 3.94 24.634l.005.016.005.016c2.653 7.855 6.575 14.936 11.766 21.201 5.269 6.361 11.733 11.413 19.321 15.154m58.163-55.148.01-.015c2.998-4.343 5.197-8.955 6.622-13.852 1.467-5.04 2.176-9.872 2.176-14.513 0-4.335-.697-8.177-2.012-11.592l-.041-.109-.038-.11c-1.121-3.279-2.866-5.693-5.197-7.475-2.026-1.548-5.053-2.613-9.646-2.613-5.335 0-9.796 1.243-13.579 3.569l-.053.032-.053.031c-4.052 2.378-7.426 5.537-10.156 9.541a52.3 52.3 0 0 0-6.661 13.745c-1.454 5.016-2.157 9.826-2.157 14.447 0 4.358.638 8.321 1.862 11.932 1.246 3.306 3.081 5.852 5.484 7.803 2.047 1.543 5.008 2.569 9.387 2.569 5.495 0 10.017-1.255 13.78-3.568 4.107-2.524 7.515-5.779 10.262-9.808zM171.542 186.53c-6.663-4.89-14.525-7.228-23.288-7.228H84.491L51.478 335.434h51.685l9.273-44.204h15.658c8.698 0 16.935-1.893 24.636-5.69a69.4 69.4 0 0 0 19.864-14.652l.025-.027.025-.027c5.695-6.169 10.18-13.036 13.418-20.587 3.261-7.602 4.93-15.381 4.93-23.294 0-7.901-1.665-15.457-4.983-22.605-3.247-7.286-8.082-13.265-14.467-17.818m-35.967 55.601.023-.03c.765-1.001 1.462-2.391 1.969-4.328l.037-.142.044-.141c.574-1.827.929-4.221.929-7.293 0-2.333-.311-3.426-.466-3.769a4 4 0 0 0-.222-.434l-.027-.045-.013-.01h-11.5l-3.905 18.655h10.249c.063-.013.306-.078.757-.35.511-.309 1.235-.919 2.103-2.084zM469.872 186.53c-6.663-4.89-14.525-7.228-23.289-7.228h-63.762l-33.014 156.132h51.686l9.273-44.204h15.658c8.698 0 16.935-1.893 24.636-5.69a69.4 69.4 0 0 0 19.863-14.652l.026-.027.024-.027c5.696-6.169 10.18-13.036 13.419-20.587 3.26-7.602 4.93-15.381 4.93-23.294 0-7.901-1.665-15.457-4.983-22.605-3.247-7.286-8.082-13.265-14.467-17.818m-35.967 55.601.023-.03c.765-1.001 1.462-2.391 1.968-4.328l.038-.142.044-.141c.574-1.827.929-4.221.929-7.293 0-2.333-.311-3.426-.466-3.769a4 4 0 0 0-.222-.434l-.027-.045-.014-.01h-11.5l-3.904 18.655h10.249c.063-.013.306-.078.756-.35.511-.309 1.235-.919 2.104-2.084z",clipRule:"evenodd"}),nt.jsxs("mask",{id:"path-6-outside-1_873_174",width:"435",height:"156",x:"35.823",y:"166.788",fill:"#000",maskUnits:"userSpaceOnUse",children:[nt.jsx("path",{fill:"#fff",d:"M35.823 166.788h435v156h-435z"}),nt.jsx("path",{fillRule:"evenodd",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318m-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391m-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852m133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd"})]}),nt.jsx("path",{fill:"#fff",fillRule:"evenodd",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318m-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391m-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852m133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd"}),nt.jsx("path",{stroke:"#fff",strokeWidth:"0.3",d:"M68.98 168.599h62.13c8.395 0 15.832 2.234 22.11 6.846 6.094 4.342 10.707 10.041 13.812 17.016 3.197 6.883 4.8 14.151 4.8 21.761 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.016.018-.017.017a67.4 67.4 0 0 1-19.29 14.23c-7.419 3.657-15.352 5.483-23.749 5.483H93.657l-9.273 44.203h-47.56zm21.407 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.144-13.383q7.862-8.516 12.297-18.857t4.435-20.885-4.435-20.074q-4.233-9.53-12.499-15.41-8.265-6.084-19.756-6.083H72.244L41.803 316.62h39.31zm29.65-41.79.015-.019c.958-1.253 1.758-2.901 2.32-5.048l.025-.096.03-.093c.651-2.074 1.022-4.685 1.022-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.697-1.289 2.679-2.606zm-22.19 7.32 6.45-30.821h16.733q1.612 0 3.024 1.014 1.411.811 2.419 3.041t1.008 6.286q0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217zm115.507 78.686-.042-.021c-7.343-3.62-13.579-8.498-18.66-14.63-5.028-6.07-8.83-12.931-11.405-20.555l-.007-.022c-2.559-7.721-3.836-15.725-3.836-23.993 0-9.126 1.562-18.1 4.668-26.904l.009-.028.01-.027a95.4 95.4 0 0 1 13.263-24.351l.007-.009c5.738-7.601 12.392-14.225 19.957-19.863 7.707-5.777 16.054-10.293 25.032-13.538 9.045-3.27 18.444-4.903 28.176-4.903 9.373 0 17.85 1.786 25.332 5.469 7.357 3.487 13.603 8.304 18.689 14.442 5.043 6.087 8.78 13.033 11.217 20.801 2.557 7.586 3.829 15.516 3.829 23.768 0 9.144-1.635 18.132-4.883 26.944-3.094 8.623-7.448 16.738-13.045 24.337l-.01.015-.011.014c-5.604 7.468-12.184 14.085-19.73 19.847a92.6 92.6 0 0 1-24.863 13.564c-9.045 3.27-18.445 4.903-28.176 4.903-9.365 0-17.885-1.715-25.478-5.24zm107.57-58.815q4.638-12.571 4.637-25.549 0-11.76-3.629-22.507-3.427-10.95-10.483-19.466-7.055-8.516-17.337-13.382-10.281-5.07-23.586-5.07-13.91 0-26.813 4.664t-23.99 12.977q-10.886 8.111-19.151 19.06a91.3 91.3 0 0 0-12.701 23.319q-4.434 12.571-4.435 25.548 0 11.762 3.629 22.71 3.629 10.748 10.685 19.263 7.055 8.517 17.337 13.586 10.482 4.866 23.788 4.866 13.91 0 26.812-4.663a88.6 88.6 0 0 0 23.788-12.977q10.887-8.314 18.95-19.061 8.065-10.949 12.499-23.318Zm-48.684 6.628.014-.019c3.119-4.518 5.413-9.327 6.901-14.439 1.515-5.208 2.257-10.231 2.257-15.083 0-4.557-.734-8.65-2.148-12.324l-.027-.072-.025-.074c-1.24-3.628-3.21-6.386-5.885-8.43-2.47-1.889-5.966-3.026-10.865-3.026-5.662 0-10.495 1.324-14.63 3.866l-.036.021-.035.021c-4.314 2.531-7.907 5.899-10.802 10.146a54.3 54.3 0 0 0-6.928 14.3c-1.506 5.191-2.244 10.2-2.244 15.038 0 4.574.672 8.77 1.981 12.619 1.368 3.647 3.421 6.515 6.143 8.717 2.483 1.882 5.911 2.996 10.625 2.996 5.813 0 10.71-1.332 14.831-3.866 4.351-2.674 7.967-6.128 10.873-10.391Zm-38.808 14.46q-5.04-4.056-7.459-10.544-2.217-6.489-2.218-13.991 0-7.908 2.42-16.221a58.3 58.3 0 0 1 7.459-15.411q4.838-7.096 12.095-11.355 7.258-4.461 16.733-4.461 8.265 0 13.305 3.853 5.04 3.852 7.257 10.341 2.42 6.287 2.42 13.788 0 7.908-2.42 16.222-2.418 8.313-7.459 15.613-4.838 7.097-12.095 11.558t-16.934 4.46q-8.064 0-13.104-3.852Zm133.877-110.306h62.13c8.396 0 15.833 2.234 22.111 6.846 6.094 4.342 10.706 10.04 13.811 17.015 3.198 6.884 4.801 14.151 4.801 21.762 0 7.619-1.606 15.125-4.765 22.491-3.145 7.332-7.501 14.006-13.045 20.011l-.017.018-.016.017a67.4 67.4 0 0 1-19.291 14.23c-7.418 3.657-15.351 5.483-23.748 5.483h-17.293l-9.273 44.203h-47.561zm21.408 103.817h20.563q11.693 0 21.974-5.069a63.3 63.3 0 0 0 18.143-13.383q7.863-8.516 12.298-18.857t4.435-20.885-4.435-20.074q-4.234-9.53-12.499-15.41-8.265-6.084-19.757-6.083h-58.865L340.133 316.62h39.311zm29.649-41.79.015-.019c.959-1.253 1.759-2.901 2.321-5.048l.025-.096.029-.093c.652-2.074 1.023-4.685 1.023-7.904 0-2.456-.319-3.883-.647-4.607-.44-.975-.761-1.204-.76-1.206l-.166-.095-.166-.119c-.311-.224-.51-.259-.68-.259h-13.461l-4.754 22.71h12.772c.292 0 .857-.096 1.756-.638.788-.476 1.696-1.289 2.679-2.606zm-22.19 7.32 6.451-30.821h16.733q1.613 0 3.024 1.014 1.41.811 2.419 3.041 1.008 2.231 1.008 6.286 0 5.272-1.21 9.125-1.008 3.852-3.024 6.488-1.814 2.433-3.83 3.65t-3.83 1.217z",clipRule:"evenodd",mask:"url(#path-6-outside-1_873_174)"})]}),nt.jsxs("defs",{children:[nt.jsxs("radialGradient",{id:"paint0_radial_873_174",cx:"0",cy:"0",r:"1",gradientTransform:"matrix(229.99965 647.20057 -371.3092 131.95444 -62.8 -86.4)",gradientUnits:"userSpaceOnUse",children:[nt.jsx("stop",{stopColor:"#6335EC"}),nt.jsx("stop",{offset:"1",stopOpacity:"0"})]}),nt.jsxs("radialGradient",{id:"paint1_radial_873_174",cx:"0",cy:"0",r:"1",gradientTransform:"rotate(-116.448 530.068 140.453)scale(1055.25 662.094)",gradientUnits:"userSpaceOnUse",children:[nt.jsx("stop",{stopColor:"#E6007A"}),nt.jsx("stop",{offset:"1",stopOpacity:"0"})]}),nt.jsx("clipPath",{id:"clip0_873_174",children:nt.jsx("rect",{width:"512",height:"512",fill:"#fff",rx:"256"})})]})]});rt.createRoot(document.getElementById("root")).render(nt.jsx(Ke.StrictMode,{children:nt.jsx(aI,{})})); + From 1ae63085d91e045a1f7d3b34ee8928cac5f9396d Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 11:13:34 +0100 Subject: [PATCH 067/117] refactor: rename parachain to rollup --- crates/pop-cli/src/commands/up/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 5d409065a..7b7c216a8 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -73,11 +73,11 @@ impl Command { } #[cfg(feature = "parachain")] if pop_parachains::is_supported(project_path.as_deref())? { - cli.warning("Parachain deployment is currently not implemented.")?; + cli.warning("Rollup deployment is currently not implemented.")?; return Ok("parachain"); } cli.warning( - "No contract or parachain detected. Ensure you are in a valid project directory.", + "No contract or rollup detected. Ensure you are in a valid project directory.", )?; Ok("") } @@ -135,9 +135,9 @@ mod tests { } #[tokio::test] - async fn detects_parachain_correctly() -> anyhow::Result<()> { + async fn detects_rollup_correctly() -> anyhow::Result<()> { let temp_dir = tempfile::tempdir()?; - let name = "parachain"; + let name = "rollup"; let project_path = temp_dir.path().join(name); let config = Config { symbol: "DOT".to_string(), @@ -148,7 +148,7 @@ mod tests { let args = create_up_args(project_path)?; let mut cli = - MockCli::new().expect_warning("Parachain deployment is currently not implemented."); + MockCli::new().expect_warning("Rollup deployment is currently not implemented."); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); cli.verify() } @@ -163,7 +163,7 @@ mod tests { cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; let mut cli = MockCli::new().expect_warning( - "No contract or parachain detected. Ensure you are in a valid project directory.", + "No contract or rollup detected. Ensure you are in a valid project directory.", ); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, ""); cli.verify() From 1d55f2349e9840605b9f1e9f73eabe9b48fe62d9 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 11:14:43 +0100 Subject: [PATCH 068/117] docs: improve deprecation message --- crates/pop-cli/src/commands/up/network.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/up/network.rs b/crates/pop-cli/src/commands/up/network.rs index f45d5f45c..7233e5b05 100644 --- a/crates/pop-cli/src/commands/up/network.rs +++ b/crates/pop-cli/src/commands/up/network.rs @@ -64,7 +64,7 @@ impl ZombienetCommand { // Show warning if specified as deprecated. if !self.valid { log::warning( - "DEPRECATION: Please use `pop up network` (or simply `pop up n`) in the future...", + "DEPRECATION: Please use `pop up network` (or simply `pop u n`) in the future...", )?; } From 0f53ec6478c600366f2f1441c51192e995acee26 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 11:17:30 +0100 Subject: [PATCH 069/117] refactor: rename parachain to rollup in up help --- crates/pop-cli/src/commands/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 0f597183c..07a3aa59a 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -55,9 +55,9 @@ fn about_build() -> &'static str { /// Help message for the `up` command. fn about_up() -> &'static str { #[cfg(all(feature = "parachain", feature = "contract"))] - return "Deploy a parachain, deploy a smart contract or launch a local network."; + return "Deploy a rollup(parachain), deploy a smart contract or launch a local network."; #[cfg(all(feature = "parachain", not(feature = "contract")))] - return "Deploy a parachain or launch a local network."; + return "Deploy a rollup(parachain) or launch a local network."; #[cfg(all(feature = "contract", not(feature = "parachain")))] return "Deploy a smart contract."; } From 1476a1db10c6e8c64de77ea6ea8966c93357d6fa Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 10 Feb 2025 11:23:52 +0100 Subject: [PATCH 070/117] feat: deploy a parachain commands --- crates/pop-cli/src/commands/up/mod.rs | 15 +++++++++++--- crates/pop-cli/src/commands/up/parachain.rs | 23 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 crates/pop-cli/src/commands/up/parachain.rs diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 7b7c216a8..3c075704b 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -11,6 +11,8 @@ use std::path::PathBuf; mod contract; #[cfg(feature = "parachain")] mod network; +#[cfg(feature = "parachain")] +mod parachain; /// Arguments for launching or deploying a project. #[derive(Args, Clone)] @@ -29,6 +31,10 @@ pub(crate) struct UpArgs { #[cfg(feature = "contract")] pub(crate) contract: contract::UpContractCommand, + #[command(flatten)] + #[cfg(feature = "parachain")] + pub(crate) parachain: parachain::UpParachainCommand, + #[command(subcommand)] pub(crate) command: Option, } @@ -73,7 +79,9 @@ impl Command { } #[cfg(feature = "parachain")] if pop_parachains::is_supported(project_path.as_deref())? { - cli.warning("Rollup deployment is currently not implemented.")?; + let mut cmd = args.parachain; + cmd.path = project_path; + cmd.execute(cli).await?; return Ok("parachain"); } cli.warning( @@ -85,7 +93,7 @@ impl Command { #[cfg(test)] mod tests { - use super::{contract::UpContractCommand, *}; + use super::{contract::UpContractCommand, parachain::UpParachainCommand, *}; use cli::MockCli; use duct::cmd; @@ -114,6 +122,7 @@ mod tests { skip_confirm: false, valid: false, }, + parachain: UpParachainCommand::default(), command: None, }) } @@ -148,7 +157,7 @@ mod tests { let args = create_up_args(project_path)?; let mut cli = - MockCli::new().expect_warning("Rollup deployment is currently not implemented."); + MockCli::new().expect_warning("Deploy a parachain"); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); cli.verify() } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs new file mode 100644 index 000000000..beeca6709 --- /dev/null +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::cli; +use clap::Args; +use std::path::PathBuf; + +const HELP_HEADER: &str = "Parachain deployment options"; + +#[derive(Args, Clone, Default)] +#[clap(next_help_heading = HELP_HEADER)] +pub struct UpParachainCommand { + /// Path to the contract build directory. + #[clap(skip)] + pub(crate) path: Option, +} + +impl UpParachainCommand { + /// Executes the command. + pub(crate) async fn execute(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<()> { + cli.intro("Deploy a parachain")?; + Ok(()) + } +} From 9c8e1e1c058f715a8c62f7a7fdb67d01f81f7ea3 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 10 Feb 2025 17:31:20 +0100 Subject: [PATCH 071/117] feat: build specs in pop up --- crates/pop-cli/src/commands/build/spec.rs | 89 ++++++++++++++------- crates/pop-cli/src/commands/up/mod.rs | 8 +- crates/pop-cli/src/commands/up/parachain.rs | 35 +++++++- 3 files changed, 100 insertions(+), 32 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index 7e9ba052b..c09220bf7 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -9,7 +9,7 @@ use crate::{ style::style, }; use clap::{Args, ValueEnum}; -use cliclack::spinner; +use cliclack::{spinner, ProgressBar}; use pop_common::Profile; use pop_parachains::{ binary_path, build_parachain, export_wasm_file, generate_genesis_state_file, @@ -193,7 +193,7 @@ impl BuildSpecCommand { /// /// # Arguments /// * `cli` - The cli. - async fn configure_build_spec( + pub async fn configure_build_spec( self, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result { @@ -439,7 +439,7 @@ impl BuildSpecCommand { // Represents the configuration for building a chain specification. #[derive(Debug)] -struct BuildSpec { +pub struct BuildSpec { output_file: PathBuf, profile: Profile, id: u32, @@ -461,39 +461,18 @@ impl BuildSpec { fn build(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<&'static str> { cli.intro("Building your chain spec")?; let mut generated_files = vec![]; - let BuildSpec { - ref output_file, - ref profile, - id, - default_bootnode, - ref chain, - genesis_state, - genesis_code, - .. - } = self; + let BuildSpec { ref output_file, ref profile, id, genesis_code, genesis_state, .. } = self; // Ensure binary is built. let binary_path = ensure_binary_exists(cli, profile)?; let spinner = spinner(); - spinner.start("Generating chain specification..."); - // Generate chain spec. - generate_plain_chain_spec(&binary_path, output_file, default_bootnode, chain)?; - // Customize spec based on input. - self.customize()?; + let raw_chain_spec = self.generate_chain_spec(&binary_path, &spinner)?; + generated_files.push(format!( "Plain text chain specification file generated at: {}", &output_file.display() )); - // Generate raw spec. - spinner.set_message("Generating raw chain specification..."); - let spec_name = &output_file - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or(DEFAULT_SPEC_NAME) - .trim_end_matches(".json"); - let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; generated_files.push(format!( "Raw chain specification file generated at: {}", raw_chain_spec.display() @@ -529,6 +508,62 @@ impl BuildSpec { Ok("spec") } + /// Generates chain specification files and returns the paths of the genesis code and genesis + /// state. + /// + /// # Arguments + /// * `cli` - The cli. + pub fn generate_genesis_artifacts( + self, + cli: &mut impl cli::traits::Cli, + ) -> anyhow::Result<(PathBuf, PathBuf)> { + // Ensure binary is built once. + let binary_path = ensure_binary_exists(cli, &self.profile)?; + let spinner = spinner(); + spinner.start("Generating files..."); + + let raw_chain_spec = self.generate_chain_spec(&binary_path, &spinner)?; + + spinner.set_message("Generating genesis code..."); + let wasm_file_name = format!("para-{}.wasm", self.id); + let genesis_code = export_wasm_file(&binary_path, &raw_chain_spec, &wasm_file_name)?; + + spinner.set_message("Generating genesis state..."); + let genesis_file_name = format!("para-{}-genesis-state", self.id); + let genesis_state = + generate_genesis_state_file(&binary_path, &raw_chain_spec, &genesis_file_name)?; + + spinner.stop("Genesis artifacts generated successfully."); + Ok((genesis_code, genesis_state)) + } + + /// Generates plain and raw chain specification files. + fn generate_chain_spec( + &self, + binary_path: &PathBuf, + spinner: &ProgressBar, + ) -> anyhow::Result { + let BuildSpec { output_file, chain, .. } = self; + spinner.start("Generating chain specification..."); + + // Generate plain chain spec. + generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; + // Customize spec based on input. + self.customize()?; + + // Generate raw spec. + spinner.set_message("Generating raw chain specification..."); + let spec_name = &output_file + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(DEFAULT_SPEC_NAME) + .trim_end_matches(".json"); + let raw_spec_name = format!("{spec_name}-raw.json"); + let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; + + Ok(raw_chain_spec) + } + // Customize a chain specification. fn customize(&self) -> anyhow::Result<()> { let mut chain_spec = ChainSpec::from(&self.output_file)?; diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 3c075704b..fdb7eb744 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -27,14 +27,14 @@ pub(crate) struct UpArgs { #[arg(value_name = "PATH", index = 1, global = true, conflicts_with = "path")] pub path_pos: Option, - #[command(flatten)] - #[cfg(feature = "contract")] - pub(crate) contract: contract::UpContractCommand, - #[command(flatten)] #[cfg(feature = "parachain")] pub(crate) parachain: parachain::UpParachainCommand, + #[command(flatten)] + #[cfg(feature = "contract")] + pub(crate) contract: contract::UpContractCommand, + #[command(subcommand)] pub(crate) command: Option, } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index beeca6709..14172ec38 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 -use crate::cli; +use crate::{ + build::spec::BuildSpecCommand, + cli::{self}, +}; use clap::Args; use std::path::PathBuf; @@ -12,12 +15,42 @@ pub struct UpParachainCommand { /// Path to the contract build directory. #[clap(skip)] pub(crate) path: Option, + /// Parachain ID to be used when generating the chain spec files. + #[arg(short, long)] + pub(crate) id: Option, + /// Path to the genesis state file. + #[arg(short = 'G', long = "genesis-state")] + pub(crate) genesis_state: Option, + /// Path to the genesis code file. + #[arg(short = 'C', long = "genesis-code")] + pub(crate) genesis_code: Option, } impl UpParachainCommand { /// Executes the command. pub(crate) async fn execute(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<()> { cli.intro("Deploy a parachain")?; + let para_id = self.id.unwrap_or(reserve_para_id(cli)?); + let (genesis_state, genesis_code) = + match (self.genesis_state.clone(), self.genesis_code.clone()) { + (Some(state), Some(code)) => (state, code), + _ => generate_spec_files(para_id, cli).await?, + }; + cli.outro("Parachain deployment complete.")?; Ok(()) } } + +fn reserve_para_id(cli: &mut impl cli::traits::Cli) -> anyhow::Result { + Ok(2000) +} + +async fn generate_spec_files( + id: u32, + cli: &mut impl cli::traits::Cli, +) -> anyhow::Result<(PathBuf, PathBuf)> { + let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } + .configure_build_spec(cli) + .await?; + build_spec.generate_genesis_artifacts(cli) +} From deed4ee29b8f1444c11d8e71de10341f79ac956c Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 10:49:16 +0100 Subject: [PATCH 072/117] feat: logic to interact with a chain --- crates/pop-cli/src/commands/call/chain.rs | 8 +- crates/pop-cli/src/commands/up/parachain.rs | 84 ++++++++++++++++++++- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 18f61fe77..45a9a334e 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -358,13 +358,13 @@ impl CallChainCommand { } // Represents a chain, including its URL, client connection, and available pallets. -struct Chain { +pub struct Chain { // Websocket endpoint of the node. - url: Url, + pub(crate) url: Url, // The client used to interact with the chain. - client: OnlineClient, + pub(crate) client: OnlineClient, // A list of pallets available on the chain. - pallets: Vec, + pub(crate) pallets: Vec, } /// Represents a configured dispatchable function call, including the pallet, function, arguments, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 14172ec38..187b2e700 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,11 +2,21 @@ use crate::{ build::spec::BuildSpecCommand, - cli::{self}, + call::chain::Chain, + cli::{self, traits::*}, + common::wallet::request_signature, }; +use anyhow::{anyhow, Result}; use clap::Args; +use pop_parachains::{ + construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, + submit_signed_extrinsic, Action, OnlineClient, Payload, SubstrateConfig, +}; + use std::path::PathBuf; +use url::Url; +const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; const HELP_HEADER: &str = "Parachain deployment options"; #[derive(Args, Clone, Default)] @@ -24,13 +34,17 @@ pub struct UpParachainCommand { /// Path to the genesis code file. #[arg(short = 'C', long = "genesis-code")] pub(crate) genesis_code: Option, + /// Websocket endpoint of the relay chain. + #[arg(long)] + pub(crate) relay_url: Option, } impl UpParachainCommand { /// Executes the command. - pub(crate) async fn execute(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<()> { + pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a parachain")?; - let para_id = self.id.unwrap_or(reserve_para_id(cli)?); + let chain = self.configure_chain(cli).await?; + let para_id = self.id.unwrap_or(reserve_para_id(chain, cli).await?); let (genesis_state, genesis_code) = match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), @@ -39,9 +53,44 @@ impl UpParachainCommand { cli.outro("Parachain deployment complete.")?; Ok(()) } + + // Configures the chain by resolving the URL and fetching its metadata. + async fn configure_chain(&self, cli: &mut impl Cli) -> Result { + // Resolve url. + let url = match &self.relay_url { + Some(url) => url.clone(), + None => { + // Prompt for url. + let url: String = cli + .input("Enter the relay chain node URL to deploy your parachain:") + .default_input(DEFAULT_URL) + .interact()?; + Url::parse(&url)? + }, + }; + + // Parse metadata from chain url. + let client = set_up_client(url.as_str()).await?; + let mut pallets = parse_chain_metadata(&client).map_err(|e| { + anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) + })?; + // Sort by name for display. + pallets.sort_by(|a, b| a.name.cmp(&b.name)); + pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); + Ok(Chain { url, client, pallets }) + } } -fn reserve_para_id(cli: &mut impl cli::traits::Cli) -> anyhow::Result { +async fn reserve_para_id(chain: Chain, cli: &mut impl Cli) -> Result { + cli.info("Reserving a parachain ID for your parachain...")?; + let ex = find_dispatchable_by_name( + &chain.pallets, + Action::Reserve.pallet_name(), + Action::Reserve.function_name(), + )?; + let xt = construct_extrinsic(ex, Vec::new())?; + let call_data = xt.encode_call_data(&chain.client.metadata())?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; Ok(2000) } @@ -49,8 +98,35 @@ async fn generate_spec_files( id: u32, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { + cli.info("Generating the chain spec for your parachain—some extra information is needed:")?; let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } .configure_build_spec(cli) .await?; build_spec.generate_genesis_artifacts(cli) } + +// Sign and submit an extrinsic using wallet integration. +pub async fn submit_extrinsic_with_wallet( + client: &OnlineClient, + url: &Url, + call_data: Vec, + cli: &mut impl Cli, +) -> Result<()> { + let maybe_payload = request_signature(call_data, url.to_string()).await?; + if let Some(payload) = maybe_payload { + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner.start( + "Submitting the extrinsic and then waiting for finalization, please be patient...", + ); + + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); + } else { + cli.outro_cancel("No signed payload received.")?; + } + Ok(()) +} From 83ca4cf66c90eca596b88c6e9068d3e7a30cebda Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 16:05:53 +0100 Subject: [PATCH 073/117] feat: register parachain --- crates/pop-cli/src/commands/call/chain.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 68 ++++++++++----------- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 45a9a334e..019ba3996 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -474,7 +474,7 @@ impl Call { } // Sign and submit an extrinsic using wallet integration. -async fn submit_extrinsic_with_wallet( +pub async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, call_data: Vec, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 187b2e700..e65c954f4 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,15 +2,14 @@ use crate::{ build::spec::BuildSpecCommand, - call::chain::Chain, + call::chain::{submit_extrinsic_with_wallet, Chain}, cli::{self, traits::*}, - common::wallet::request_signature, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, - submit_signed_extrinsic, Action, OnlineClient, Payload, SubstrateConfig, + Action, Payload, }; use std::path::PathBuf; @@ -22,7 +21,7 @@ const HELP_HEADER: &str = "Parachain deployment options"; #[derive(Args, Clone, Default)] #[clap(next_help_heading = HELP_HEADER)] pub struct UpParachainCommand { - /// Path to the contract build directory. + /// Path to the chain directory. #[clap(skip)] pub(crate) path: Option, /// Parachain ID to be used when generating the chain spec files. @@ -44,12 +43,17 @@ impl UpParachainCommand { pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a parachain")?; let chain = self.configure_chain(cli).await?; - let para_id = self.id.unwrap_or(reserve_para_id(chain, cli).await?); + let para_id = if let Some(id) = self.id { + id + } else { + reserve_para_id(&chain, cli).await? + }; let (genesis_state, genesis_code) = match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), - _ => generate_spec_files(para_id, cli).await?, + _ => generate_spec_files(para_id, self.path, cli).await?, }; + register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await?; cli.outro("Parachain deployment complete.")?; Ok(()) } @@ -71,17 +75,14 @@ impl UpParachainCommand { // Parse metadata from chain url. let client = set_up_client(url.as_str()).await?; - let mut pallets = parse_chain_metadata(&client).map_err(|e| { + let pallets = parse_chain_metadata(&client).map_err(|e| { anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) })?; - // Sort by name for display. - pallets.sort_by(|a, b| a.name.cmp(&b.name)); - pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); Ok(Chain { url, client, pallets }) } } -async fn reserve_para_id(chain: Chain, cli: &mut impl Cli) -> Result { +async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { cli.info("Reserving a parachain ID for your parachain...")?; let ex = find_dispatchable_by_name( &chain.pallets, @@ -96,37 +97,32 @@ async fn reserve_para_id(chain: Chain, cli: &mut impl Cli) -> Result { async fn generate_spec_files( id: u32, + path: Option, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { - cli.info("Generating the chain spec for your parachain—some extra information is needed:")?; + cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; + if let Some(path) = &path { + std::env::set_current_dir(path).map_err(|err| { + anyhow!("Failed to change working directory to {}: {}", path.display(), err) + })?; + } let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } .configure_build_spec(cli) .await?; build_spec.generate_genesis_artifacts(cli) } -// Sign and submit an extrinsic using wallet integration. -pub async fn submit_extrinsic_with_wallet( - client: &OnlineClient, - url: &Url, - call_data: Vec, - cli: &mut impl Cli, -) -> Result<()> { - let maybe_payload = request_signature(call_data, url.to_string()).await?; - if let Some(payload) = maybe_payload { - cli.success("Signed payload received.")?; - let spinner = cliclack::spinner(); - spinner.start( - "Submitting the extrinsic and then waiting for finalization, please be patient...", - ); - - let result = submit_signed_extrinsic(client.clone(), payload) - .await - .map_err(|err| anyhow!("{}", format!("{err:?}")))?; - - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); - } else { - cli.outro_cancel("No signed payload received.")?; - } +async fn register_parachain(chain: &Chain, id: u32, genesis_state: PathBuf, genesis_code: PathBuf, cli: &mut impl Cli) -> Result<()> { + cli.info("Registering a parachain ID")?; + let ex = find_dispatchable_by_name( + &chain.pallets, + Action::Register.pallet_name(), + Action::Register.function_name(), + )?; + let state = std::fs::read_to_string(genesis_state).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let code = std::fs::read_to_string(genesis_code).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + let call_data = xt.encode_call_data(&chain.client.metadata())?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; Ok(()) -} +} \ No newline at end of file From 78a328e9387a009743d4026b7967fc289aabac79 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 17:54:33 +0100 Subject: [PATCH 074/117] refactor: clean code and improve docs --- crates/pop-cli/src/commands/build/spec.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 133 ++++++++++++++------ 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index c09220bf7..2b580f198 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -534,7 +534,7 @@ impl BuildSpec { generate_genesis_state_file(&binary_path, &raw_chain_spec, &genesis_file_name)?; spinner.stop("Genesis artifacts generated successfully."); - Ok((genesis_code, genesis_state)) + Ok((genesis_state, genesis_code)) } /// Generates plain and raw chain specification files. diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index e65c954f4..1b17bbbff 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,15 +1,15 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, + build::spec::{BuildSpec, BuildSpecCommand}, call::chain::{submit_extrinsic_with_wallet, Chain}, cli::{self, traits::*}, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, - Action, Payload, + construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, + Payload, }; use std::path::PathBuf; @@ -24,7 +24,7 @@ pub struct UpParachainCommand { /// Path to the chain directory. #[clap(skip)] pub(crate) path: Option, - /// Parachain ID to be used when generating the chain spec files. + /// Parachain ID to use. If not specified, a new ID will be reserved. #[arg(short, long)] pub(crate) id: Option, /// Path to the genesis state file. @@ -43,17 +43,40 @@ impl UpParachainCommand { pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a parachain")?; let chain = self.configure_chain(cli).await?; - let para_id = if let Some(id) = self.id { - id - } else { - reserve_para_id(&chain, cli).await? - }; + let para_id = match self.id { + Some(id) => id, + None => { + cli.info("Reserving a parachain ID...")?; + match reserve_para_id(&chain, cli).await { + Ok(id) => id, + Err(e) => { + cli.outro_cancel(&format!("Failed to reserve parachain ID: {}", e))?; + return Err(e); + }, + } + }, + }; let (genesis_state, genesis_code) = match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), - _ => generate_spec_files(para_id, self.path, cli).await?, + _ => { + cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; + match generate_spec_files(para_id, self.path, cli).await { + Ok(files) => files, + Err(e) => { + cli.outro_cancel(&format!("Failed to generate spec files: {}", e))?; + return Err(e); + }, + } + }, }; - register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await?; + cli.info("Registering a parachain ID")?; + if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await + { + cli.outro_cancel(&format!("Failed to register parachain: {}", e))?; + return Err(e); + } + cli.outro("Parachain deployment complete.")?; Ok(()) } @@ -82,47 +105,85 @@ impl UpParachainCommand { } } +/// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { - cli.info("Reserving a parachain ID for your parachain...")?; - let ex = find_dispatchable_by_name( + let call_data = prepare_reserve_para_id_extrinsic(chain)?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; + Ok(2000) +} + +/// Constructs an extrinsic for reserving a parachain ID. +fn prepare_reserve_para_id_extrinsic(chain: &Chain) -> Result> { + let function = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), Action::Reserve.function_name(), )?; - let xt = construct_extrinsic(ex, Vec::new())?; - let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(2000) + let xt = construct_extrinsic(function, Vec::new())?; + Ok(xt.encode_call_data(&chain.client.metadata())?) } +/// Generates chain spec files for the parachain. async fn generate_spec_files( id: u32, - path: Option, - cli: &mut impl cli::traits::Cli, + path: Option, + cli: &mut impl Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { - cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; - if let Some(path) = &path { - std::env::set_current_dir(path).map_err(|err| { - anyhow!("Failed to change working directory to {}: {}", path.display(), err) - })?; - } - let build_spec = BuildSpecCommand { id: Some(id), ..Default::default() } - .configure_build_spec(cli) - .await?; + change_working_directory(&path)?; + let build_spec = configure_build_spec(id, cli).await?; build_spec.generate_genesis_artifacts(cli) } -async fn register_parachain(chain: &Chain, id: u32, genesis_state: PathBuf, genesis_code: PathBuf, cli: &mut impl Cli) -> Result<()> { - cli.info("Registering a parachain ID")?; +/// Changes the working directory if a path is provided, ensuring the build spec process runs in the +/// correct context. +fn change_working_directory(path: &Option) -> Result<()> { + if let Some(path) = path { + std::env::set_current_dir(path)?; + } + Ok(()) +} + +/// Configures the chain specification requirements. +async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result { + Ok(BuildSpecCommand { + id: Some(id), + genesis_code: true, + genesis_state: true, + ..Default::default() + } + .configure_build_spec(cli) + .await?) +} + +/// Registers a parachain by submitting an extrinsic. +async fn register_parachain( + chain: &Chain, + id: u32, + genesis_state: PathBuf, + genesis_code: PathBuf, + cli: &mut impl Cli, +) -> Result<()> { + let call_data = prepare_register_parachain_extrinsic(chain, id, genesis_state, genesis_code)?; + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; + Ok(()) +} + +/// Constructs an extrinsic for registering a parachain. +fn prepare_register_parachain_extrinsic( + chain: &Chain, + id: u32, + genesis_state: PathBuf, + genesis_code: PathBuf, +) -> Result> { let ex = find_dispatchable_by_name( &chain.pallets, Action::Register.pallet_name(), Action::Register.function_name(), )?; - let state = std::fs::read_to_string(genesis_state).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; - let code = std::fs::read_to_string(genesis_code).map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let state = std::fs::read_to_string(genesis_state) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let code = std::fs::read_to_string(genesis_code) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; - let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(()) -} \ No newline at end of file + Ok(xt.encode_call_data(&chain.client.metadata())?) +} From 1b65915d89bcf8ada56606810720ab93392aaa1b Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 11 Feb 2025 20:30:26 +0100 Subject: [PATCH 075/117] test: unit tests for pop up methods --- crates/pop-cli/src/commands/up/parachain.rs | 153 +++++++++++++++++- .../pop-parachains/src/call/metadata/mod.rs | 5 +- 2 files changed, 155 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 1b17bbbff..32ce91007 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -3,7 +3,7 @@ use crate::{ build::spec::{BuildSpec, BuildSpecCommand}, call::chain::{submit_extrinsic_with_wallet, Chain}, - cli::{self, traits::*}, + cli::traits::*, }; use anyhow::{anyhow, Result}; use clap::Args; @@ -89,7 +89,7 @@ impl UpParachainCommand { None => { // Prompt for url. let url: String = cli - .input("Enter the relay chain node URL to deploy your parachain:") + .input("Enter the relay chain node URL to deploy your parachain") .default_input(DEFAULT_URL) .interact()?; Url::parse(&url)? @@ -187,3 +187,152 @@ fn prepare_register_parachain_extrinsic( let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; Ok(xt.encode_call_data(&chain.client.metadata())?) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + build::spec::{ChainType, RelayChain}, + cli::MockCli, + }; + use pop_common::Profile; + use pop_parachains::decode_call_data; + use std::{env, fs}; + use strum::{EnumMessage, VariantArray}; + use tempfile::tempdir; + use url::Url; + + const POLKADOT_NETWORK_URL: &str = "wss://polkadot-rpc.publicnode.com"; + + #[tokio::test] + async fn configure_chain_works() -> Result<()> { + let mut cli = MockCli::new().expect_input( + "Enter the relay chain node URL to deploy your parachain", + POLKADOT_NETWORK_URL.into(), + ); + let chain = UpParachainCommand::default().configure_chain(&mut cli).await?; + assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); + cli.verify() + } + + #[tokio::test] + async fn prepare_reserve_para_id_extrinsic_works() -> Result<()> { + let mut cli = MockCli::new(); + let chain = UpParachainCommand { + relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), + ..Default::default() + } + .configure_chain(&mut cli) + .await?; + let call_data = prepare_reserve_para_id_extrinsic(&chain)?; + assert_eq!(call_data, decode_call_data("0x4605")?); + Ok(()) + } + + #[tokio::test] + async fn change_working_directory_works() -> Result<()> { + let temp_dir = tempdir()?; + let my_parachain_path = Some(temp_dir.path().to_path_buf()); + change_working_directory(&my_parachain_path)?; + assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + + change_working_directory(&None)?; + assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + Ok(()) + } + + #[tokio::test] + async fn prepare_register_parachain_extrinsic_works() -> Result<()> { + let mut cli = MockCli::new(); + let chain = UpParachainCommand { + relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), + ..Default::default() + } + .configure_chain(&mut cli) + .await?; + // Create a temporary files to act as genesis_state and genesis_code files. + let temp_dir = tempdir()?; + let genesis_state_path = temp_dir.path().join("genesis_state"); + let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); + std::fs::write(&genesis_state_path, "0x1234")?; + std::fs::write(&genesis_code_path, "0x1234")?; + + let call_data = prepare_register_parachain_extrinsic( + &chain, + 2000, + genesis_state_path, + genesis_code_path, + )?; + assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); + Ok(()) + } + + #[tokio::test] + async fn configure_build_spec_works() -> Result<()> { + let mut cli = MockCli::new().expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) + .expect_input( + "Name or path for the plain chain spec file:", "output_file".to_string()) + .expect_input( + "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) + .expect_select( + "Choose the chain type: ", + Some(false), + true, + Some(chain_types()), + ChainType::Development as usize, + ).expect_select( + "Choose the relay your chain will be connecting to: ", + Some(false), + true, + Some(relays()), + RelayChain::PaseoLocal as usize, + ).expect_select( + "Choose the build profile of the binary that should be used: ", + Some(false), + true, + Some(profiles()), + Profile::Release as usize, + ); + + configure_build_spec(2000, &mut cli).await?; + cli.verify()?; + Ok(()) + } + + + fn relays() -> Vec<(String, String)> { + RelayChain::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn chain_types() -> Vec<(String, String)> { + ChainType::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn profiles() -> Vec<(String, String)> { + Profile::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } +} diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 5a9a9a2cb..2d371424c 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -188,7 +188,10 @@ pub fn parse_dispatchable_arguments( .add_custom_parser(custom_parsers::parse_ss58) .parse(&processed_param) .0 - .map_err(|_| Error::ParamProcessingError) + .map_err(|_| { + eprintln!("Failed to parse parameter: {}", processed_param); + Error::ParamProcessingError + }) }) .collect() } From 1bf57e84bc0d78760d940cbd30fd2afdfdc5ee30 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 12 Feb 2025 15:39:25 +0100 Subject: [PATCH 076/117] refactor: small fixes with visibility and removing logs --- crates/pop-cli/src/commands/build/spec.rs | 10 +++++----- crates/pop-cli/src/commands/call/chain.rs | 4 ++-- crates/pop-cli/src/commands/up/parachain.rs | 1 - crates/pop-parachains/src/call/metadata/mod.rs | 5 +---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index 2b580f198..b1b84a8ed 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -193,7 +193,7 @@ impl BuildSpecCommand { /// /// # Arguments /// * `cli` - The cli. - pub async fn configure_build_spec( + pub(crate) async fn configure_build_spec( self, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result { @@ -439,7 +439,7 @@ impl BuildSpecCommand { // Represents the configuration for building a chain specification. #[derive(Debug)] -pub struct BuildSpec { +pub(crate) struct BuildSpec { output_file: PathBuf, profile: Profile, id: u32, @@ -508,12 +508,12 @@ impl BuildSpec { Ok("spec") } - /// Generates chain specification files and returns the paths of the genesis code and genesis - /// state. + /// Generates chain specification files and returns the file paths for the generated genesis + /// code and genesis state files. /// /// # Arguments /// * `cli` - The cli. - pub fn generate_genesis_artifacts( + pub(crate) fn generate_genesis_artifacts( self, cli: &mut impl cli::traits::Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 019ba3996..8698dc317 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -358,7 +358,7 @@ impl CallChainCommand { } // Represents a chain, including its URL, client connection, and available pallets. -pub struct Chain { +pub(crate) struct Chain { // Websocket endpoint of the node. pub(crate) url: Url, // The client used to interact with the chain. @@ -474,7 +474,7 @@ impl Call { } // Sign and submit an extrinsic using wallet integration. -pub async fn submit_extrinsic_with_wallet( +pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, call_data: Vec, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 32ce91007..24424d129 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -299,7 +299,6 @@ mod tests { Ok(()) } - fn relays() -> Vec<(String, String)> { RelayChain::VARIANTS .iter() diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 2d371424c..5a9a9a2cb 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -188,10 +188,7 @@ pub fn parse_dispatchable_arguments( .add_custom_parser(custom_parsers::parse_ss58) .parse(&processed_param) .0 - .map_err(|_| { - eprintln!("Failed to parse parameter: {}", processed_param); - Error::ParamProcessingError - }) + .map_err(|_| Error::ParamProcessingError) }) .collect() } From cd610bd572d8715f681826123b0dca24d170ba2d Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 13 Feb 2025 09:28:07 +0100 Subject: [PATCH 077/117] feat: return events in submit_signed_extrinsic --- crates/pop-cli/src/commands/call/chain.rs | 17 ++++++++++------- crates/pop-cli/src/commands/up/parachain.rs | 2 +- crates/pop-parachains/src/call/mod.rs | 8 ++++---- crates/pop-parachains/src/lib.rs | 1 + 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 8698dc317..2c636fe02 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -12,7 +12,8 @@ use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, sign_and_submit_extrinsic, submit_signed_extrinsic, supported_actions, Action, CallData, - DynamicPayload, Function, OnlineClient, Pallet, Param, Payload, SubstrateConfig, + DynamicPayload, ExtrinsicEvents, Function, OnlineClient, Pallet, Param, Payload, + SubstrateConfig, }; use url::Url; @@ -109,7 +110,9 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli).await + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + .await?; + Ok(()) } else { call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await }; @@ -473,13 +476,13 @@ impl Call { } } -// Sign and submit an extrinsic using wallet integration. +// Sign and submit an extrinsic using wallet integration, then returns the resulting events. pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, call_data: Vec, cli: &mut impl Cli, -) -> Result<()> { +) -> Result> { let maybe_payload = request_signature(call_data, url.to_string()).await?; if let Some(payload) = maybe_payload { cli.success("Signed payload received.")?; @@ -492,11 +495,11 @@ pub(crate) async fn submit_extrinsic_with_wallet( .await .map_err(|err| anyhow!("{}", format!("{err:?}")))?; - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result)); + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); + Ok(result) } else { - display_message("No signed payload received.", false, cli)?; + return Err(anyhow!("No signed payload received.")); } - Ok(()) } // Displays a message to the user, with formatting based on the success status. diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 24424d129..1d94cb075 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -108,7 +108,7 @@ impl UpParachainCommand { /// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_para_id_extrinsic(chain)?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; + let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; Ok(2000) } diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 4569687ec..95a339d05 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -7,6 +7,7 @@ use pop_common::{ }; use sp_core::bytes::{from_hex, to_hex}; use subxt::{ + blocks::ExtrinsicEvents, dynamic::Value, tx::{DynamicPayload, Payload, SubmittableExtrinsic}, OnlineClient, SubstrateConfig, @@ -90,18 +91,17 @@ pub async fn sign_and_submit_extrinsic( pub async fn submit_signed_extrinsic( client: OnlineClient, payload: String, -) -> Result { +) -> Result, Error> { let hex_encoded = from_hex(&payload).map_err(|e| Error::CallDataDecodingError(e.to_string()))?; let extrinsic = SubmittableExtrinsic::from_bytes(client, hex_encoded); - let result = extrinsic + Ok(extrinsic .submit_and_watch() .await .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))? .wait_for_finalized_success() .await - .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?; - Ok(format!("{:?}", result.extrinsic_hash())) + .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?) } /// Encodes the call data for a given extrinsic into a hexadecimal string. diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 50bb6af5b..fce911856 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -34,6 +34,7 @@ pub use new_parachain::instantiate_template_dir; pub use relay::{clear_dmpq, RelayChain}; // External export from subxt. pub use subxt::{ + blocks::ExtrinsicEvents, tx::{DynamicPayload, Payload}, OnlineClient, SubstrateConfig, }; From 386666cd0ac8e4761659a357b547ab9e9443daaa Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 14 Feb 2025 12:41:07 +0100 Subject: [PATCH 078/117] feat: get para_id from event --- Cargo.lock | 1 + Cargo.toml | 1 + crates/pop-cli/src/commands/up/parachain.rs | 24 ++++++++----- crates/pop-parachains/Cargo.toml | 1 + .../src/call/metadata/events.rs | 35 +++++++++++++++++++ .../pop-parachains/src/call/metadata/mod.rs | 1 + crates/pop-parachains/src/errors.rs | 3 ++ crates/pop-parachains/src/lib.rs | 1 + 8 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 crates/pop-parachains/src/call/metadata/events.rs diff --git a/Cargo.lock b/Cargo.lock index f3a264fc3..ffffe857b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9449,6 +9449,7 @@ dependencies = [ "duct", "glob", "indexmap 2.7.0", + "parity-scale-codec", "pop-common", "scale-info", "scale-value", diff --git a/Cargo.toml b/Cargo.toml index 07be639c7..37b62a14f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ subxt = "0.38.0" ink_env = "5.0.0" sp-core = "32.0.0" sp-weights = "31.0.0" +scale = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } scale-info = { version = "2.11.4", default-features = false, features = ["derive"] } scale-value = { version = "0.17.0", default-features = false, features = ["from-string", "parser-ss58"] } contract-build = "5.0.2" diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 1d94cb075..4c67f743e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -8,8 +8,8 @@ use crate::{ use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, - Payload, + construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, + parse_chain_metadata, set_up_client, Action, Payload, }; use std::path::PathBuf; @@ -50,8 +50,8 @@ impl UpParachainCommand { match reserve_para_id(&chain, cli).await { Ok(id) => id, Err(e) => { - cli.outro_cancel(&format!("Failed to reserve parachain ID: {}", e))?; - return Err(e); + cli.outro_cancel(&format!("{}", e))?; + return Ok(()); }, } }, @@ -60,12 +60,12 @@ impl UpParachainCommand { match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => (state, code), _ => { - cli.info("Generating the chain spec for your parachain, some extra information is needed:")?; + cli.info("Generating the chain spec for your parachain.")?; match generate_spec_files(para_id, self.path, cli).await { Ok(files) => files, Err(e) => { cli.outro_cancel(&format!("Failed to generate spec files: {}", e))?; - return Err(e); + return Ok(()); }, } }, @@ -74,7 +74,7 @@ impl UpParachainCommand { if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await { cli.outro_cancel(&format!("Failed to register parachain: {}", e))?; - return Err(e); + return Ok(()); } cli.outro("Parachain deployment complete.")?; @@ -108,8 +108,14 @@ impl UpParachainCommand { /// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_para_id_extrinsic(chain)?; - let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(2000) + let events = submit_extrinsic_with_wallet(&chain.client.clone(), &chain.url, call_data, cli) + .await + .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; + let para_id = extract_para_id_from_event(&events).await.map_err(|_| { + anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with --id.") + })?; + cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; + Ok(para_id) } /// Constructs an extrinsic for reserving a parachain ID. diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index 619b3336e..a7bd5b4d8 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -25,6 +25,7 @@ url.workspace = true askama.workspace = true indexmap.workspace = true +scale.workspace = true scale-info.workspace = true scale-value.workspace = true sp-core.workspace = true diff --git a/crates/pop-parachains/src/call/metadata/events.rs b/crates/pop-parachains/src/call/metadata/events.rs new file mode 100644 index 000000000..f15957bcd --- /dev/null +++ b/crates/pop-parachains/src/call/metadata/events.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0 + +use scale::{Decode, Encode}; +use subxt::{ + blocks::ExtrinsicEvents, + events::StaticEvent, + ext::{scale_decode::DecodeAsType, scale_encode::EncodeAsType}, + SubstrateConfig, +}; + +use crate::Error; + +#[derive(Debug, Encode, Decode, DecodeAsType, EncodeAsType)] +#[decode_as_type(crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub struct Reserved { + pub para_id: u32, +} +impl StaticEvent for Reserved { + const PALLET: &'static str = "Registrar"; + const EVENT: &'static str = "Reserved"; +} + +/// Extracts the `para_id` field from a `Reserved` event. +/// +/// # Arguments +/// * `events` - The extrinsic events from a transaction. +pub async fn extract_para_id_from_event( + events: &ExtrinsicEvents, +) -> Result { + let reserved_event = events.find_first::()?; + reserved_event + .map(|event| event.para_id) + .ok_or(Error::EventNotFound("Reserved".to_string())) +} diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 5a9a9a2cb..38f0bfaa7 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -7,6 +7,7 @@ use std::fmt::{Display, Formatter}; use subxt::{dynamic::Value, utils::to_hex, Metadata, OnlineClient, SubstrateConfig}; pub mod action; +pub mod events; pub mod params; /// Represents a pallet in the blockchain, including its dispatchable functions. diff --git a/crates/pop-parachains/src/errors.rs b/crates/pop-parachains/src/errors.rs index 2dd8805ac..0288c4415 100644 --- a/crates/pop-parachains/src/errors.rs +++ b/crates/pop-parachains/src/errors.rs @@ -27,6 +27,9 @@ pub enum Error { CurrentDirAccess, #[error("Failed to parse the endowment value")] EndowmentError, + /// The specified event was not found in the extrinsic events. + #[error("Event {0} not found.")] + EventNotFound(String), /// An error occurred during the submission of an extrinsic. #[error("Extrinsic submission error: {0}")] ExtrinsicSubmissionError(String), diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index fce911856..9e71dcbfe 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -21,6 +21,7 @@ pub use call::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, metadata::{ action::{supported_actions, Action}, + events::extract_para_id_from_event, find_dispatchable_by_name, find_pallet_by_name, params::Param, parse_chain_metadata, Function, Pallet, From 2971f8a3376404602df902c47f96e9a177f0c738 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 18:30:39 +0100 Subject: [PATCH 079/117] test: fix detects_parachain_correctly --- crates/pop-cli/src/commands/up/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index fdb7eb744..87b8ddbeb 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -155,9 +155,12 @@ mod tests { }; instantiate_template_dir(&Parachain::Standard, &project_path, None, config)?; - let args = create_up_args(project_path)?; - let mut cli = - MockCli::new().expect_warning("Deploy a parachain"); + let mut args = create_up_args(project_path)?; + args.parachain.relay_url = Some(Url::parse("wss://polkadot-rpc.publicnode.com")?); + args.parachain.id = Some(2000); + args.parachain.genesis_code = Some(PathBuf::from("path/to/genesis")); + args.parachain.genesis_state = Some(PathBuf::from("path/to/state")); + let mut cli = MockCli::new(); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); cli.verify() } From ef53c4fb13fc9ea9fda7959956970c8c23f62387 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 22:53:29 +0100 Subject: [PATCH 080/117] refactor: improve docs and code --- crates/pop-cli/src/commands/build/spec.rs | 54 ++++++++++----------- crates/pop-cli/src/commands/call/chain.rs | 20 ++++++-- crates/pop-cli/src/commands/up/parachain.rs | 4 +- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index b1b84a8ed..269627715 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -508,6 +508,33 @@ impl BuildSpec { Ok("spec") } + /// Generates plain and raw chain specification files. + fn generate_chain_spec( + &self, + binary_path: &PathBuf, + spinner: &ProgressBar, + ) -> anyhow::Result { + let BuildSpec { output_file, chain, .. } = self; + spinner.start("Generating chain specification..."); + + // Generate plain chain spec. + generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; + // Customize spec based on input. + self.customize()?; + + // Generate raw spec. + spinner.set_message("Generating raw chain specification..."); + let spec_name = &output_file + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(DEFAULT_SPEC_NAME) + .trim_end_matches(".json"); + let raw_spec_name = format!("{spec_name}-raw.json"); + let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; + + Ok(raw_chain_spec) + } + /// Generates chain specification files and returns the file paths for the generated genesis /// code and genesis state files. /// @@ -537,33 +564,6 @@ impl BuildSpec { Ok((genesis_state, genesis_code)) } - /// Generates plain and raw chain specification files. - fn generate_chain_spec( - &self, - binary_path: &PathBuf, - spinner: &ProgressBar, - ) -> anyhow::Result { - let BuildSpec { output_file, chain, .. } = self; - spinner.start("Generating chain specification..."); - - // Generate plain chain spec. - generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; - // Customize spec based on input. - self.customize()?; - - // Generate raw spec. - spinner.set_message("Generating raw chain specification..."); - let spec_name = &output_file - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or(DEFAULT_SPEC_NAME) - .trim_end_matches(".json"); - let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; - - Ok(raw_chain_spec) - } - // Customize a chain specification. fn customize(&self) -> anyhow::Result<()> { let mut chain_spec = ChainSpec::from(&self.output_file)?; diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 2c636fe02..8739d84dc 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -110,9 +110,15 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) - .await?; - Ok(()) + match submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + .await + { + Ok(_) => Ok(()), + Err(e) => { + display_message(&e.to_string(), false, &mut cli)?; + break; + }, + } } else { call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await }; @@ -476,7 +482,13 @@ impl Call { } } -// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +/// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +/// +/// # Arguments +/// * `client` - The client used to interact with the chain. +/// * `url` - Endpoint of the node. +/// * `call_data` - The call data to be signed. +/// * `cli` - The CLI implementation to be used. pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 4c67f743e..bc282427d 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -27,10 +27,10 @@ pub struct UpParachainCommand { /// Parachain ID to use. If not specified, a new ID will be reserved. #[arg(short, long)] pub(crate) id: Option, - /// Path to the genesis state file. + /// Path to the genesis state file. If not specified, it will be generated. #[arg(short = 'G', long = "genesis-state")] pub(crate) genesis_state: Option, - /// Path to the genesis code file. + /// Path to the genesis code file. If not specified, it will be generated. #[arg(short = 'C', long = "genesis-code")] pub(crate) genesis_code: Option, /// Websocket endpoint of the relay chain. From c3b9b7080adb9879447287493c5cf08b9db9192c Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 17 Feb 2025 23:24:26 +0100 Subject: [PATCH 081/117] test: fix change_working_directory_works --- crates/pop-cli/src/commands/up/parachain.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index bc282427d..6df399891 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -237,6 +237,7 @@ mod tests { #[tokio::test] async fn change_working_directory_works() -> Result<()> { + let original_dir = std::env::current_dir()?; let temp_dir = tempdir()?; let my_parachain_path = Some(temp_dir.path().to_path_buf()); change_working_directory(&my_parachain_path)?; @@ -244,6 +245,9 @@ mod tests { change_working_directory(&None)?; assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + + // Reset working directory back to original + change_working_directory(&Some(original_dir))?; Ok(()) } From 1aa4bad13769efbcb9e3e5e21e34b0f542214b29 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 18 Feb 2025 09:06:06 +0100 Subject: [PATCH 082/117] fix: clippy warnings --- crates/pop-cli/src/commands/build/spec.rs | 6 +++--- crates/pop-cli/src/commands/call/chain.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 17 ++++++----------- crates/pop-parachains/src/call/mod.rs | 4 ++-- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index 269627715..eed7eb396 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -511,14 +511,14 @@ impl BuildSpec { /// Generates plain and raw chain specification files. fn generate_chain_spec( &self, - binary_path: &PathBuf, + binary_path: &Path, spinner: &ProgressBar, ) -> anyhow::Result { let BuildSpec { output_file, chain, .. } = self; spinner.start("Generating chain specification..."); // Generate plain chain spec. - generate_plain_chain_spec(&binary_path, output_file, self.default_bootnode, chain)?; + generate_plain_chain_spec(binary_path, output_file, self.default_bootnode, chain)?; // Customize spec based on input. self.customize()?; @@ -530,7 +530,7 @@ impl BuildSpec { .unwrap_or(DEFAULT_SPEC_NAME) .trim_end_matches(".json"); let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; + let raw_chain_spec = generate_raw_chain_spec(binary_path, output_file, &raw_spec_name)?; Ok(raw_chain_spec) } diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 8739d84dc..c6835f738 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -510,7 +510,7 @@ pub(crate) async fn submit_extrinsic_with_wallet( spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); Ok(result) } else { - return Err(anyhow!("No signed payload received.")); + Err(anyhow!("No signed payload received.")) } } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 6df399891..f3f061699 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -50,7 +50,7 @@ impl UpParachainCommand { match reserve_para_id(&chain, cli).await { Ok(id) => id, Err(e) => { - cli.outro_cancel(&format!("{}", e))?; + cli.outro_cancel(format!("{}", e))?; return Ok(()); }, } @@ -64,7 +64,7 @@ impl UpParachainCommand { match generate_spec_files(para_id, self.path, cli).await { Ok(files) => files, Err(e) => { - cli.outro_cancel(&format!("Failed to generate spec files: {}", e))?; + cli.outro_cancel(format!("Failed to generate spec files: {}", e))?; return Ok(()); }, } @@ -73,7 +73,7 @@ impl UpParachainCommand { cli.info("Registering a parachain ID")?; if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await { - cli.outro_cancel(&format!("Failed to register parachain: {}", e))?; + cli.outro_cancel(format!("Failed to register parachain: {}", e))?; return Ok(()); } @@ -151,14 +151,9 @@ fn change_working_directory(path: &Option) -> Result<()> { /// Configures the chain specification requirements. async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result { - Ok(BuildSpecCommand { - id: Some(id), - genesis_code: true, - genesis_state: true, - ..Default::default() - } - .configure_build_spec(cli) - .await?) + BuildSpecCommand { id: Some(id), genesis_code: true, genesis_state: true, ..Default::default() } + .configure_build_spec(cli) + .await } /// Registers a parachain by submitting an extrinsic. diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 95a339d05..7ad7bcc07 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -95,13 +95,13 @@ pub async fn submit_signed_extrinsic( let hex_encoded = from_hex(&payload).map_err(|e| Error::CallDataDecodingError(e.to_string()))?; let extrinsic = SubmittableExtrinsic::from_bytes(client, hex_encoded); - Ok(extrinsic + extrinsic .submit_and_watch() .await .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))? .wait_for_finalized_success() .await - .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e)))?) + .map_err(|e| Error::ExtrinsicSubmissionError(format!("{:?}", e))) } /// Encodes the call data for a given extrinsic into a hexadecimal string. From 81de3a74cbf65e8c1440b513553166140b1cd5af Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 09:52:47 +0100 Subject: [PATCH 083/117] refactor: move submit_extrinsic_with_wallet in a common file --- crates/pop-cli/src/commands/call/chain.rs | 39 ++------------------- crates/pop-cli/src/commands/up/parachain.rs | 3 +- crates/pop-cli/src/common/wallet.rs | 29 +++++++++++++++ 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index c6835f738..f418b6a90 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -4,16 +4,15 @@ use std::path::Path; use crate::{ cli::{self, traits::*}, - common::wallet::{prompt_to_use_wallet, request_signature}, + common::wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, - sign_and_submit_extrinsic, submit_signed_extrinsic, supported_actions, Action, CallData, - DynamicPayload, ExtrinsicEvents, Function, OnlineClient, Pallet, Param, Payload, - SubstrateConfig, + sign_and_submit_extrinsic, supported_actions, Action, CallData, DynamicPayload, Function, + OnlineClient, Pallet, Param, Payload, SubstrateConfig, }; use url::Url; @@ -482,38 +481,6 @@ impl Call { } } -/// Sign and submit an extrinsic using wallet integration, then returns the resulting events. -/// -/// # Arguments -/// * `client` - The client used to interact with the chain. -/// * `url` - Endpoint of the node. -/// * `call_data` - The call data to be signed. -/// * `cli` - The CLI implementation to be used. -pub(crate) async fn submit_extrinsic_with_wallet( - client: &OnlineClient, - url: &Url, - call_data: Vec, - cli: &mut impl Cli, -) -> Result> { - let maybe_payload = request_signature(call_data, url.to_string()).await?; - if let Some(payload) = maybe_payload { - cli.success("Signed payload received.")?; - let spinner = cliclack::spinner(); - spinner.start( - "Submitting the extrinsic and then waiting for finalization, please be patient...", - ); - - let result = submit_signed_extrinsic(client.clone(), payload) - .await - .map_err(|err| anyhow!("{}", format!("{err:?}")))?; - - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); - Ok(result) - } else { - Err(anyhow!("No signed payload received.")) - } -} - // Displays a message to the user, with formatting based on the success status. fn display_message(message: &str, success: bool, cli: &mut impl Cli) -> Result<()> { if success { diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index f3f061699..5d45ff17a 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,8 +2,9 @@ use crate::{ build::spec::{BuildSpec, BuildSpecCommand}, - call::chain::{submit_extrinsic_with_wallet, Chain}, + call::chain::Chain, cli::traits::*, + common::wallet::submit_extrinsic_with_wallet, }; use anyhow::{anyhow, Result}; use clap::Args; diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 1aedd0294..5b8f5a83e 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -4,7 +4,10 @@ use crate::{ cli::traits::Cli, wallet_integration::{FrontendFromString, TransactionData, WalletIntegrationManager}, }; +use anyhow::{anyhow, Result}; use cliclack::{log, spinner}; +use pop_parachains::{submit_signed_extrinsic, ExtrinsicEvents, OnlineClient, SubstrateConfig}; +use url::Url; /// The prompt to ask the user if they want to use the wallet for signing. pub const USE_WALLET_PROMPT: &str = "Do you want to use your browser wallet to sign the extrinsic? (Selecting 'No' will prompt you to manually enter the secret key URI for signing, e.g., '//Alice')"; @@ -65,3 +68,29 @@ pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { Ok(false) } } + +// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +pub(crate) async fn submit_extrinsic_with_wallet( + client: &OnlineClient, + url: &Url, + call_data: Vec, + cli: &mut impl Cli, +) -> Result> { + let maybe_payload = request_signature(call_data, url.to_string()).await?; + if let Some(payload) = maybe_payload { + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner.start( + "Submitting the extrinsic and then waiting for finalization, please be patient...", + ); + + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); + Ok(result) + } else { + Err(anyhow!("No signed payload received.")) + } +} From 66019ed69b62d1174ca0e006a6faf11e7b3b40b6 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 11:57:21 +0100 Subject: [PATCH 084/117] refactor: remove unnecesary code --- crates/pop-cli/src/commands/build/spec.rs | 5 ++--- crates/pop-cli/src/commands/call/chain.rs | 10 ++-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index eed7eb396..fda8379c7 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -530,9 +530,8 @@ impl BuildSpec { .unwrap_or(DEFAULT_SPEC_NAME) .trim_end_matches(".json"); let raw_spec_name = format!("{spec_name}-raw.json"); - let raw_chain_spec = generate_raw_chain_spec(binary_path, output_file, &raw_spec_name)?; - - Ok(raw_chain_spec) + generate_raw_chain_spec(binary_path, output_file, &raw_spec_name) + .map_err(anyhow::Error::from) } /// Generates chain specification files and returns the file paths for the generated genesis diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index f418b6a90..fac36c325 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -109,15 +109,9 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - match submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) .await - { - Ok(_) => Ok(()), - Err(e) => { - display_message(&e.to_string(), false, &mut cli)?; - break; - }, - } + .map(|_| ()) // Mapping to `()` since we don't need events returned } else { call.submit_extrinsic(&chain.client, &chain.url, xt, &mut cli).await }; From dfa5da59aa6bf16f1c5e452df445bfb8d8155782 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 14:55:45 +0100 Subject: [PATCH 085/117] refactor: UpChainCommand structure --- crates/pop-cli/src/commands/up/mod.rs | 6 +- crates/pop-cli/src/commands/up/parachain.rs | 167 ++++++++++---------- 2 files changed, 90 insertions(+), 83 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 87b8ddbeb..cf86fad92 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -29,7 +29,7 @@ pub(crate) struct UpArgs { #[command(flatten)] #[cfg(feature = "parachain")] - pub(crate) parachain: parachain::UpParachainCommand, + pub(crate) parachain: parachain::UpChainCommand, #[command(flatten)] #[cfg(feature = "contract")] @@ -93,7 +93,7 @@ impl Command { #[cfg(test)] mod tests { - use super::{contract::UpContractCommand, parachain::UpParachainCommand, *}; + use super::{contract::UpContractCommand, parachain::UpChainCommand, *}; use cli::MockCli; use duct::cmd; @@ -122,7 +122,7 @@ mod tests { skip_confirm: false, valid: false, }, - parachain: UpParachainCommand::default(), + parachain: UpChainCommand::default(), command: None, }) } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 5d45ff17a..83f02865d 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -12,16 +12,15 @@ use pop_parachains::{ construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, Payload, }; - use std::path::PathBuf; use url::Url; const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; -const HELP_HEADER: &str = "Parachain deployment options"; +const HELP_HEADER: &str = "Chain deployment options"; #[derive(Args, Clone, Default)] #[clap(next_help_heading = HELP_HEADER)] -pub struct UpParachainCommand { +pub struct UpChainCommand { /// Path to the chain directory. #[clap(skip)] pub(crate) path: Option, @@ -39,49 +38,32 @@ pub struct UpParachainCommand { pub(crate) relay_url: Option, } -impl UpParachainCommand { +impl UpChainCommand { /// Executes the command. pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { - cli.intro("Deploy a parachain")?; - let chain = self.configure_chain(cli).await?; - let para_id = match self.id { - Some(id) => id, - None => { - cli.info("Reserving a parachain ID...")?; - match reserve_para_id(&chain, cli).await { - Ok(id) => id, - Err(e) => { - cli.outro_cancel(format!("{}", e))?; - return Ok(()); - }, - } + cli.intro("Deploy a chain")?; + let chain_config = match self.prepare_chain_for_registration(cli).await { + Ok(chain) => chain, + Err(e) => { + cli.outro_cancel(format!("{}", e))?; + return Ok(()); }, }; - let (genesis_state, genesis_code) = - match (self.genesis_state.clone(), self.genesis_code.clone()) { - (Some(state), Some(code)) => (state, code), - _ => { - cli.info("Generating the chain spec for your parachain.")?; - match generate_spec_files(para_id, self.path, cli).await { - Ok(files) => files, - Err(e) => { - cli.outro_cancel(format!("Failed to generate spec files: {}", e))?; - return Ok(()); - }, - } - }, - }; - cli.info("Registering a parachain ID")?; - if let Err(e) = register_parachain(&chain, para_id, genesis_state, genesis_code, cli).await - { - cli.outro_cancel(format!("Failed to register parachain: {}", e))?; - return Ok(()); + match chain_config.register_parachain(cli).await { + Ok(_) => cli.success("Chain deployed successfully")?, + Err(e) => cli.outro_cancel(format!("{}", e))?, } - - cli.outro("Parachain deployment complete.")?; Ok(()) } + // Prepares the chain for registration by setting up its configuration. + async fn prepare_chain_for_registration(self, cli: &mut impl Cli) -> Result { + let chain = self.configure_chain(cli).await?; + let para_id = self.resolve_parachain_id(&chain, cli).await?; + let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; + Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) + } + // Configures the chain by resolving the URL and fetching its metadata. async fn configure_chain(&self, cli: &mut impl Cli) -> Result { // Resolve url. @@ -104,6 +86,63 @@ impl UpParachainCommand { })?; Ok(Chain { url, client, pallets }) } + // Resolves the parachain ID, reserving a new one if necessary. + async fn resolve_parachain_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + match self.id { + Some(id) => Ok(id), + None => { + cli.info("Reserving a parachain ID...")?; + return reserve_para_id(&chain, cli).await; + }, + } + } + // Resolves the genesis state and code files, generating them if necessary. + async fn resolve_genesis_files( + &self, + para_id: u32, + cli: &mut impl Cli, + ) -> Result<(PathBuf, PathBuf)> { + match (self.genesis_state.clone(), self.genesis_code.clone()) { + (Some(state), Some(code)) => Ok((state, code)), + _ => { + cli.info("Generating the chain spec for your parachain.")?; + return generate_spec_files(para_id, self.path.clone(), cli).await; + }, + } + } +} + +// Represents the configuration for deploying a chain. +pub(crate) struct UpChain { + id: u32, + genesis_state: PathBuf, + genesis_code: PathBuf, + chain: Chain, +} +impl UpChain { + // Registers a parachain by submitting an extrinsic. + async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { + cli.info("Registering a parachain ID")?; + let call_data = self.prepare_register_parachain_extrinsic()?; + submit_extrinsic_with_wallet(&self.chain.client, &self.chain.url, call_data, cli).await?; + Ok(()) + } + + // Constructs an extrinsic for registering a parachain. + fn prepare_register_parachain_extrinsic(&self) -> Result> { + let UpChain { id, genesis_code, genesis_state, chain } = self; + let ex = find_dispatchable_by_name( + &chain.pallets, + Action::Register.pallet_name(), + Action::Register.function_name(), + )?; + let state = std::fs::read_to_string(genesis_state) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let code = std::fs::read_to_string(genesis_code) + .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + Ok(xt.encode_call_data(&chain.client.metadata())?) + } } /// Reserves a parachain ID by submitting an extrinsic. @@ -157,39 +196,6 @@ async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result .await } -/// Registers a parachain by submitting an extrinsic. -async fn register_parachain( - chain: &Chain, - id: u32, - genesis_state: PathBuf, - genesis_code: PathBuf, - cli: &mut impl Cli, -) -> Result<()> { - let call_data = prepare_register_parachain_extrinsic(chain, id, genesis_state, genesis_code)?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli).await?; - Ok(()) -} - -/// Constructs an extrinsic for registering a parachain. -fn prepare_register_parachain_extrinsic( - chain: &Chain, - id: u32, - genesis_state: PathBuf, - genesis_code: PathBuf, -) -> Result> { - let ex = find_dispatchable_by_name( - &chain.pallets, - Action::Register.pallet_name(), - Action::Register.function_name(), - )?; - let state = std::fs::read_to_string(genesis_state) - .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; - let code = std::fs::read_to_string(genesis_code) - .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; - let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; - Ok(xt.encode_call_data(&chain.client.metadata())?) -} - #[cfg(test)] mod tests { use super::*; @@ -212,7 +218,7 @@ mod tests { "Enter the relay chain node URL to deploy your parachain", POLKADOT_NETWORK_URL.into(), ); - let chain = UpParachainCommand::default().configure_chain(&mut cli).await?; + let chain = UpChainCommand::default().configure_chain(&mut cli).await?; assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); cli.verify() } @@ -220,7 +226,7 @@ mod tests { #[tokio::test] async fn prepare_reserve_para_id_extrinsic_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpParachainCommand { + let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), ..Default::default() } @@ -250,7 +256,7 @@ mod tests { #[tokio::test] async fn prepare_register_parachain_extrinsic_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpParachainCommand { + let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), ..Default::default() } @@ -263,12 +269,13 @@ mod tests { std::fs::write(&genesis_state_path, "0x1234")?; std::fs::write(&genesis_code_path, "0x1234")?; - let call_data = prepare_register_parachain_extrinsic( - &chain, - 2000, - genesis_state_path, - genesis_code_path, - )?; + let call_data = UpChain { + id: 2000, + genesis_state: genesis_state_path, + genesis_code: genesis_code_path, + chain, + } + .prepare_register_parachain_extrinsic()?; assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); Ok(()) } From a78f51ad00a71ae0f9d8718e34d20fbb2c9c133f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 15:16:29 +0100 Subject: [PATCH 086/117] test: adjust tests to refactored struct --- crates/pop-cli/src/commands/up/parachain.rs | 157 +++++++------------- 1 file changed, 54 insertions(+), 103 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 83f02865d..3ede2e410 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::{BuildSpec, BuildSpecCommand}, + build::spec::BuildSpecCommand, call::chain::Chain, cli::traits::*, common::wallet::submit_extrinsic_with_wallet, @@ -175,51 +175,54 @@ async fn generate_spec_files( path: Option, cli: &mut impl Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { - change_working_directory(&path)?; - let build_spec = configure_build_spec(id, cli).await?; - build_spec.generate_genesis_artifacts(cli) -} - -/// Changes the working directory if a path is provided, ensuring the build spec process runs in the -/// correct context. -fn change_working_directory(path: &Option) -> Result<()> { + // Changes the working directory if a path is provided to ensure the build spec process runs in + // the correct context. if let Some(path) = path { std::env::set_current_dir(path)?; } - Ok(()) -} - -/// Configures the chain specification requirements. -async fn configure_build_spec(id: u32, cli: &mut impl Cli) -> Result { - BuildSpecCommand { id: Some(id), genesis_code: true, genesis_state: true, ..Default::default() } - .configure_build_spec(cli) - .await + let build_spec = BuildSpecCommand { + id: Some(id), + genesis_code: true, + genesis_state: true, + ..Default::default() + } + .configure_build_spec(cli) + .await?; + build_spec.generate_genesis_artifacts(cli) } #[cfg(test)] mod tests { use super::*; - use crate::{ - build::spec::{ChainType, RelayChain}, - cli::MockCli, - }; - use pop_common::Profile; + use crate::cli::MockCli; use pop_parachains::decode_call_data; - use std::{env, fs}; - use strum::{EnumMessage, VariantArray}; + use std::fs; use tempfile::tempdir; use url::Url; const POLKADOT_NETWORK_URL: &str = "wss://polkadot-rpc.publicnode.com"; + const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; #[tokio::test] - async fn configure_chain_works() -> Result<()> { + async fn prepare_chain_for_registration_works() -> Result<()> { let mut cli = MockCli::new().expect_input( "Enter the relay chain node URL to deploy your parachain", POLKADOT_NETWORK_URL.into(), ); - let chain = UpChainCommand::default().configure_chain(&mut cli).await?; - assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); + let (genesis_state, genesis_code) = create_temp_genesis_files()?; + let chain_config = UpChainCommand { + id: Some(2000), + genesis_state: Some(genesis_state.clone()), + genesis_code: Some(genesis_code.clone()), + ..Default::default() + } + .prepare_chain_for_registration(&mut cli) + .await?; + + assert_eq!(chain_config.id, 2000); + assert_eq!(chain_config.genesis_code, genesis_code); + assert_eq!(chain_config.genesis_state, genesis_state); + assert_eq!(chain_config.chain.url, Url::parse(POLKADOT_NETWORK_URL)?); cli.verify() } @@ -238,19 +241,23 @@ mod tests { } #[tokio::test] - async fn change_working_directory_works() -> Result<()> { - let original_dir = std::env::current_dir()?; - let temp_dir = tempdir()?; - let my_parachain_path = Some(temp_dir.path().to_path_buf()); - change_working_directory(&my_parachain_path)?; - assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); - - change_working_directory(&None)?; - assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(temp_dir.path())?); + async fn register_parachain_fails_wrong_chain() -> Result<()> { + let mut cli = MockCli::new() + .expect_intro("Deploy a chain") + .expect_info("Registering a parachain ID") + .expect_outro_cancel("Failed to find the pallet Registrar"); + let (genesis_state, genesis_code) = create_temp_genesis_files()?; + UpChainCommand { + id: Some(2000), + genesis_state: Some(genesis_state.clone()), + genesis_code: Some(genesis_code.clone()), + relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + path: None, + } + .execute(&mut cli) + .await?; - // Reset working directory back to original - change_working_directory(&Some(original_dir))?; - Ok(()) + cli.verify() } #[tokio::test] @@ -280,71 +287,15 @@ mod tests { Ok(()) } - #[tokio::test] - async fn configure_build_spec_works() -> Result<()> { - let mut cli = MockCli::new().expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) - .expect_input( - "Name or path for the plain chain spec file:", "output_file".to_string()) - .expect_input( - "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) - .expect_select( - "Choose the chain type: ", - Some(false), - true, - Some(chain_types()), - ChainType::Development as usize, - ).expect_select( - "Choose the relay your chain will be connecting to: ", - Some(false), - true, - Some(relays()), - RelayChain::PaseoLocal as usize, - ).expect_select( - "Choose the build profile of the binary that should be used: ", - Some(false), - true, - Some(profiles()), - Profile::Release as usize, - ); - - configure_build_spec(2000, &mut cli).await?; - cli.verify()?; - Ok(()) - } - - fn relays() -> Vec<(String, String)> { - RelayChain::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } + // Creates temporary files to act as `genesis_state` and `genesis_code` files. + fn create_temp_genesis_files() -> Result<(PathBuf, PathBuf)> { + let temp_dir = tempdir()?; // Create a temporary directory + let genesis_state_path = temp_dir.path().join("genesis_state"); + let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); - fn chain_types() -> Vec<(String, String)> { - ChainType::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } + fs::write(&genesis_state_path, "0x1234")?; + fs::write(&genesis_code_path, "0x1234")?; - fn profiles() -> Vec<(String, String)> { - Profile::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() + Ok((genesis_state_path, genesis_code_path)) } } From 98176fa765b7bc08d2d2b4bb37c8cf81549e2c9f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 15:33:32 +0100 Subject: [PATCH 087/117] refactor: renaming prepare_register_parachain_call_data and prepare_rerve_parachain_call_data --- crates/pop-cli/src/commands/build/spec.rs | 2 +- crates/pop-cli/src/commands/call/chain.rs | 6 ++--- crates/pop-cli/src/commands/up/parachain.rs | 30 ++++++++++----------- crates/pop-parachains/src/errors.rs | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index fda8379c7..c78a5c30c 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -508,7 +508,7 @@ impl BuildSpec { Ok("spec") } - /// Generates plain and raw chain specification files. + /// Generates plain and raw chain specification files, and returns the path to the latter. fn generate_chain_spec( &self, binary_path: &Path, diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index fac36c325..c90ea66f2 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -371,8 +371,8 @@ pub(crate) struct Chain { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. -#[derive(Clone)] -struct Call { +#[derive(Clone, Default)] +pub(crate) struct Call { /// The dispatchable function to execute. function: Function, /// The dispatchable function arguments, encoded as strings. @@ -393,7 +393,7 @@ struct Call { impl Call { // Prepares the extrinsic. - fn prepare_extrinsic( + pub(crate) fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 3ede2e410..5904f474b 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, - call::chain::Chain, - cli::traits::*, + build::spec::BuildSpecCommand, call::chain::Chain, cli::traits::*, common::wallet::submit_extrinsic_with_wallet, }; use anyhow::{anyhow, Result}; @@ -123,13 +121,13 @@ impl UpChain { // Registers a parachain by submitting an extrinsic. async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { cli.info("Registering a parachain ID")?; - let call_data = self.prepare_register_parachain_extrinsic()?; + let call_data = self.prepare_register_parachain_call_data()?; submit_extrinsic_with_wallet(&self.chain.client, &self.chain.url, call_data, cli).await?; Ok(()) } - // Constructs an extrinsic for registering a parachain. - fn prepare_register_parachain_extrinsic(&self) -> Result> { + // Prepares and returns the encoded call data for registering a parachain. + fn prepare_register_parachain_call_data(&self) -> Result> { let UpChain { id, genesis_code, genesis_state, chain } = self; let ex = find_dispatchable_by_name( &chain.pallets, @@ -145,10 +143,10 @@ impl UpChain { } } -/// Reserves a parachain ID by submitting an extrinsic. +// Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { - let call_data = prepare_reserve_para_id_extrinsic(chain)?; - let events = submit_extrinsic_with_wallet(&chain.client.clone(), &chain.url, call_data, cli) + let call_data = prepare_reserve_parachain_call_data(chain)?; + let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; let para_id = extract_para_id_from_event(&events).await.map_err(|_| { @@ -158,8 +156,8 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { Ok(para_id) } -/// Constructs an extrinsic for reserving a parachain ID. -fn prepare_reserve_para_id_extrinsic(chain: &Chain) -> Result> { +// Prepares and returns the encoded call data for reserving a parachain ID. +fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { let function = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), @@ -169,7 +167,7 @@ fn prepare_reserve_para_id_extrinsic(chain: &Chain) -> Result> { Ok(xt.encode_call_data(&chain.client.metadata())?) } -/// Generates chain spec files for the parachain. +// Generates chain spec files for the parachain. async fn generate_spec_files( id: u32, path: Option, @@ -227,7 +225,7 @@ mod tests { } #[tokio::test] - async fn prepare_reserve_para_id_extrinsic_works() -> Result<()> { + async fn prepare_reserve_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -235,7 +233,7 @@ mod tests { } .configure_chain(&mut cli) .await?; - let call_data = prepare_reserve_para_id_extrinsic(&chain)?; + let call_data = prepare_reserve_parachain_call_data(&chain)?; assert_eq!(call_data, decode_call_data("0x4605")?); Ok(()) } @@ -261,7 +259,7 @@ mod tests { } #[tokio::test] - async fn prepare_register_parachain_extrinsic_works() -> Result<()> { + async fn prepare_register_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = UpChainCommand { relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -282,7 +280,7 @@ mod tests { genesis_code: genesis_code_path, chain, } - .prepare_register_parachain_extrinsic()?; + .prepare_register_parachain_call_data()?; assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); Ok(()) } diff --git a/crates/pop-parachains/src/errors.rs b/crates/pop-parachains/src/errors.rs index 0288c4415..889bd2be9 100644 --- a/crates/pop-parachains/src/errors.rs +++ b/crates/pop-parachains/src/errors.rs @@ -27,7 +27,7 @@ pub enum Error { CurrentDirAccess, #[error("Failed to parse the endowment value")] EndowmentError, - /// The specified event was not found in the extrinsic events. + /// The specified event was not found. #[error("Event {0} not found.")] EventNotFound(String), /// An error occurred during the submission of an extrinsic. From 5535b9c50397c1d5f44e467e74243b80d375a004 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 15:41:30 +0100 Subject: [PATCH 088/117] refactor: move events module --- crates/pop-cli/src/commands/up/parachain.rs | 2 +- crates/pop-parachains/src/call/metadata/mod.rs | 1 - crates/pop-parachains/src/lib.rs | 2 +- crates/pop-parachains/src/{call/metadata => utils}/events.rs | 4 +--- crates/pop-parachains/src/utils/mod.rs | 1 + 5 files changed, 4 insertions(+), 6 deletions(-) rename crates/pop-parachains/src/{call/metadata => utils}/events.rs (88%) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 5904f474b..3f97df65e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -149,7 +149,7 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; - let para_id = extract_para_id_from_event(&events).await.map_err(|_| { + let para_id = extract_para_id_from_event(&events).map_err(|_| { anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with --id.") })?; cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; diff --git a/crates/pop-parachains/src/call/metadata/mod.rs b/crates/pop-parachains/src/call/metadata/mod.rs index 38f0bfaa7..5a9a9a2cb 100644 --- a/crates/pop-parachains/src/call/metadata/mod.rs +++ b/crates/pop-parachains/src/call/metadata/mod.rs @@ -7,7 +7,6 @@ use std::fmt::{Display, Formatter}; use subxt::{dynamic::Value, utils::to_hex, Metadata, OnlineClient, SubstrateConfig}; pub mod action; -pub mod events; pub mod params; /// Represents a pallet in the blockchain, including its dispatchable functions. diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 9e71dcbfe..4fe7003b1 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -21,7 +21,6 @@ pub use call::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, metadata::{ action::{supported_actions, Action}, - events::extract_para_id_from_event, find_dispatchable_by_name, find_pallet_by_name, params::Param, parse_chain_metadata, Function, Pallet, @@ -33,6 +32,7 @@ pub use indexmap::IndexSet; pub use new_pallet::{create_pallet_template, new_pallet_options::*, TemplatePalletConfig}; pub use new_parachain::instantiate_template_dir; pub use relay::{clear_dmpq, RelayChain}; +pub use utils::events::extract_para_id_from_event; // External export from subxt. pub use subxt::{ blocks::ExtrinsicEvents, diff --git a/crates/pop-parachains/src/call/metadata/events.rs b/crates/pop-parachains/src/utils/events.rs similarity index 88% rename from crates/pop-parachains/src/call/metadata/events.rs rename to crates/pop-parachains/src/utils/events.rs index f15957bcd..2f47e7e3e 100644 --- a/crates/pop-parachains/src/call/metadata/events.rs +++ b/crates/pop-parachains/src/utils/events.rs @@ -25,9 +25,7 @@ impl StaticEvent for Reserved { /// /// # Arguments /// * `events` - The extrinsic events from a transaction. -pub async fn extract_para_id_from_event( - events: &ExtrinsicEvents, -) -> Result { +pub fn extract_para_id_from_event(events: &ExtrinsicEvents) -> Result { let reserved_event = events.find_first::()?; reserved_event .map(|event| event.para_id) diff --git a/crates/pop-parachains/src/utils/mod.rs b/crates/pop-parachains/src/utils/mod.rs index 265ebafd4..a59eacd9a 100644 --- a/crates/pop-parachains/src/utils/mod.rs +++ b/crates/pop-parachains/src/utils/mod.rs @@ -1,3 +1,4 @@ // SPDX-License-Identifier: GPL-3.0 +pub mod events; pub mod helpers; From 204bb7c9f1b6111ffbec4110a59f3e897833492e Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 16:26:22 +0100 Subject: [PATCH 089/117] fix: submit_extrinsic_with_wallet under parachain feature --- crates/pop-cli/src/common/wallet.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 5b8f5a83e..a648285dd 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -6,6 +6,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use cliclack::{log, spinner}; +#[cfg(feature = "parachain")] use pop_parachains::{submit_signed_extrinsic, ExtrinsicEvents, OnlineClient, SubstrateConfig}; use url::Url; @@ -70,6 +71,7 @@ pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { } // Sign and submit an extrinsic using wallet integration, then returns the resulting events. +#[cfg(feature = "parachain")] pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, url: &Url, From 9ed671ef7f4550f51bffdbcb243eeb71662588cd Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 17:07:55 +0100 Subject: [PATCH 090/117] refactor: remove unnecesary code --- crates/pop-cli/src/commands/call/chain.rs | 4 ++-- crates/pop-cli/src/commands/up/parachain.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index c90ea66f2..5a3aacba3 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -372,7 +372,7 @@ pub(crate) struct Chain { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] -pub(crate) struct Call { +pub struct Call { /// The dispatchable function to execute. function: Function, /// The dispatchable function arguments, encoded as strings. @@ -393,7 +393,7 @@ pub(crate) struct Call { impl Call { // Prepares the extrinsic. - pub(crate) fn prepare_extrinsic( + pub fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 3f97df65e..ae329d564 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -90,7 +90,7 @@ impl UpChainCommand { Some(id) => Ok(id), None => { cli.info("Reserving a parachain ID...")?; - return reserve_para_id(&chain, cli).await; + reserve_para_id(chain, cli).await }, } } @@ -104,7 +104,7 @@ impl UpChainCommand { (Some(state), Some(code)) => Ok((state, code)), _ => { cli.info("Generating the chain spec for your parachain.")?; - return generate_spec_files(para_id, self.path.clone(), cli).await; + generate_spec_files(para_id, self.path.clone(), cli).await }, } } From 7a4ee0a1b4a257ffa2347d2ade2cf448f6585646 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Thu, 20 Feb 2025 17:40:09 +0100 Subject: [PATCH 091/117] test: increase coverage with reserve_parachain_id_fails_wrong_chain and resolve_genesis_files_fails_wrong_path --- crates/pop-cli/src/commands/up/parachain.rs | 124 +++++++++++++++++++- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index ae329d564..d06e83d5a 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -89,7 +89,7 @@ impl UpChainCommand { match self.id { Some(id) => Ok(id), None => { - cli.info("Reserving a parachain ID...")?; + cli.info("Reserving a parachain ID")?; reserve_para_id(chain, cli).await }, } @@ -103,7 +103,7 @@ impl UpChainCommand { match (self.genesis_state.clone(), self.genesis_code.clone()) { (Some(state), Some(code)) => Ok((state, code)), _ => { - cli.info("Generating the chain spec for your parachain.")?; + cli.info("Generating the chain spec for your parachain")?; generate_spec_files(para_id, self.path.clone(), cli).await }, } @@ -192,9 +192,15 @@ async fn generate_spec_files( #[cfg(test)] mod tests { use super::*; - use crate::cli::MockCli; + use crate::{ + build::spec::{ChainType, RelayChain}, + cli::MockCli, + }; + use duct::cmd; + use pop_common::Profile; use pop_parachains::decode_call_data; - use std::fs; + use std::{env, fs}; + use strum::{EnumMessage, VariantArray}; use tempfile::tempdir; use url::Url; @@ -238,6 +244,80 @@ mod tests { Ok(()) } + #[tokio::test] + async fn reserve_parachain_id_fails_wrong_chain() -> Result<()> { + let mut cli = MockCli::new() + .expect_intro("Deploy a chain") + .expect_info("Reserving a parachain ID") + .expect_outro_cancel("Failed to find the pallet Registrar"); + let (genesis_state, genesis_code) = create_temp_genesis_files()?; + UpChainCommand { + id: None, + genesis_state: Some(genesis_state.clone()), + genesis_code: Some(genesis_code.clone()), + relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + path: None, + } + .execute(&mut cli) + .await?; + + cli.verify() + } + + #[tokio::test] + async fn resolve_genesis_files_fails_wrong_path() -> Result<()> { + // Mock a project path without node. + let name = "hello_world"; + let temp_dir = tempfile::tempdir()?; + let path = temp_dir.path(); + let project_path = path.join(name); + cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; + let original_dir = std::env::current_dir()?; + + let mut cli = MockCli::new() + .expect_intro("Deploy a chain") + .expect_info("Generating the chain spec for your parachain") + .expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) + .expect_input( + "Name or path for the plain chain spec file:", "output_file".to_string()) + .expect_input( + "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) + .expect_select( + "Choose the chain type: ", + Some(false), + true, + Some(chain_types()), + ChainType::Development as usize, + ).expect_select( + "Choose the relay your chain will be connecting to: ", + Some(false), + true, + Some(relays()), + RelayChain::PaseoLocal as usize, + ).expect_select( + "Choose the build profile of the binary that should be used: ", + Some(false), + true, + Some(profiles()), + Profile::Release as usize, + ).expect_outro_cancel(format!("Failed to get manifest path: {}/node/Cargo.toml", fs::canonicalize(&project_path)?.display().to_string())); + + UpChainCommand { + id: Some(2000), + genesis_state: None, + genesis_code: None, + relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + path: Some(project_path.clone()), + } + .execute(&mut cli) + .await?; + + assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(project_path)?); + // Reset working directory back to original + std::env::set_current_dir(original_dir)?; + cli.verify() + } + #[tokio::test] async fn register_parachain_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() @@ -296,4 +376,40 @@ mod tests { Ok((genesis_state_path, genesis_code_path)) } + + fn relays() -> Vec<(String, String)> { + RelayChain::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn chain_types() -> Vec<(String, String)> { + ChainType::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } + + fn profiles() -> Vec<(String, String)> { + Profile::VARIANTS + .iter() + .map(|variant| { + ( + variant.get_message().unwrap_or(variant.as_ref()).into(), + variant.get_detailed_message().unwrap_or_default().into(), + ) + }) + .collect() + } } From 4bb6d2f1bef68656de000166c5f7c2a73f25afd2 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:27:40 +0100 Subject: [PATCH 092/117] refactor: remove unnecesary clones --- crates/pop-cli/src/commands/call/chain.rs | 6 +++--- crates/pop-cli/src/commands/up/parachain.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 5a3aacba3..5950e9b19 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -362,11 +362,11 @@ impl CallChainCommand { // Represents a chain, including its URL, client connection, and available pallets. pub(crate) struct Chain { // Websocket endpoint of the node. - pub(crate) url: Url, + pub url: Url, // The client used to interact with the chain. - pub(crate) client: OnlineClient, + pub client: OnlineClient, // A list of pallets available on the chain. - pub(crate) pallets: Vec, + pub pallets: Vec, } /// Represents a configured dispatchable function call, including the pallet, function, arguments, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index d06e83d5a..b803af0f2 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -10,7 +10,7 @@ use pop_parachains::{ construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, parse_chain_metadata, set_up_client, Action, Payload, }; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use url::Url; const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; @@ -100,11 +100,11 @@ impl UpChainCommand { para_id: u32, cli: &mut impl Cli, ) -> Result<(PathBuf, PathBuf)> { - match (self.genesis_state.clone(), self.genesis_code.clone()) { - (Some(state), Some(code)) => Ok((state, code)), + match (&self.genesis_state, &self.genesis_code) { + (Some(state), Some(code)) => Ok((state.clone(), code.clone())), _ => { cli.info("Generating the chain spec for your parachain")?; - generate_spec_files(para_id, self.path.clone(), cli).await + generate_spec_files(para_id, self.path.as_deref(), cli).await }, } } @@ -170,7 +170,7 @@ fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { // Generates chain spec files for the parachain. async fn generate_spec_files( id: u32, - path: Option, + path: Option<&Path>, cli: &mut impl Cli, ) -> anyhow::Result<(PathBuf, PathBuf)> { // Changes the working directory if a path is provided to ensure the build spec process runs in From a7ab10fe8f290e0b533f5e32435b4a3b9f1b512d Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:29:01 +0100 Subject: [PATCH 093/117] refactor: minor improvements --- crates/pop-cli/src/commands/up/parachain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index b803af0f2..84a4050e9 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -150,7 +150,7 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; let para_id = extract_para_id_from_event(&events).map_err(|_| { - anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with --id.") + anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with `--id`.") })?; cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; Ok(para_id) @@ -158,12 +158,12 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { // Prepares and returns the encoded call data for reserving a parachain ID. fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { - let function = find_dispatchable_by_name( + let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), Action::Reserve.function_name(), )?; - let xt = construct_extrinsic(function, Vec::new())?; + let xt = construct_extrinsic(dispatchable, Vec::new())?; Ok(xt.encode_call_data(&chain.client.metadata())?) } From 22acd08ccc4493d921e71ff1e265fbe7834be772 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:44:44 +0100 Subject: [PATCH 094/117] test: refactor tests and include comments --- crates/pop-cli/src/commands/up/parachain.rs | 45 +++++++-------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 84a4050e9..ce131eb5e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -240,7 +240,10 @@ mod tests { .configure_chain(&mut cli) .await?; let call_data = prepare_reserve_parachain_call_data(&chain)?; - assert_eq!(call_data, decode_call_data("0x4605")?); + // Encoded call data for a reserve extrinsic. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4605 + let encoded_reserve_extrinsic: &str = "0x4605"; + assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); Ok(()) } @@ -286,19 +289,19 @@ mod tests { "Choose the chain type: ", Some(false), true, - Some(chain_types()), + Some(get_messages(ChainType::VARIANTS)), ChainType::Development as usize, ).expect_select( "Choose the relay your chain will be connecting to: ", Some(false), true, - Some(relays()), + Some(get_messages(RelayChain::VARIANTS)), RelayChain::PaseoLocal as usize, ).expect_select( "Choose the build profile of the binary that should be used: ", Some(false), true, - Some(profiles()), + Some(get_messages(Profile::VARIANTS)), Profile::Release as usize, ).expect_outro_cancel(format!("Failed to get manifest path: {}/node/Cargo.toml", fs::canonicalize(&project_path)?.display().to_string())); @@ -361,7 +364,10 @@ mod tests { chain, } .prepare_register_parachain_call_data()?; - assert_eq!(call_data, decode_call_data("0x4600d0070000081234081234")?); + // Encoded call data for a register extrinsic with the above values. + // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 + let encoded_reserve_extrinsic: &str = "0x4605"; + assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); Ok(()) } @@ -377,32 +383,9 @@ mod tests { Ok((genesis_state_path, genesis_code_path)) } - fn relays() -> Vec<(String, String)> { - RelayChain::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } - - fn chain_types() -> Vec<(String, String)> { - ChainType::VARIANTS - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } - - fn profiles() -> Vec<(String, String)> { - Profile::VARIANTS + // Generic helper function to convert enum variants into (message, detailed message) tuples. + fn get_messages>(variants: &[T]) -> Vec<(String, String)> { + variants .iter() .map(|variant| { ( From f5ae484806b9f971504f078c2a7c725b07b66164 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 16:52:26 +0100 Subject: [PATCH 095/117] refactor: map errors in submit_extrinsic_with_wallet --- crates/pop-cli/src/common/wallet.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index a648285dd..8eabd8fdb 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -79,20 +79,16 @@ pub(crate) async fn submit_extrinsic_with_wallet( cli: &mut impl Cli, ) -> Result> { let maybe_payload = request_signature(call_data, url.to_string()).await?; - if let Some(payload) = maybe_payload { - cli.success("Signed payload received.")?; - let spinner = cliclack::spinner(); - spinner.start( - "Submitting the extrinsic and then waiting for finalization, please be patient...", - ); + let payload = maybe_payload.ok_or_else(|| anyhow!("No signed payload received."))?; + cli.success("Signed payload received.")?; + let spinner = cliclack::spinner(); + spinner + .start("Submitting the extrinsic and then waiting for finalization, please be patient..."); - let result = submit_signed_extrinsic(client.clone(), payload) - .await - .map_err(|err| anyhow!("{}", format!("{err:?}")))?; + let result = submit_signed_extrinsic(client.clone(), payload) + .await + .map_err(anyhow::Error::from)?; - spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); - Ok(result) - } else { - Err(anyhow!("No signed payload received.")) - } + spinner.stop(format!("Extrinsic submitted with hash: {:?}", result.extrinsic_hash())); + Ok(result) } From 6dd6ff47bae03ac2216698922e4d12d8256d823f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 21 Feb 2025 17:05:23 +0100 Subject: [PATCH 096/117] test: fix prepare_register_parachain_call_data_works --- crates/pop-cli/src/commands/up/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index ce131eb5e..7339f08fc 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -366,8 +366,8 @@ mod tests { .prepare_register_parachain_call_data()?; // Encoded call data for a register extrinsic with the above values. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 - let encoded_reserve_extrinsic: &str = "0x4605"; - assert_eq!(call_data, decode_call_data(encoded_reserve_extrinsic)?); + let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; + assert_eq!(call_data, decode_call_data(encoded_register_extrinsic)?); Ok(()) } From c4679e5391b5e821e5fdaaed77ead72a4aee4f59 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sun, 23 Feb 2025 18:27:23 +0100 Subject: [PATCH 097/117] refactor: move configure_chain into a common folder --- crates/pop-cli/src/commands/call/chain.rs | 110 ++++++++------------ crates/pop-cli/src/commands/up/parachain.rs | 63 +++++------ crates/pop-cli/src/common/chain.rs | 60 +++++++++++ crates/pop-cli/src/common/mod.rs | 2 + 4 files changed, 134 insertions(+), 101 deletions(-) create mode 100644 crates/pop-cli/src/common/chain.rs diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 5950e9b19..0758ee6da 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -4,15 +4,18 @@ use std::path::Path; use crate::{ cli::{self, traits::*}, - common::wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, + common::{ + chain::{configure_chain, Chain}, + wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, + }, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ construct_extrinsic, construct_sudo_extrinsic, decode_call_data, encode_call_data, - find_dispatchable_by_name, find_pallet_by_name, parse_chain_metadata, set_up_client, - sign_and_submit_extrinsic, supported_actions, Action, CallData, DynamicPayload, Function, - OnlineClient, Pallet, Param, Payload, SubstrateConfig, + find_dispatchable_by_name, find_pallet_by_name, sign_and_submit_extrinsic, supported_actions, + Action, CallData, DynamicPayload, Function, OnlineClient, Pallet, Param, Payload, + SubstrateConfig, }; use url::Url; @@ -67,10 +70,17 @@ impl CallChainCommand { /// Executes the command. pub(crate) async fn execute(mut self) -> Result<()> { let mut cli = cli::Cli; + cli.intro("Call a chain")?; // Check if all fields are specified via the command line. let prompt_to_repeat_call = self.requires_user_input(); // Configure the chain. - let chain = self.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + DEFAULT_URL, + &self.url, + &mut cli, + ) + .await?; // Execute the call if call_data is provided. if let Some(call_data) = self.call_data.as_ref() { if let Err(e) = self @@ -134,33 +144,6 @@ impl CallChainCommand { Ok(()) } - // Configures the chain by resolving the URL and fetching its metadata. - async fn configure_chain(&self, cli: &mut impl Cli) -> Result { - cli.intro("Call a chain")?; - // Resolve url. - let url = match &self.url { - Some(url) => url.clone(), - None => { - // Prompt for url. - let url: String = cli - .input("Which chain would you like to interact with?") - .default_input(DEFAULT_URL) - .interact()?; - Url::parse(&url)? - }, - }; - - // Parse metadata from chain url. - let client = set_up_client(url.as_str()).await?; - let mut pallets = parse_chain_metadata(&client).map_err(|e| { - anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) - })?; - // Sort by name for display. - pallets.sort_by(|a, b| a.name.cmp(&b.name)); - pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); - Ok(Chain { url, client, pallets }) - } - // Configure the call based on command line arguments/call UI. fn configure_call(&mut self, chain: &Chain, cli: &mut impl Cli) -> Result { loop { @@ -359,16 +342,6 @@ impl CallChainCommand { } } -// Represents a chain, including its URL, client connection, and available pallets. -pub(crate) struct Chain { - // Websocket endpoint of the node. - pub url: Url, - // The client used to interact with the chain. - pub client: OnlineClient, - // A list of pallets available on the chain. - pub pallets: Vec, -} - /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] @@ -646,6 +619,7 @@ fn parse_function_name(name: &str) -> Result { mod tests { use super::*; use crate::{cli::MockCli, common::wallet::USE_WALLET_PROMPT}; + use pop_parachains::{parse_chain_metadata, set_up_client}; use tempfile::tempdir; use url::Url; @@ -653,26 +627,12 @@ mod tests { const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; const POLKADOT_NETWORK_URL: &str = "wss://polkadot-rpc.publicnode.com"; - #[tokio::test] - async fn configure_chain_works() -> Result<()> { - let call_config = - CallChainCommand { suri: Some(DEFAULT_URI.to_string()), ..Default::default() }; - let mut cli = MockCli::new().expect_intro("Call a chain").expect_input( - "Which chain would you like to interact with?", - POP_NETWORK_TESTNET_URL.into(), - ); - let chain = call_config.configure_chain(&mut cli).await?; - assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); - cli.verify() - } - #[tokio::test] async fn guide_user_to_call_chain_works() -> Result<()> { let mut call_config = CallChainCommand { pallet: Some("System".to_string()), ..Default::default() }; let mut cli = MockCli::new() - .expect_intro("Call a chain") .expect_input("Which chain would you like to interact with?", POP_NETWORK_TESTNET_URL.into()) .expect_select( "Select the function to call:", @@ -700,7 +660,13 @@ mod tests { .expect_confirm("Would you like to dispatch this function call with `Root` origin?", true) .expect_confirm(USE_WALLET_PROMPT, true); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &None, + &mut cli, + ) + .await?; assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); let call_chain = call_config.configure_call(&chain, &mut cli)?; @@ -718,11 +684,17 @@ mod tests { async fn guide_user_to_configure_predefined_action_works() -> Result<()> { let mut call_config = CallChainCommand::default(); - let mut cli = MockCli::new().expect_intro("Call a chain").expect_input( + let mut cli = MockCli::new().expect_input( "Which chain would you like to interact with?", POLKADOT_NETWORK_URL.into(), ); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &None, + &mut cli, + ) + .await?; assert_eq!(chain.url, Url::parse(POLKADOT_NETWORK_URL)?); cli.verify()?; @@ -872,20 +844,30 @@ mod tests { sudo: true, }; let mut cli = MockCli::new() - .expect_intro("Call a chain") .expect_warning("NOTE: sudo is not supported by the chain. Ignoring `--sudo` flag."); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) + .await?; call_config.configure_sudo(&chain, &mut cli)?; assert!(!call_config.sudo); cli.verify()?; // Test when sudo pallet exist. - cli = MockCli::new().expect_intro("Call a chain").expect_confirm( + cli = MockCli::new().expect_confirm( "Would you like to dispatch this function call with `Root` origin?", true, ); - call_config.url = Some(Url::parse(POP_NETWORK_TESTNET_URL)?); - let chain = call_config.configure_chain(&mut cli).await?; + let chain = configure_chain( + "Which chain would you like to interact with?", + POP_NETWORK_TESTNET_URL, + &Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + &mut cli, + ) + .await?; call_config.configure_sudo(&chain, &mut cli)?; assert!(call_config.sudo); cli.verify() diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 7339f08fc..e2179fbbe 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,14 +1,17 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, call::chain::Chain, cli::traits::*, - common::wallet::submit_extrinsic_with_wallet, + build::spec::BuildSpecCommand, + cli::traits::*, + common::{ + chain::{configure_chain, Chain}, + wallet::submit_extrinsic_with_wallet, + }, }; use anyhow::{anyhow, Result}; use clap::Args; use pop_parachains::{ - construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, - parse_chain_metadata, set_up_client, Action, Payload, + construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, Action, Payload, }; use std::path::{Path, PathBuf}; use url::Url; @@ -56,34 +59,18 @@ impl UpChainCommand { // Prepares the chain for registration by setting up its configuration. async fn prepare_chain_for_registration(self, cli: &mut impl Cli) -> Result { - let chain = self.configure_chain(cli).await?; + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &self.relay_url, + cli, + ) + .await?; let para_id = self.resolve_parachain_id(&chain, cli).await?; let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) } - // Configures the chain by resolving the URL and fetching its metadata. - async fn configure_chain(&self, cli: &mut impl Cli) -> Result { - // Resolve url. - let url = match &self.relay_url { - Some(url) => url.clone(), - None => { - // Prompt for url. - let url: String = cli - .input("Enter the relay chain node URL to deploy your parachain") - .default_input(DEFAULT_URL) - .interact()?; - Url::parse(&url)? - }, - }; - - // Parse metadata from chain url. - let client = set_up_client(url.as_str()).await?; - let pallets = parse_chain_metadata(&client).map_err(|e| { - anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())) - })?; - Ok(Chain { url, client, pallets }) - } // Resolves the parachain ID, reserving a new one if necessary. async fn resolve_parachain_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { match self.id { @@ -233,11 +220,12 @@ mod tests { #[tokio::test] async fn prepare_reserve_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpChainCommand { - relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), - ..Default::default() - } - .configure_chain(&mut cli) + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) .await?; let call_data = prepare_reserve_parachain_call_data(&chain)?; // Encoded call data for a reserve extrinsic. @@ -344,11 +332,12 @@ mod tests { #[tokio::test] async fn prepare_register_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = UpChainCommand { - relay_url: Some(Url::parse(POLKADOT_NETWORK_URL)?), - ..Default::default() - } - .configure_chain(&mut cli) + let chain = configure_chain( + "Enter the relay chain node URL to deploy your parachain", + DEFAULT_URL, + &Some(Url::parse(POLKADOT_NETWORK_URL)?), + &mut cli, + ) .await?; // Create a temporary files to act as genesis_state and genesis_code files. let temp_dir = tempdir()?; diff --git a/crates/pop-cli/src/common/chain.rs b/crates/pop-cli/src/common/chain.rs new file mode 100644 index 000000000..d82272548 --- /dev/null +++ b/crates/pop-cli/src/common/chain.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::cli::traits::*; +use anyhow::{anyhow, Result}; +use pop_parachains::{parse_chain_metadata, set_up_client, OnlineClient, Pallet, SubstrateConfig}; +use url::Url; + +// Represents a chain, including its URL, client connection, and available pallets. +pub(crate) struct Chain { + // Websocket endpoint of the node. + pub url: Url, + // The client used to interact with the chain. + pub client: OnlineClient, + // A list of pallets available on the chain. + pub pallets: Vec, +} + +// Configures the chain by resolving the URL and fetching its metadata. +pub(crate) async fn configure_chain( + input_message: &str, + default_input: &str, + url: &Option, + cli: &mut impl Cli, +) -> Result { + // Resolve url. + let url = match url { + Some(url) => url.clone(), + None => { + // Prompt for url. + let url: String = cli.input(input_message).default_input(default_input).interact()?; + Url::parse(&url)? + }, + }; + + // Parse metadata from chain url. + let client = set_up_client(url.as_str()).await?; + let mut pallets = parse_chain_metadata(&client) + .map_err(|e| anyhow!(format!("Unable to fetch the chain metadata: {}", e.to_string())))?; + // Sort by name for display. + pallets.sort_by(|a, b| a.name.cmp(&b.name)); + pallets.iter_mut().for_each(|p| p.functions.sort_by(|a, b| a.name.cmp(&b.name))); + Ok(Chain { url, client, pallets }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::MockCli; + + const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; + + #[tokio::test] + async fn configure_chain_works() -> Result<()> { + let message = "Enter the URL of the chain:"; + let mut cli = MockCli::new().expect_input(message, POP_NETWORK_TESTNET_URL.into()); + let chain = configure_chain(message, POP_NETWORK_TESTNET_URL, &None, &mut cli).await?; + assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); + cli.verify() + } +} diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index 512d2a670..8730cb87f 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-3.0 pub mod builds; +#[cfg(feature = "parachain")] +pub mod chain; #[cfg(feature = "contract")] pub mod contracts; pub mod helpers; From ab8901e42db668c9b5b776a2c12a7bf55a914c7f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 24 Feb 2025 09:06:00 +0100 Subject: [PATCH 098/117] refactor: function visibility --- crates/pop-cli/src/commands/call/chain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index 0758ee6da..c437c78cf 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -345,7 +345,7 @@ impl CallChainCommand { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] -pub struct Call { +struct Call { /// The dispatchable function to execute. function: Function, /// The dispatchable function arguments, encoded as strings. @@ -366,7 +366,7 @@ pub struct Call { impl Call { // Prepares the extrinsic. - pub fn prepare_extrinsic( + fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, From 5e6f87744dc44441d01cb4b99897062ee88c3c78 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 26 Feb 2025 09:47:34 +0100 Subject: [PATCH 099/117] fix: error message and include test for it --- crates/pop-cli/src/commands/up/parachain.rs | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index e2179fbbe..9cf099a1b 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -124,7 +124,7 @@ impl UpChain { let state = std::fs::read_to_string(genesis_state) .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; let code = std::fs::read_to_string(genesis_code) - .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; + .map_err(|err| anyhow!("Failed to read genesis code file: {}", err.to_string()))?; let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; Ok(xt.encode_call_data(&chain.client.metadata())?) } @@ -343,20 +343,34 @@ mod tests { let temp_dir = tempdir()?; let genesis_state_path = temp_dir.path().join("genesis_state"); let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); + let up_chain = UpChain { + id: 2000, + genesis_state: genesis_state_path.clone(), + genesis_code: genesis_code_path.clone(), + chain, + }; + + // Expect failure when the genesis state file cannot be read. + assert!(matches!( + up_chain.prepare_register_parachain_call_data(), + Err(message) if message.to_string().contains("Failed to read genesis state file") + )); std::fs::write(&genesis_state_path, "0x1234")?; + + // Expect failure when the genesis code file cannot be read. + assert!(matches!( + up_chain.prepare_register_parachain_call_data(), + Err(message) if message.to_string().contains("Failed to read genesis code file") + )); std::fs::write(&genesis_code_path, "0x1234")?; - let call_data = UpChain { - id: 2000, - genesis_state: genesis_state_path, - genesis_code: genesis_code_path, - chain, - } - .prepare_register_parachain_call_data()?; // Encoded call data for a register extrinsic with the above values. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; - assert_eq!(call_data, decode_call_data(encoded_register_extrinsic)?); + assert_eq!( + up_chain.prepare_register_parachain_call_data()?, + decode_call_data(encoded_register_extrinsic)? + ); Ok(()) } From fb507d91f21a5f588018343fa0426f9297126305 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Fri, 28 Feb 2025 19:55:52 +0100 Subject: [PATCH 100/117] refactor: build specs removing repetitive code --- crates/pop-cli/src/commands/build/spec.rs | 112 ++++++++------------ crates/pop-cli/src/commands/up/parachain.rs | 24 +++-- 2 files changed, 60 insertions(+), 76 deletions(-) diff --git a/crates/pop-cli/src/commands/build/spec.rs b/crates/pop-cli/src/commands/build/spec.rs index c78a5c30c..94fc713e6 100644 --- a/crates/pop-cli/src/commands/build/spec.rs +++ b/crates/pop-cli/src/commands/build/spec.rs @@ -9,7 +9,7 @@ use crate::{ style::style, }; use clap::{Args, ValueEnum}; -use cliclack::{spinner, ProgressBar}; +use cliclack::spinner; use pop_common::Profile; use pop_parachains::{ binary_path, build_parachain, export_wasm_file, generate_genesis_state_file, @@ -25,6 +25,9 @@ use std::{thread::sleep, time::Duration}; use strum::{EnumMessage, VariantArray}; use strum_macros::{AsRefStr, Display, EnumString}; +pub(crate) type CodePathBuf = PathBuf; +pub(crate) type StatePathBuf = PathBuf; + const DEFAULT_CHAIN: &str = "dev"; const DEFAULT_PARA_ID: u32 = 2000; const DEFAULT_PROTOCOL_ID: &str = "my-protocol"; @@ -179,13 +182,13 @@ impl BuildSpecCommand { // Checks for appchain project in `./`. if is_supported(None)? { let build_spec = self.configure_build_spec(&mut cli).await?; - build_spec.build(&mut cli) + build_spec.build(&mut cli)?; } else { cli.outro_cancel( "🚫 Can't build a specification for target. Maybe not a chain project ?", )?; - Ok("spec") } + Ok("spec") } /// Configure chain specification requirements by prompting for missing inputs, validating @@ -458,42 +461,72 @@ impl BuildSpec { // This function generates plain and raw chain spec files based on the provided configuration, // optionally including genesis state and runtime artifacts. If the node binary is missing, // it triggers a build process. - fn build(self, cli: &mut impl cli::traits::Cli) -> anyhow::Result<&'static str> { + pub(crate) fn build( + self, + cli: &mut impl cli::traits::Cli, + ) -> anyhow::Result<(Option, Option)> { cli.intro("Building your chain spec")?; let mut generated_files = vec![]; - let BuildSpec { ref output_file, ref profile, id, genesis_code, genesis_state, .. } = self; + let BuildSpec { + ref output_file, + ref profile, + id, + default_bootnode, + ref chain, + genesis_state, + genesis_code, + .. + } = self; // Ensure binary is built. let binary_path = ensure_binary_exists(cli, profile)?; let spinner = spinner(); + spinner.start("Generating chain specification..."); - let raw_chain_spec = self.generate_chain_spec(&binary_path, &spinner)?; - + // Generate chain spec. + generate_plain_chain_spec(&binary_path, output_file, default_bootnode, chain)?; + // Customize spec based on input. + self.customize()?; generated_files.push(format!( "Plain text chain specification file generated at: {}", &output_file.display() )); + // Generate raw spec. + spinner.set_message("Generating raw chain specification..."); + let spec_name = &output_file + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(DEFAULT_SPEC_NAME) + .trim_end_matches(".json"); + let raw_spec_name = format!("{spec_name}-raw.json"); + let raw_chain_spec = generate_raw_chain_spec(&binary_path, output_file, &raw_spec_name)?; generated_files.push(format!( "Raw chain specification file generated at: {}", raw_chain_spec.display() )); // Generate genesis artifacts. - if genesis_code { + let genesis_code_file = if genesis_code { spinner.set_message("Generating genesis code..."); let wasm_file_name = format!("para-{}.wasm", id); let wasm_file = export_wasm_file(&binary_path, &raw_chain_spec, &wasm_file_name)?; generated_files .push(format!("WebAssembly runtime file exported at: {}", wasm_file.display())); - } - if genesis_state { + Some(wasm_file) + } else { + None + }; + let genesis_state_file = if genesis_state { spinner.set_message("Generating genesis state..."); let genesis_file_name = format!("para-{}-genesis-state", id); let genesis_state_file = generate_genesis_state_file(&binary_path, &raw_chain_spec, &genesis_file_name)?; generated_files .push(format!("Genesis State file exported at: {}", genesis_state_file.display())); - } + Some(genesis_state_file) + } else { + None + }; spinner.stop("Chain specification built successfully."); let generated_files: Vec<_> = generated_files @@ -505,62 +538,7 @@ impl BuildSpec { "Need help? Learn more at {}\n", style("https://learn.onpop.io").magenta().underlined() ))?; - Ok("spec") - } - - /// Generates plain and raw chain specification files, and returns the path to the latter. - fn generate_chain_spec( - &self, - binary_path: &Path, - spinner: &ProgressBar, - ) -> anyhow::Result { - let BuildSpec { output_file, chain, .. } = self; - spinner.start("Generating chain specification..."); - - // Generate plain chain spec. - generate_plain_chain_spec(binary_path, output_file, self.default_bootnode, chain)?; - // Customize spec based on input. - self.customize()?; - - // Generate raw spec. - spinner.set_message("Generating raw chain specification..."); - let spec_name = &output_file - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or(DEFAULT_SPEC_NAME) - .trim_end_matches(".json"); - let raw_spec_name = format!("{spec_name}-raw.json"); - generate_raw_chain_spec(binary_path, output_file, &raw_spec_name) - .map_err(anyhow::Error::from) - } - - /// Generates chain specification files and returns the file paths for the generated genesis - /// code and genesis state files. - /// - /// # Arguments - /// * `cli` - The cli. - pub(crate) fn generate_genesis_artifacts( - self, - cli: &mut impl cli::traits::Cli, - ) -> anyhow::Result<(PathBuf, PathBuf)> { - // Ensure binary is built once. - let binary_path = ensure_binary_exists(cli, &self.profile)?; - let spinner = spinner(); - spinner.start("Generating files..."); - - let raw_chain_spec = self.generate_chain_spec(&binary_path, &spinner)?; - - spinner.set_message("Generating genesis code..."); - let wasm_file_name = format!("para-{}.wasm", self.id); - let genesis_code = export_wasm_file(&binary_path, &raw_chain_spec, &wasm_file_name)?; - - spinner.set_message("Generating genesis state..."); - let genesis_file_name = format!("para-{}-genesis-state", self.id); - let genesis_state = - generate_genesis_state_file(&binary_path, &raw_chain_spec, &genesis_file_name)?; - - spinner.stop("Genesis artifacts generated successfully."); - Ok((genesis_state, genesis_code)) + Ok((genesis_code_file, genesis_state_file)) } // Customize a chain specification. diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 9cf099a1b..ef1bbe9fb 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{ - build::spec::BuildSpecCommand, + build::spec::{BuildSpecCommand, CodePathBuf, StatePathBuf}, cli::traits::*, common::{ chain::{configure_chain, Chain}, @@ -30,10 +30,10 @@ pub struct UpChainCommand { pub(crate) id: Option, /// Path to the genesis state file. If not specified, it will be generated. #[arg(short = 'G', long = "genesis-state")] - pub(crate) genesis_state: Option, + pub(crate) genesis_state: Option, /// Path to the genesis code file. If not specified, it will be generated. #[arg(short = 'C', long = "genesis-code")] - pub(crate) genesis_code: Option, + pub(crate) genesis_code: Option, /// Websocket endpoint of the relay chain. #[arg(long)] pub(crate) relay_url: Option, @@ -67,7 +67,7 @@ impl UpChainCommand { ) .await?; let para_id = self.resolve_parachain_id(&chain, cli).await?; - let (genesis_state, genesis_code) = self.resolve_genesis_files(para_id, cli).await?; + let (genesis_code, genesis_state) = self.resolve_genesis_files(para_id, cli).await?; Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) } @@ -86,9 +86,9 @@ impl UpChainCommand { &self, para_id: u32, cli: &mut impl Cli, - ) -> Result<(PathBuf, PathBuf)> { - match (&self.genesis_state, &self.genesis_code) { - (Some(state), Some(code)) => Ok((state.clone(), code.clone())), + ) -> Result<(CodePathBuf, StatePathBuf)> { + match (&self.genesis_code, &self.genesis_state) { + (Some(code), Some(state)) => Ok((code.clone(), state.clone())), _ => { cli.info("Generating the chain spec for your parachain")?; generate_spec_files(para_id, self.path.as_deref(), cli).await @@ -159,7 +159,7 @@ async fn generate_spec_files( id: u32, path: Option<&Path>, cli: &mut impl Cli, -) -> anyhow::Result<(PathBuf, PathBuf)> { +) -> anyhow::Result<(CodePathBuf, StatePathBuf)> { // Changes the working directory if a path is provided to ensure the build spec process runs in // the correct context. if let Some(path) = path { @@ -173,7 +173,13 @@ async fn generate_spec_files( } .configure_build_spec(cli) .await?; - build_spec.generate_genesis_artifacts(cli) + + let (genesis_code_file, genesis_state_file) = build_spec.build(cli)?; + Ok(( + genesis_code_file.ok_or_else(|| anyhow::anyhow!("Failed to generate the genesis code."))?, + genesis_state_file + .ok_or_else(|| anyhow::anyhow!("Failed to generate the genesis state file."))?, + )) } #[cfg(test)] From 63aad385c92ea85b47a1ef50798ff4e0cb2ba9fa Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 09:08:05 +0100 Subject: [PATCH 101/117] refactor: use prepare_extrinsic from Call module to prepare a call --- crates/pop-cli/src/commands/call/chain.rs | 16 +++++----- crates/pop-cli/src/commands/up/parachain.rs | 34 ++++++++++++--------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index c437c78cf..bc35103be 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -345,28 +345,28 @@ impl CallChainCommand { /// Represents a configured dispatchable function call, including the pallet, function, arguments, /// and signing options. #[derive(Clone, Default)] -struct Call { +pub(crate) struct Call { /// The dispatchable function to execute. - function: Function, + pub(crate) function: Function, /// The dispatchable function arguments, encoded as strings. - args: Vec, + pub(crate) args: Vec, /// Secret key URI for the account signing the extrinsic. /// /// e.g. /// - for a dev account "//Alice" /// - with a password "//Alice///SECRET_PASSWORD" - suri: String, + pub(crate) suri: String, /// Whether to use your browser wallet to sign the extrinsic. - use_wallet: bool, + pub(crate) use_wallet: bool, /// Whether to automatically sign and submit the extrinsic without prompting for confirmation. - skip_confirm: bool, + pub(crate) skip_confirm: bool, /// Whether to dispatch the function call with `Root` origin. - sudo: bool, + pub(crate) sudo: bool, } impl Call { // Prepares the extrinsic. - fn prepare_extrinsic( + pub(crate) fn prepare_extrinsic( &self, client: &OnlineClient, cli: &mut impl Cli, diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index ef1bbe9fb..3b485ff0a 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -2,6 +2,7 @@ use crate::{ build::spec::{BuildSpecCommand, CodePathBuf, StatePathBuf}, + call::chain::Call, cli::traits::*, common::{ chain::{configure_chain, Chain}, @@ -10,9 +11,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use clap::Args; -use pop_parachains::{ - construct_extrinsic, extract_para_id_from_event, find_dispatchable_by_name, Action, Payload, -}; +use pop_parachains::{extract_para_id_from_event, find_dispatchable_by_name, Action, Payload}; use std::path::{Path, PathBuf}; use url::Url; @@ -108,15 +107,15 @@ impl UpChain { // Registers a parachain by submitting an extrinsic. async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { cli.info("Registering a parachain ID")?; - let call_data = self.prepare_register_parachain_call_data()?; + let call_data = self.prepare_register_parachain_call_data(cli)?; submit_extrinsic_with_wallet(&self.chain.client, &self.chain.url, call_data, cli).await?; Ok(()) } // Prepares and returns the encoded call data for registering a parachain. - fn prepare_register_parachain_call_data(&self) -> Result> { + fn prepare_register_parachain_call_data(&self, cli: &mut impl Cli) -> Result> { let UpChain { id, genesis_code, genesis_state, chain } = self; - let ex = find_dispatchable_by_name( + let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Register.pallet_name(), Action::Register.function_name(), @@ -125,14 +124,20 @@ impl UpChain { .map_err(|err| anyhow!("Failed to read genesis state file: {}", err.to_string()))?; let code = std::fs::read_to_string(genesis_code) .map_err(|err| anyhow!("Failed to read genesis code file: {}", err.to_string()))?; - let xt = construct_extrinsic(ex, vec![id.to_string(), state, code])?; + let xt = Call { + function: dispatchable.clone(), + args: vec![id.to_string(), state, code], + use_wallet: true, + ..Default::default() + } + .prepare_extrinsic(&chain.client, cli)?; Ok(xt.encode_call_data(&chain.client.metadata())?) } } // Reserves a parachain ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { - let call_data = prepare_reserve_parachain_call_data(chain)?; + let call_data = prepare_reserve_parachain_call_data(chain, cli)?; let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; @@ -144,13 +149,14 @@ async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { } // Prepares and returns the encoded call data for reserving a parachain ID. -fn prepare_reserve_parachain_call_data(chain: &Chain) -> Result> { +fn prepare_reserve_parachain_call_data(chain: &Chain, cli: &mut impl Cli) -> Result> { let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), Action::Reserve.function_name(), )?; - let xt = construct_extrinsic(dispatchable, Vec::new())?; + let xt = Call { function: dispatchable.clone(), use_wallet: true, ..Default::default() } + .prepare_extrinsic(&chain.client, cli)?; Ok(xt.encode_call_data(&chain.client.metadata())?) } @@ -233,7 +239,7 @@ mod tests { &mut cli, ) .await?; - let call_data = prepare_reserve_parachain_call_data(&chain)?; + let call_data = prepare_reserve_parachain_call_data(&chain, &mut cli)?; // Encoded call data for a reserve extrinsic. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4605 let encoded_reserve_extrinsic: &str = "0x4605"; @@ -358,14 +364,14 @@ mod tests { // Expect failure when the genesis state file cannot be read. assert!(matches!( - up_chain.prepare_register_parachain_call_data(), + up_chain.prepare_register_parachain_call_data(&mut cli), Err(message) if message.to_string().contains("Failed to read genesis state file") )); std::fs::write(&genesis_state_path, "0x1234")?; // Expect failure when the genesis code file cannot be read. assert!(matches!( - up_chain.prepare_register_parachain_call_data(), + up_chain.prepare_register_parachain_call_data(&mut cli), Err(message) if message.to_string().contains("Failed to read genesis code file") )); std::fs::write(&genesis_code_path, "0x1234")?; @@ -374,7 +380,7 @@ mod tests { // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; assert_eq!( - up_chain.prepare_register_parachain_call_data()?, + up_chain.prepare_register_parachain_call_data(&mut cli)?, decode_call_data(encoded_register_extrinsic)? ); Ok(()) From 321442de98b1900c755ebd34652b98d4e5f07eca Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 18:04:00 +0100 Subject: [PATCH 102/117] docs: improve comments and messages --- crates/pop-cli/src/commands/up/parachain.rs | 18 +++++++++--------- crates/pop-cli/src/common/chain.rs | 4 ++-- crates/pop-cli/src/common/wallet.rs | 5 ++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 3b485ff0a..610f61855 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -21,10 +21,10 @@ const HELP_HEADER: &str = "Chain deployment options"; #[derive(Args, Clone, Default)] #[clap(next_help_heading = HELP_HEADER)] pub struct UpChainCommand { - /// Path to the chain directory. + /// Path to the project. #[clap(skip)] pub(crate) path: Option, - /// Parachain ID to use. If not specified, a new ID will be reserved. + /// ID to use. If not specified, a new ID will be reserved. #[arg(short, long)] pub(crate) id: Option, /// Path to the genesis state file. If not specified, it will be generated. @@ -104,7 +104,7 @@ pub(crate) struct UpChain { chain: Chain, } impl UpChain { - // Registers a parachain by submitting an extrinsic. + // Registers by submitting an extrinsic. async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { cli.info("Registering a parachain ID")?; let call_data = self.prepare_register_parachain_call_data(cli)?; @@ -135,20 +135,20 @@ impl UpChain { } } -// Reserves a parachain ID by submitting an extrinsic. +// Reserves an ID by submitting an extrinsic. async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_parachain_call_data(chain, cli)?; let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) .await - .map_err(|e| anyhow::anyhow!("Parachain ID reservation failed: {}", e))?; + .map_err(|e| anyhow::anyhow!("ID reservation failed: {}", e))?; let para_id = extract_para_id_from_event(&events).map_err(|_| { - anyhow::anyhow!("Unable to parse the event. Specify the parachain ID manually with `--id`.") + anyhow::anyhow!("Unable to parse the event. Specify the ID manually with `--id`.") })?; - cli.success(format!("Successfully reserved parachain ID: {}", para_id))?; + cli.success(format!("Successfully reserved ID: {}", para_id))?; Ok(para_id) } -// Prepares and returns the encoded call data for reserving a parachain ID. +// Prepares and returns the encoded call data for reserving an ID. fn prepare_reserve_parachain_call_data(chain: &Chain, cli: &mut impl Cli) -> Result> { let dispatchable = find_dispatchable_by_name( &chain.pallets, @@ -160,7 +160,7 @@ fn prepare_reserve_parachain_call_data(chain: &Chain, cli: &mut impl Cli) -> Res Ok(xt.encode_call_data(&chain.client.metadata())?) } -// Generates chain spec files for the parachain. +// Generates chain spec files for the project. async fn generate_spec_files( id: u32, path: Option<&Path>, diff --git a/crates/pop-cli/src/common/chain.rs b/crates/pop-cli/src/common/chain.rs index d82272548..543c60375 100644 --- a/crates/pop-cli/src/common/chain.rs +++ b/crates/pop-cli/src/common/chain.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Result}; use pop_parachains::{parse_chain_metadata, set_up_client, OnlineClient, Pallet, SubstrateConfig}; use url::Url; -// Represents a chain, including its URL, client connection, and available pallets. +// Represents a chain and its associated metadata. pub(crate) struct Chain { // Websocket endpoint of the node. pub url: Url, @@ -15,7 +15,7 @@ pub(crate) struct Chain { pub pallets: Vec, } -// Configures the chain by resolving the URL and fetching its metadata. +// Configures a chain by resolving the URL and fetching its metadata. pub(crate) async fn configure_chain( input_message: &str, default_input: &str, diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 8eabd8fdb..56b9792ba 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -70,7 +70,7 @@ pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { } } -// Sign and submit an extrinsic using wallet integration, then returns the resulting events. +// Sign and submit an extrinsic using wallet integration. #[cfg(feature = "parachain")] pub(crate) async fn submit_extrinsic_with_wallet( client: &OnlineClient, @@ -82,8 +82,7 @@ pub(crate) async fn submit_extrinsic_with_wallet( let payload = maybe_payload.ok_or_else(|| anyhow!("No signed payload received."))?; cli.success("Signed payload received.")?; let spinner = cliclack::spinner(); - spinner - .start("Submitting the extrinsic and then waiting for finalization, please be patient..."); + spinner.start("Submitting the extrinsic and waiting for finalization, please be patient..."); let result = submit_signed_extrinsic(client.clone(), payload) .await From 18c79317d54118e39d82025459dca1a1594a961f Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 18:14:49 +0100 Subject: [PATCH 103/117] refactor: rename variables and structs --- crates/pop-cli/src/commands/call/chain.rs | 18 +++--- crates/pop-cli/src/commands/up/mod.rs | 6 +- crates/pop-cli/src/commands/up/parachain.rs | 68 ++++++++++----------- crates/pop-cli/src/common/chain.rs | 6 +- crates/pop-cli/src/common/wallet.rs | 2 +- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/crates/pop-cli/src/commands/call/chain.rs b/crates/pop-cli/src/commands/call/chain.rs index bc35103be..43828ca25 100644 --- a/crates/pop-cli/src/commands/call/chain.rs +++ b/crates/pop-cli/src/commands/call/chain.rs @@ -5,8 +5,8 @@ use std::path::Path; use crate::{ cli::{self, traits::*}, common::{ - chain::{configure_chain, Chain}, - wallet::{prompt_to_use_wallet, submit_extrinsic_with_wallet}, + chain::{configure, Chain}, + wallet::{self, prompt_to_use_wallet}, }, }; use anyhow::{anyhow, Result}; @@ -74,7 +74,7 @@ impl CallChainCommand { // Check if all fields are specified via the command line. let prompt_to_repeat_call = self.requires_user_input(); // Configure the chain. - let chain = configure_chain( + let chain = configure( "Which chain would you like to interact with?", DEFAULT_URL, &self.url, @@ -119,7 +119,7 @@ impl CallChainCommand { // Sign and submit the extrinsic. let result = if self.use_wallet { let call_data = xt.encode_call_data(&chain.client.metadata())?; - submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, &mut cli) + wallet::submit_extrinsic(&chain.client, &chain.url, call_data, &mut cli) .await .map(|_| ()) // Mapping to `()` since we don't need events returned } else { @@ -229,7 +229,7 @@ impl CallChainCommand { if use_wallet { let call_data_bytes = decode_call_data(call_data).map_err(|err| anyhow!("{}", format!("{err:?}")))?; - submit_extrinsic_with_wallet(client, url, call_data_bytes, cli) + wallet::submit_extrinsic(client, url, call_data_bytes, cli) .await .map_err(|err| anyhow!("{}", format!("{err:?}")))?; display_message("Call complete.", true, cli)?; @@ -660,7 +660,7 @@ mod tests { .expect_confirm("Would you like to dispatch this function call with `Root` origin?", true) .expect_confirm(USE_WALLET_PROMPT, true); - let chain = configure_chain( + let chain = configure( "Which chain would you like to interact with?", POP_NETWORK_TESTNET_URL, &None, @@ -688,7 +688,7 @@ mod tests { "Which chain would you like to interact with?", POLKADOT_NETWORK_URL.into(), ); - let chain = configure_chain( + let chain = configure( "Which chain would you like to interact with?", POP_NETWORK_TESTNET_URL, &None, @@ -845,7 +845,7 @@ mod tests { }; let mut cli = MockCli::new() .expect_warning("NOTE: sudo is not supported by the chain. Ignoring `--sudo` flag."); - let chain = configure_chain( + let chain = configure( "Which chain would you like to interact with?", POP_NETWORK_TESTNET_URL, &Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -861,7 +861,7 @@ mod tests { "Would you like to dispatch this function call with `Root` origin?", true, ); - let chain = configure_chain( + let chain = configure( "Which chain would you like to interact with?", POP_NETWORK_TESTNET_URL, &Some(Url::parse(POP_NETWORK_TESTNET_URL)?), diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index cf86fad92..07032bfc9 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -29,7 +29,7 @@ pub(crate) struct UpArgs { #[command(flatten)] #[cfg(feature = "parachain")] - pub(crate) parachain: parachain::UpChainCommand, + pub(crate) parachain: parachain::UpCommand, #[command(flatten)] #[cfg(feature = "contract")] @@ -93,7 +93,7 @@ impl Command { #[cfg(test)] mod tests { - use super::{contract::UpContractCommand, parachain::UpChainCommand, *}; + use super::{contract::UpContractCommand, *}; use cli::MockCli; use duct::cmd; @@ -122,7 +122,7 @@ mod tests { skip_confirm: false, valid: false, }, - parachain: UpChainCommand::default(), + parachain: parachain::UpCommand::default(), command: None, }) } diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 610f61855..8a7cdea35 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -5,8 +5,8 @@ use crate::{ call::chain::Call, cli::traits::*, common::{ - chain::{configure_chain, Chain}, - wallet::submit_extrinsic_with_wallet, + chain::{configure, Chain}, + wallet::submit_extrinsic, }, }; use anyhow::{anyhow, Result}; @@ -20,7 +20,7 @@ const HELP_HEADER: &str = "Chain deployment options"; #[derive(Args, Clone, Default)] #[clap(next_help_heading = HELP_HEADER)] -pub struct UpChainCommand { +pub struct UpCommand { /// Path to the project. #[clap(skip)] pub(crate) path: Option, @@ -38,18 +38,18 @@ pub struct UpChainCommand { pub(crate) relay_url: Option, } -impl UpChainCommand { +impl UpCommand { /// Executes the command. pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { cli.intro("Deploy a chain")?; - let chain_config = match self.prepare_chain_for_registration(cli).await { + let config = match self.prepare_for_registration(cli).await { Ok(chain) => chain, Err(e) => { cli.outro_cancel(format!("{}", e))?; return Ok(()); }, }; - match chain_config.register_parachain(cli).await { + match config.register(cli).await { Ok(_) => cli.success("Chain deployed successfully")?, Err(e) => cli.outro_cancel(format!("{}", e))?, } @@ -57,17 +57,17 @@ impl UpChainCommand { } // Prepares the chain for registration by setting up its configuration. - async fn prepare_chain_for_registration(self, cli: &mut impl Cli) -> Result { - let chain = configure_chain( + async fn prepare_for_registration(self, cli: &mut impl Cli) -> Result { + let chain = configure( "Enter the relay chain node URL to deploy your parachain", DEFAULT_URL, &self.relay_url, cli, ) .await?; - let para_id = self.resolve_parachain_id(&chain, cli).await?; - let (genesis_code, genesis_state) = self.resolve_genesis_files(para_id, cli).await?; - Ok(UpChain { id: para_id, genesis_state, genesis_code, chain }) + let id = self.resolve_parachain_id(&chain, cli).await?; + let (genesis_code, genesis_state) = self.resolve_genesis_files(id, cli).await?; + Ok(Registration { id, genesis_state, genesis_code, chain }) } // Resolves the parachain ID, reserving a new one if necessary. @@ -76,45 +76,45 @@ impl UpChainCommand { Some(id) => Ok(id), None => { cli.info("Reserving a parachain ID")?; - reserve_para_id(chain, cli).await + reserve(chain, cli).await }, } } // Resolves the genesis state and code files, generating them if necessary. async fn resolve_genesis_files( &self, - para_id: u32, + id: u32, cli: &mut impl Cli, ) -> Result<(CodePathBuf, StatePathBuf)> { match (&self.genesis_code, &self.genesis_state) { (Some(code), Some(state)) => Ok((code.clone(), state.clone())), _ => { cli.info("Generating the chain spec for your parachain")?; - generate_spec_files(para_id, self.path.as_deref(), cli).await + generate_spec_files(id, self.path.as_deref(), cli).await }, } } } // Represents the configuration for deploying a chain. -pub(crate) struct UpChain { +pub(crate) struct Registration { id: u32, genesis_state: PathBuf, genesis_code: PathBuf, chain: Chain, } -impl UpChain { +impl Registration { // Registers by submitting an extrinsic. - async fn register_parachain(&self, cli: &mut impl Cli) -> Result<()> { + async fn register(&self, cli: &mut impl Cli) -> Result<()> { cli.info("Registering a parachain ID")?; let call_data = self.prepare_register_parachain_call_data(cli)?; - submit_extrinsic_with_wallet(&self.chain.client, &self.chain.url, call_data, cli).await?; + submit_extrinsic(&self.chain.client, &self.chain.url, call_data, cli).await?; Ok(()) } // Prepares and returns the encoded call data for registering a parachain. fn prepare_register_parachain_call_data(&self, cli: &mut impl Cli) -> Result> { - let UpChain { id, genesis_code, genesis_state, chain } = self; + let Registration { id, genesis_code, genesis_state, chain } = self; let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Register.pallet_name(), @@ -136,16 +136,16 @@ impl UpChain { } // Reserves an ID by submitting an extrinsic. -async fn reserve_para_id(chain: &Chain, cli: &mut impl Cli) -> Result { +async fn reserve(chain: &Chain, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_parachain_call_data(chain, cli)?; - let events = submit_extrinsic_with_wallet(&chain.client, &chain.url, call_data, cli) + let events = submit_extrinsic(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("ID reservation failed: {}", e))?; - let para_id = extract_para_id_from_event(&events).map_err(|_| { + let id = extract_para_id_from_event(&events).map_err(|_| { anyhow::anyhow!("Unable to parse the event. Specify the ID manually with `--id`.") })?; - cli.success(format!("Successfully reserved ID: {}", para_id))?; - Ok(para_id) + cli.success(format!("Successfully reserved ID: {}", id))?; + Ok(id) } // Prepares and returns the encoded call data for reserving an ID. @@ -207,19 +207,19 @@ mod tests { const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; #[tokio::test] - async fn prepare_chain_for_registration_works() -> Result<()> { + async fn prepare_for_registration_works() -> Result<()> { let mut cli = MockCli::new().expect_input( "Enter the relay chain node URL to deploy your parachain", POLKADOT_NETWORK_URL.into(), ); let (genesis_state, genesis_code) = create_temp_genesis_files()?; - let chain_config = UpChainCommand { + let chain_config = UpCommand { id: Some(2000), genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), ..Default::default() } - .prepare_chain_for_registration(&mut cli) + .prepare_for_registration(&mut cli) .await?; assert_eq!(chain_config.id, 2000); @@ -232,7 +232,7 @@ mod tests { #[tokio::test] async fn prepare_reserve_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = configure_chain( + let chain = configure( "Enter the relay chain node URL to deploy your parachain", DEFAULT_URL, &Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -254,7 +254,7 @@ mod tests { .expect_info("Reserving a parachain ID") .expect_outro_cancel("Failed to find the pallet Registrar"); let (genesis_state, genesis_code) = create_temp_genesis_files()?; - UpChainCommand { + UpCommand { id: None, genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), @@ -305,7 +305,7 @@ mod tests { Profile::Release as usize, ).expect_outro_cancel(format!("Failed to get manifest path: {}/node/Cargo.toml", fs::canonicalize(&project_path)?.display().to_string())); - UpChainCommand { + UpCommand { id: Some(2000), genesis_state: None, genesis_code: None, @@ -322,13 +322,13 @@ mod tests { } #[tokio::test] - async fn register_parachain_fails_wrong_chain() -> Result<()> { + async fn register_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() .expect_intro("Deploy a chain") .expect_info("Registering a parachain ID") .expect_outro_cancel("Failed to find the pallet Registrar"); let (genesis_state, genesis_code) = create_temp_genesis_files()?; - UpChainCommand { + UpCommand { id: Some(2000), genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), @@ -344,7 +344,7 @@ mod tests { #[tokio::test] async fn prepare_register_parachain_call_data_works() -> Result<()> { let mut cli = MockCli::new(); - let chain = configure_chain( + let chain = configure( "Enter the relay chain node URL to deploy your parachain", DEFAULT_URL, &Some(Url::parse(POLKADOT_NETWORK_URL)?), @@ -355,7 +355,7 @@ mod tests { let temp_dir = tempdir()?; let genesis_state_path = temp_dir.path().join("genesis_state"); let genesis_code_path = temp_dir.path().join("genesis_code.wasm"); - let up_chain = UpChain { + let up_chain = Registration { id: 2000, genesis_state: genesis_state_path.clone(), genesis_code: genesis_code_path.clone(), diff --git a/crates/pop-cli/src/common/chain.rs b/crates/pop-cli/src/common/chain.rs index 543c60375..e886abe1a 100644 --- a/crates/pop-cli/src/common/chain.rs +++ b/crates/pop-cli/src/common/chain.rs @@ -16,7 +16,7 @@ pub(crate) struct Chain { } // Configures a chain by resolving the URL and fetching its metadata. -pub(crate) async fn configure_chain( +pub(crate) async fn configure( input_message: &str, default_input: &str, url: &Option, @@ -50,10 +50,10 @@ mod tests { const POP_NETWORK_TESTNET_URL: &str = "wss://rpc1.paseo.popnetwork.xyz"; #[tokio::test] - async fn configure_chain_works() -> Result<()> { + async fn configure_works() -> Result<()> { let message = "Enter the URL of the chain:"; let mut cli = MockCli::new().expect_input(message, POP_NETWORK_TESTNET_URL.into()); - let chain = configure_chain(message, POP_NETWORK_TESTNET_URL, &None, &mut cli).await?; + let chain = configure(message, POP_NETWORK_TESTNET_URL, &None, &mut cli).await?; assert_eq!(chain.url, Url::parse(POP_NETWORK_TESTNET_URL)?); cli.verify() } diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 56b9792ba..f43f3ffcd 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -72,7 +72,7 @@ pub fn prompt_to_use_wallet(cli: &mut impl Cli) -> anyhow::Result { // Sign and submit an extrinsic using wallet integration. #[cfg(feature = "parachain")] -pub(crate) async fn submit_extrinsic_with_wallet( +pub(crate) async fn submit_extrinsic( client: &OnlineClient, url: &Url, call_data: Vec, From 88138f50a79704a6569f8409b064f02a274b4c48 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 18:16:21 +0100 Subject: [PATCH 104/117] refactor: relay_chain_url --- crates/pop-cli/src/commands/up/mod.rs | 2 +- crates/pop-cli/src/commands/up/parachain.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 07032bfc9..5d456e227 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -156,7 +156,7 @@ mod tests { instantiate_template_dir(&Parachain::Standard, &project_path, None, config)?; let mut args = create_up_args(project_path)?; - args.parachain.relay_url = Some(Url::parse("wss://polkadot-rpc.publicnode.com")?); + args.parachain.relay_chain_url = Some(Url::parse("wss://polkadot-rpc.publicnode.com")?); args.parachain.id = Some(2000); args.parachain.genesis_code = Some(PathBuf::from("path/to/genesis")); args.parachain.genesis_state = Some(PathBuf::from("path/to/state")); diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 8a7cdea35..a9f1879f2 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -35,7 +35,7 @@ pub struct UpCommand { pub(crate) genesis_code: Option, /// Websocket endpoint of the relay chain. #[arg(long)] - pub(crate) relay_url: Option, + pub(crate) relay_chain_url: Option, } impl UpCommand { @@ -61,7 +61,7 @@ impl UpCommand { let chain = configure( "Enter the relay chain node URL to deploy your parachain", DEFAULT_URL, - &self.relay_url, + &self.relay_chain_url, cli, ) .await?; From 66541a9d9738703e405b042a783a8c0cdd27eeae Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 18:17:53 +0100 Subject: [PATCH 105/117] refactor: rename prepare_reserve_call_data and prepare_register_call_data --- crates/pop-cli/src/commands/up/parachain.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index a9f1879f2..d0194c665 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -107,13 +107,13 @@ impl Registration { // Registers by submitting an extrinsic. async fn register(&self, cli: &mut impl Cli) -> Result<()> { cli.info("Registering a parachain ID")?; - let call_data = self.prepare_register_parachain_call_data(cli)?; + let call_data = self.prepare_register_call_data(cli)?; submit_extrinsic(&self.chain.client, &self.chain.url, call_data, cli).await?; Ok(()) } // Prepares and returns the encoded call data for registering a parachain. - fn prepare_register_parachain_call_data(&self, cli: &mut impl Cli) -> Result> { + fn prepare_register_call_data(&self, cli: &mut impl Cli) -> Result> { let Registration { id, genesis_code, genesis_state, chain } = self; let dispatchable = find_dispatchable_by_name( &chain.pallets, @@ -137,7 +137,7 @@ impl Registration { // Reserves an ID by submitting an extrinsic. async fn reserve(chain: &Chain, cli: &mut impl Cli) -> Result { - let call_data = prepare_reserve_parachain_call_data(chain, cli)?; + let call_data = prepare_reserve_call_data(chain, cli)?; let events = submit_extrinsic(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("ID reservation failed: {}", e))?; @@ -149,7 +149,7 @@ async fn reserve(chain: &Chain, cli: &mut impl Cli) -> Result { } // Prepares and returns the encoded call data for reserving an ID. -fn prepare_reserve_parachain_call_data(chain: &Chain, cli: &mut impl Cli) -> Result> { +fn prepare_reserve_call_data(chain: &Chain, cli: &mut impl Cli) -> Result> { let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), @@ -230,7 +230,7 @@ mod tests { } #[tokio::test] - async fn prepare_reserve_parachain_call_data_works() -> Result<()> { + async fn prepare_reserve_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = configure( "Enter the relay chain node URL to deploy your parachain", @@ -239,7 +239,7 @@ mod tests { &mut cli, ) .await?; - let call_data = prepare_reserve_parachain_call_data(&chain, &mut cli)?; + let call_data = prepare_reserve_call_data(&chain, &mut cli)?; // Encoded call data for a reserve extrinsic. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4605 let encoded_reserve_extrinsic: &str = "0x4605"; @@ -342,7 +342,7 @@ mod tests { } #[tokio::test] - async fn prepare_register_parachain_call_data_works() -> Result<()> { + async fn prepare_register_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = configure( "Enter the relay chain node URL to deploy your parachain", @@ -364,14 +364,14 @@ mod tests { // Expect failure when the genesis state file cannot be read. assert!(matches!( - up_chain.prepare_register_parachain_call_data(&mut cli), + up_chain.prepare_register_call_data(&mut cli), Err(message) if message.to_string().contains("Failed to read genesis state file") )); std::fs::write(&genesis_state_path, "0x1234")?; // Expect failure when the genesis code file cannot be read. assert!(matches!( - up_chain.prepare_register_parachain_call_data(&mut cli), + up_chain.prepare_register_call_data(&mut cli), Err(message) if message.to_string().contains("Failed to read genesis code file") )); std::fs::write(&genesis_code_path, "0x1234")?; @@ -380,7 +380,7 @@ mod tests { // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4600d0070000081234081234 let encoded_register_extrinsic: &str = "0x4600d0070000081234081234"; assert_eq!( - up_chain.prepare_register_parachain_call_data(&mut cli)?, + up_chain.prepare_register_call_data(&mut cli)?, decode_call_data(encoded_register_extrinsic)? ); Ok(()) From 4fdbdf03186e8de6e699bde310bc02f1bba956ab Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 18:19:36 +0100 Subject: [PATCH 106/117] test: remove unnecesary test --- crates/pop-cli/src/commands/up/parachain.rs | 83 ++------------------- 1 file changed, 5 insertions(+), 78 deletions(-) diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index d0194c665..5619472a3 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -191,15 +191,9 @@ async fn generate_spec_files( #[cfg(test)] mod tests { use super::*; - use crate::{ - build::spec::{ChainType, RelayChain}, - cli::MockCli, - }; - use duct::cmd; - use pop_common::Profile; + use crate::cli::MockCli; use pop_parachains::decode_call_data; - use std::{env, fs}; - use strum::{EnumMessage, VariantArray}; + use std::fs; use tempfile::tempdir; use url::Url; @@ -248,7 +242,7 @@ mod tests { } #[tokio::test] - async fn reserve_parachain_id_fails_wrong_chain() -> Result<()> { + async fn reserve_id_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() .expect_intro("Deploy a chain") .expect_info("Reserving a parachain ID") @@ -258,7 +252,7 @@ mod tests { id: None, genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), - relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + relay_chain_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: None, } .execute(&mut cli) @@ -267,60 +261,6 @@ mod tests { cli.verify() } - #[tokio::test] - async fn resolve_genesis_files_fails_wrong_path() -> Result<()> { - // Mock a project path without node. - let name = "hello_world"; - let temp_dir = tempfile::tempdir()?; - let path = temp_dir.path(); - let project_path = path.join(name); - cmd("cargo", ["new", name, "--bin"]).dir(&path).run()?; - let original_dir = std::env::current_dir()?; - - let mut cli = MockCli::new() - .expect_intro("Deploy a chain") - .expect_info("Generating the chain spec for your parachain") - .expect_input("Provide the chain specification to use (e.g. dev, local, custom or a path to an existing file)", "dev".to_string()) - .expect_input( - "Name or path for the plain chain spec file:", "output_file".to_string()) - .expect_input( - "Enter the protocol ID that will identify your network:", "protocol_id".to_string()) - .expect_select( - "Choose the chain type: ", - Some(false), - true, - Some(get_messages(ChainType::VARIANTS)), - ChainType::Development as usize, - ).expect_select( - "Choose the relay your chain will be connecting to: ", - Some(false), - true, - Some(get_messages(RelayChain::VARIANTS)), - RelayChain::PaseoLocal as usize, - ).expect_select( - "Choose the build profile of the binary that should be used: ", - Some(false), - true, - Some(get_messages(Profile::VARIANTS)), - Profile::Release as usize, - ).expect_outro_cancel(format!("Failed to get manifest path: {}/node/Cargo.toml", fs::canonicalize(&project_path)?.display().to_string())); - - UpCommand { - id: Some(2000), - genesis_state: None, - genesis_code: None, - relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), - path: Some(project_path.clone()), - } - .execute(&mut cli) - .await?; - - assert_eq!(fs::canonicalize(env::current_dir()?)?, fs::canonicalize(project_path)?); - // Reset working directory back to original - std::env::set_current_dir(original_dir)?; - cli.verify() - } - #[tokio::test] async fn register_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() @@ -332,7 +272,7 @@ mod tests { id: Some(2000), genesis_state: Some(genesis_state.clone()), genesis_code: Some(genesis_code.clone()), - relay_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), + relay_chain_url: Some(Url::parse(POP_NETWORK_TESTNET_URL)?), path: None, } .execute(&mut cli) @@ -397,17 +337,4 @@ mod tests { Ok((genesis_state_path, genesis_code_path)) } - - // Generic helper function to convert enum variants into (message, detailed message) tuples. - fn get_messages>(variants: &[T]) -> Vec<(String, String)> { - variants - .iter() - .map(|variant| { - ( - variant.get_message().unwrap_or(variant.as_ref()).into(), - variant.get_detailed_message().unwrap_or_default().into(), - ) - }) - .collect() - } } From 6a9bb4a83a33a68f76ed425e8f28703aa8e34133 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Sat, 1 Mar 2025 18:22:42 +0100 Subject: [PATCH 107/117] refactor: remove events module --- crates/pop-cli/src/commands/up/parachain.rs | 9 +++--- crates/pop-parachains/src/lib.rs | 3 +- crates/pop-parachains/src/relay.rs | 15 +++++++++- crates/pop-parachains/src/utils/events.rs | 33 --------------------- crates/pop-parachains/src/utils/mod.rs | 1 - 5 files changed, 20 insertions(+), 41 deletions(-) delete mode 100644 crates/pop-parachains/src/utils/events.rs diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index 5619472a3..65c01df71 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -11,7 +11,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use clap::Args; -use pop_parachains::{extract_para_id_from_event, find_dispatchable_by_name, Action, Payload}; +use pop_parachains::{find_dispatchable_by_name, Action, Payload, Reserved}; use std::path::{Path, PathBuf}; use url::Url; @@ -141,9 +141,10 @@ async fn reserve(chain: &Chain, cli: &mut impl Cli) -> Result { let events = submit_extrinsic(&chain.client, &chain.url, call_data, cli) .await .map_err(|e| anyhow::anyhow!("ID reservation failed: {}", e))?; - let id = extract_para_id_from_event(&events).map_err(|_| { - anyhow::anyhow!("Unable to parse the event. Specify the ID manually with `--id`.") - })?; + let id = events + .find_first::()? + .ok_or(anyhow::anyhow!("Unable to parse the event. Specify the ID manually with `--id`."))? + .para_id; cli.success(format!("Successfully reserved ID: {}", id))?; Ok(id) } diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 4fe7003b1..26bbe6227 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -31,8 +31,7 @@ pub use errors::Error; pub use indexmap::IndexSet; pub use new_pallet::{create_pallet_template, new_pallet_options::*, TemplatePalletConfig}; pub use new_parachain::instantiate_template_dir; -pub use relay::{clear_dmpq, RelayChain}; -pub use utils::events::extract_para_id_from_event; +pub use relay::{clear_dmpq, RelayChain, Reserved}; // External export from subxt. pub use subxt::{ blocks::ExtrinsicEvents, diff --git a/crates/pop-parachains/src/relay.rs b/crates/pop-parachains/src/relay.rs index 6ded8c497..43782f3c6 100644 --- a/crates/pop-parachains/src/relay.rs +++ b/crates/pop-parachains/src/relay.rs @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-3.0 use crate::{call, DynamicPayload, Error}; +use scale::{Decode, Encode}; use sp_core::twox_128; use subxt::{ config::BlockHash, dynamic::{self, Value}, - ext::sp_core, + events::StaticEvent, + ext::{scale_decode::DecodeAsType, scale_encode::EncodeAsType, sp_core}, OnlineClient, PolkadotConfig, }; @@ -88,6 +90,17 @@ impl RelayChain { } } +#[derive(Debug, Encode, Decode, DecodeAsType, EncodeAsType)] +#[decode_as_type(crate_path = "subxt::ext::scale_decode")] +#[encode_as_type(crate_path = "subxt::ext::scale_encode")] +pub struct Reserved { + pub para_id: u32, +} +impl StaticEvent for Reserved { + const PALLET: &'static str = "Registrar"; + const EVENT: &'static str = "Reserved"; +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/pop-parachains/src/utils/events.rs b/crates/pop-parachains/src/utils/events.rs deleted file mode 100644 index 2f47e7e3e..000000000 --- a/crates/pop-parachains/src/utils/events.rs +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -use scale::{Decode, Encode}; -use subxt::{ - blocks::ExtrinsicEvents, - events::StaticEvent, - ext::{scale_decode::DecodeAsType, scale_encode::EncodeAsType}, - SubstrateConfig, -}; - -use crate::Error; - -#[derive(Debug, Encode, Decode, DecodeAsType, EncodeAsType)] -#[decode_as_type(crate_path = "subxt::ext::scale_decode")] -#[encode_as_type(crate_path = "subxt::ext::scale_encode")] -pub struct Reserved { - pub para_id: u32, -} -impl StaticEvent for Reserved { - const PALLET: &'static str = "Registrar"; - const EVENT: &'static str = "Reserved"; -} - -/// Extracts the `para_id` field from a `Reserved` event. -/// -/// # Arguments -/// * `events` - The extrinsic events from a transaction. -pub fn extract_para_id_from_event(events: &ExtrinsicEvents) -> Result { - let reserved_event = events.find_first::()?; - reserved_event - .map(|event| event.para_id) - .ok_or(Error::EventNotFound("Reserved".to_string())) -} diff --git a/crates/pop-parachains/src/utils/mod.rs b/crates/pop-parachains/src/utils/mod.rs index a59eacd9a..265ebafd4 100644 --- a/crates/pop-parachains/src/utils/mod.rs +++ b/crates/pop-parachains/src/utils/mod.rs @@ -1,4 +1,3 @@ // SPDX-License-Identifier: GPL-3.0 -pub mod events; pub mod helpers; From 9449d9fa1cb5d8f9609b5554067b69a56d91c8fc Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 09:18:52 +0100 Subject: [PATCH 108/117] refactor: rename parachain to rollup --- crates/pop-cli/src/commands/up/mod.rs | 16 ++++----- .../commands/up/{parachain.rs => rollup.rs} | 34 ++++++++----------- 2 files changed, 22 insertions(+), 28 deletions(-) rename crates/pop-cli/src/commands/up/{parachain.rs => rollup.rs} (92%) diff --git a/crates/pop-cli/src/commands/up/mod.rs b/crates/pop-cli/src/commands/up/mod.rs index 5d456e227..287f62a31 100644 --- a/crates/pop-cli/src/commands/up/mod.rs +++ b/crates/pop-cli/src/commands/up/mod.rs @@ -12,7 +12,7 @@ mod contract; #[cfg(feature = "parachain")] mod network; #[cfg(feature = "parachain")] -mod parachain; +mod rollup; /// Arguments for launching or deploying a project. #[derive(Args, Clone)] @@ -29,7 +29,7 @@ pub(crate) struct UpArgs { #[command(flatten)] #[cfg(feature = "parachain")] - pub(crate) parachain: parachain::UpCommand, + pub(crate) rollup: rollup::UpCommand, #[command(flatten)] #[cfg(feature = "contract")] @@ -79,7 +79,7 @@ impl Command { } #[cfg(feature = "parachain")] if pop_parachains::is_supported(project_path.as_deref())? { - let mut cmd = args.parachain; + let mut cmd = args.rollup; cmd.path = project_path; cmd.execute(cli).await?; return Ok("parachain"); @@ -122,7 +122,7 @@ mod tests { skip_confirm: false, valid: false, }, - parachain: parachain::UpCommand::default(), + rollup: rollup::UpCommand::default(), command: None, }) } @@ -156,10 +156,10 @@ mod tests { instantiate_template_dir(&Parachain::Standard, &project_path, None, config)?; let mut args = create_up_args(project_path)?; - args.parachain.relay_chain_url = Some(Url::parse("wss://polkadot-rpc.publicnode.com")?); - args.parachain.id = Some(2000); - args.parachain.genesis_code = Some(PathBuf::from("path/to/genesis")); - args.parachain.genesis_state = Some(PathBuf::from("path/to/state")); + args.rollup.relay_chain_url = Some(Url::parse("wss://polkadot-rpc.publicnode.com")?); + args.rollup.id = Some(2000); + args.rollup.genesis_code = Some(PathBuf::from("path/to/genesis")); + args.rollup.genesis_state = Some(PathBuf::from("path/to/state")); let mut cli = MockCli::new(); assert_eq!(Command::execute_project_deployment(args, &mut cli).await?, "parachain"); cli.verify() diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/rollup.rs similarity index 92% rename from crates/pop-cli/src/commands/up/parachain.rs rename to crates/pop-cli/src/commands/up/rollup.rs index 65c01df71..01a509fe5 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/rollup.rs @@ -58,24 +58,20 @@ impl UpCommand { // Prepares the chain for registration by setting up its configuration. async fn prepare_for_registration(self, cli: &mut impl Cli) -> Result { - let chain = configure( - "Enter the relay chain node URL to deploy your parachain", - DEFAULT_URL, - &self.relay_chain_url, - cli, - ) - .await?; - let id = self.resolve_parachain_id(&chain, cli).await?; + let chain = + configure("Enter the relay chain node URL", DEFAULT_URL, &self.relay_chain_url, cli) + .await?; + let id = self.resolve_id(&chain, cli).await?; let (genesis_code, genesis_state) = self.resolve_genesis_files(id, cli).await?; Ok(Registration { id, genesis_state, genesis_code, chain }) } - // Resolves the parachain ID, reserving a new one if necessary. - async fn resolve_parachain_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + // Resolves the ID, reserving a new one if necessary. + async fn resolve_id(&self, chain: &Chain, cli: &mut impl Cli) -> Result { match self.id { Some(id) => Ok(id), None => { - cli.info("Reserving a parachain ID")?; + cli.info("Reserving an ID")?; reserve(chain, cli).await }, } @@ -89,14 +85,14 @@ impl UpCommand { match (&self.genesis_code, &self.genesis_state) { (Some(code), Some(state)) => Ok((code.clone(), state.clone())), _ => { - cli.info("Generating the chain spec for your parachain")?; + cli.info("Generating the chain spec for your project")?; generate_spec_files(id, self.path.as_deref(), cli).await }, } } } -// Represents the configuration for deploying a chain. +// Represents the configuration for rollup registration. pub(crate) struct Registration { id: u32, genesis_state: PathBuf, @@ -112,7 +108,7 @@ impl Registration { Ok(()) } - // Prepares and returns the encoded call data for registering a parachain. + // Prepares and returns the encoded call data for registering a chain. fn prepare_register_call_data(&self, cli: &mut impl Cli) -> Result> { let Registration { id, genesis_code, genesis_state, chain } = self; let dispatchable = find_dispatchable_by_name( @@ -203,10 +199,8 @@ mod tests { #[tokio::test] async fn prepare_for_registration_works() -> Result<()> { - let mut cli = MockCli::new().expect_input( - "Enter the relay chain node URL to deploy your parachain", - POLKADOT_NETWORK_URL.into(), - ); + let mut cli = MockCli::new() + .expect_input("Enter the relay chain node URL", POLKADOT_NETWORK_URL.into()); let (genesis_state, genesis_code) = create_temp_genesis_files()?; let chain_config = UpCommand { id: Some(2000), @@ -228,7 +222,7 @@ mod tests { async fn prepare_reserve_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = configure( - "Enter the relay chain node URL to deploy your parachain", + "Enter the relay chain node URL", DEFAULT_URL, &Some(Url::parse(POLKADOT_NETWORK_URL)?), &mut cli, @@ -286,7 +280,7 @@ mod tests { async fn prepare_register_call_data_works() -> Result<()> { let mut cli = MockCli::new(); let chain = configure( - "Enter the relay chain node URL to deploy your parachain", + "Enter the relay chain node URL", DEFAULT_URL, &Some(Url::parse(POLKADOT_NETWORK_URL)?), &mut cli, From 61e49291ea7613fa62116497b65b0cffde6d9132 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 09:36:56 +0100 Subject: [PATCH 109/117] chore: improve succesful message --- crates/pop-cli/src/commands/up/rollup.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/commands/up/rollup.rs b/crates/pop-cli/src/commands/up/rollup.rs index 01a509fe5..e842442e4 100644 --- a/crates/pop-cli/src/commands/up/rollup.rs +++ b/crates/pop-cli/src/commands/up/rollup.rs @@ -8,6 +8,7 @@ use crate::{ chain::{configure, Chain}, wallet::submit_extrinsic, }, + style::style, }; use anyhow::{anyhow, Result}; use clap::Args; @@ -50,7 +51,14 @@ impl UpCommand { }, }; match config.register(cli).await { - Ok(_) => cli.success("Chain deployed successfully")?, + Ok(_) => cli.success(format!( + "Deployment successfully {}", + style(format!( + "https://polkadot.js.org/apps/?rpc={}#/parachains", + config.chain.url + )) + .dim() + ))?, Err(e) => cli.outro_cancel(format!("{}", e))?, } Ok(()) @@ -71,7 +79,7 @@ impl UpCommand { match self.id { Some(id) => Ok(id), None => { - cli.info("Reserving an ID")?; + cli.info(format!("Reserving an ID. You will need to sign a transaction to reserve an ID on {} using the `Registrar::reserve` function.", chain.url))?; reserve(chain, cli).await }, } @@ -102,7 +110,7 @@ pub(crate) struct Registration { impl Registration { // Registers by submitting an extrinsic. async fn register(&self, cli: &mut impl Cli) -> Result<()> { - cli.info("Registering a parachain ID")?; + cli.info(format!("Registering. You will need to sign a transaction on {} using the `Registrar::register` function.", self.chain.url))?; let call_data = self.prepare_register_call_data(cli)?; submit_extrinsic(&self.chain.client, &self.chain.url, call_data, cli).await?; Ok(()) @@ -240,7 +248,7 @@ mod tests { async fn reserve_id_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() .expect_intro("Deploy a chain") - .expect_info("Reserving a parachain ID") + .expect_info(format!("Reserving an ID. You will need to sign a transaction to reserve an ID on {} using the `Registrar::reserve` function.", Url::parse(POP_NETWORK_TESTNET_URL)?.as_str())) .expect_outro_cancel("Failed to find the pallet Registrar"); let (genesis_state, genesis_code) = create_temp_genesis_files()?; UpCommand { @@ -260,7 +268,7 @@ mod tests { async fn register_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() .expect_intro("Deploy a chain") - .expect_info("Registering a parachain ID") + .expect_info(format!("Registering. You will need to sign a transaction on {} using the `Registrar::register` function.", Url::parse(POP_NETWORK_TESTNET_URL)?.as_str())) .expect_outro_cancel("Failed to find the pallet Registrar"); let (genesis_state, genesis_code) = create_temp_genesis_files()?; UpCommand { From 7d3028ff334da29b85f0ce7e16dd12eba2e7aef5 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 09:43:05 +0100 Subject: [PATCH 110/117] chore: change intro title to use rollup --- crates/pop-cli/src/commands/up/rollup.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/rollup.rs b/crates/pop-cli/src/commands/up/rollup.rs index e842442e4..2fe113b80 100644 --- a/crates/pop-cli/src/commands/up/rollup.rs +++ b/crates/pop-cli/src/commands/up/rollup.rs @@ -42,7 +42,7 @@ pub struct UpCommand { impl UpCommand { /// Executes the command. pub(crate) async fn execute(self, cli: &mut impl Cli) -> Result<()> { - cli.intro("Deploy a chain")?; + cli.intro("Deploy a rollup")?; let config = match self.prepare_for_registration(cli).await { Ok(chain) => chain, Err(e) => { @@ -247,7 +247,7 @@ mod tests { #[tokio::test] async fn reserve_id_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() - .expect_intro("Deploy a chain") + .expect_intro("Deploy a rollup") .expect_info(format!("Reserving an ID. You will need to sign a transaction to reserve an ID on {} using the `Registrar::reserve` function.", Url::parse(POP_NETWORK_TESTNET_URL)?.as_str())) .expect_outro_cancel("Failed to find the pallet Registrar"); let (genesis_state, genesis_code) = create_temp_genesis_files()?; @@ -267,7 +267,7 @@ mod tests { #[tokio::test] async fn register_fails_wrong_chain() -> Result<()> { let mut cli = MockCli::new() - .expect_intro("Deploy a chain") + .expect_intro("Deploy a rollup") .expect_info(format!("Registering. You will need to sign a transaction on {} using the `Registrar::register` function.", Url::parse(POP_NETWORK_TESTNET_URL)?.as_str())) .expect_outro_cancel("Failed to find the pallet Registrar"); let (genesis_state, genesis_code) = create_temp_genesis_files()?; From 3ed1d833bb070387a2e6f56a5e10817b21064675 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 11:13:52 +0100 Subject: [PATCH 111/117] docs: comments for Reserved event --- crates/pop-parachains/src/relay.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pop-parachains/src/relay.rs b/crates/pop-parachains/src/relay.rs index 43782f3c6..6aa696544 100644 --- a/crates/pop-parachains/src/relay.rs +++ b/crates/pop-parachains/src/relay.rs @@ -90,10 +90,12 @@ impl RelayChain { } } +/// A event emitted when an id has been registered. #[derive(Debug, Encode, Decode, DecodeAsType, EncodeAsType)] #[decode_as_type(crate_path = "subxt::ext::scale_decode")] #[encode_as_type(crate_path = "subxt::ext::scale_encode")] pub struct Reserved { + /// The id that has been reserved. pub para_id: u32, } impl StaticEvent for Reserved { From bfbb8e44ba4218d20ce9f9b834793f8cf47068e2 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 15:56:32 +0100 Subject: [PATCH 112/117] refactor: clean code to avoid unnecesary clone --- crates/pop-parachains/src/call/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/pop-parachains/src/call/mod.rs b/crates/pop-parachains/src/call/mod.rs index 0867079bf..d3e368711 100644 --- a/crates/pop-parachains/src/call/mod.rs +++ b/crates/pop-parachains/src/call/mod.rs @@ -64,16 +64,13 @@ pub fn construct_proxy_extrinsic( // using `parse_dispatchable_arguments`, while the last parameter (which is the call) // must be manually added. let required_params: Vec = proxy_function.params.iter().take(2).cloned().collect(); - let parsed_args: Vec = metadata::parse_dispatchable_arguments( + let mut parsed_args: Vec = metadata::parse_dispatchable_arguments( &required_params, vec![proxied_account, "None()".to_string()], )?; - - Ok(subxt::dynamic::tx( - "Proxy", - "proxy", - [parsed_args[0].clone(), parsed_args[1].clone(), xt.into_value()].to_vec(), - )) + let real = parsed_args.remove(0); + let proxy_type = parsed_args.remove(0); + Ok(subxt::dynamic::tx("Proxy", "proxy", [real, proxy_type, xt.into_value()].to_vec())) } /// Signs and submits a given extrinsic. From 34b95399b9308df0c38f1fcaea77b71019b3cd91 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 16:15:34 +0100 Subject: [PATCH 113/117] feat: wrap Id by default in the proxy address and improve comment --- crates/pop-cli/src/commands/up/rollup.rs | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/pop-cli/src/commands/up/rollup.rs b/crates/pop-cli/src/commands/up/rollup.rs index 19ff56473..f598d6a08 100644 --- a/crates/pop-cli/src/commands/up/rollup.rs +++ b/crates/pop-cli/src/commands/up/rollup.rs @@ -39,8 +39,10 @@ pub struct UpCommand { /// Websocket endpoint of the relay chain. #[arg(long)] pub(crate) relay_chain_url: Option, - /// Proxied account address. The proxy can act on behalf of this account. - /// Specify the address type, e.g. `Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)`. + /// Proxied address. Your account must be registered as a proxy which can act on behalf of this + /// account. You can specify the MultiAddress type explicitly, e.g., + /// `Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)`. If no type is provided, it + /// defaults to `Id()`. #[arg(long = "proxy")] pub(crate) proxied_address: Option, } @@ -83,12 +85,20 @@ impl UpCommand { // Retrieves the proxied address, prompting the user if none is specified. fn resolve_proxied_address(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + let proxy = find_dispatchable_by_name(&chain.pallets, "Proxy", "proxy")?; if let Some(addr) = &self.proxied_address { - return Ok(ProxyConfig::Address(addr.clone())); + let valid_multi_address: Vec = + proxy.params[0].sub_params.iter().map(|p| p.name.to_string()).collect(); + return Ok(ProxyConfig::Address( + if valid_multi_address.iter().any(|t| addr.starts_with(&format!("{}(", t))) { + addr.to_string() + } else { + format!("Id({})", addr) + }, + )); } if cli.confirm("Would you like to use a proxy for registration? This is considered a best practice.").interact()? { - cli.info("Enter the account the proxy will represent.")?; - let proxy = find_dispatchable_by_name(&chain.pallets, "Proxy", "proxy")?; + cli.info("Enter the account that the proxy will make a call on behalf of.")?; let address = prompt_for_param(cli, &proxy.params[0])?; Ok(ProxyConfig::Address(address)) } else { @@ -283,7 +293,7 @@ mod tests { async fn resolve_proxied_address_works() -> Result<()> { let mut cli = MockCli::new() .expect_confirm("Would you like to use a proxy for registration? This is considered a best practice.", true) - .expect_info("Enter the account the proxy will represent.") + .expect_info("Enter the account that the proxy will make a call on behalf of.") .expect_select( "Select the value for the parameter: real", Some(true), @@ -330,9 +340,7 @@ mod tests { cli = MockCli::new(); let proxied_address = UpCommand { - proxied_address: Some( - "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string(), - ), + proxied_address: Some("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty".to_string()), ..Default::default() } .resolve_proxied_address(&chain, &mut cli)?; From f18de68e93111b9f14bde797ec587825157f3696 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 16:23:44 +0100 Subject: [PATCH 114/117] refactor: remove ProxyConfig and use Proxy --- crates/pop-cli/src/commands/up/rollup.rs | 70 ++++++++---------------- 1 file changed, 23 insertions(+), 47 deletions(-) diff --git a/crates/pop-cli/src/commands/up/rollup.rs b/crates/pop-cli/src/commands/up/rollup.rs index f598d6a08..3ea204a22 100644 --- a/crates/pop-cli/src/commands/up/rollup.rs +++ b/crates/pop-cli/src/commands/up/rollup.rs @@ -18,6 +18,8 @@ use pop_parachains::{ use std::path::{Path, PathBuf}; use url::Url; +type Proxy = Option; + const DEFAULT_URL: &str = "wss://paseo.rpc.amforc.com/"; const HELP_HEADER: &str = "Chain deployment options"; @@ -84,35 +86,29 @@ impl UpCommand { } // Retrieves the proxied address, prompting the user if none is specified. - fn resolve_proxied_address(&self, chain: &Chain, cli: &mut impl Cli) -> Result { + fn resolve_proxied_address(&self, chain: &Chain, cli: &mut impl Cli) -> Result { let proxy = find_dispatchable_by_name(&chain.pallets, "Proxy", "proxy")?; if let Some(addr) = &self.proxied_address { let valid_multi_address: Vec = proxy.params[0].sub_params.iter().map(|p| p.name.to_string()).collect(); - return Ok(ProxyConfig::Address( + return Ok( if valid_multi_address.iter().any(|t| addr.starts_with(&format!("{}(", t))) { - addr.to_string() + Some(addr.to_string()) } else { - format!("Id({})", addr) + Some(format!("Id({})", addr)) }, - )); + ); } if cli.confirm("Would you like to use a proxy for registration? This is considered a best practice.").interact()? { cli.info("Enter the account that the proxy will make a call on behalf of.")?; let address = prompt_for_param(cli, &proxy.params[0])?; - Ok(ProxyConfig::Address(address)) - } else { - Ok(ProxyConfig::None) + return Ok(Some(address)); } + Ok(None) } // Resolves the ID, reserving a new one if necessary. - async fn resolve_id( - &self, - chain: &Chain, - proxy: &ProxyConfig, - cli: &mut impl Cli, - ) -> Result { + async fn resolve_id(&self, chain: &Chain, proxy: &Proxy, cli: &mut impl Cli) -> Result { match self.id { Some(id) => Ok(id), None => { @@ -137,19 +133,13 @@ impl UpCommand { } } -#[derive(Clone, Debug, PartialEq)] -enum ProxyConfig { - None, - Address(String), -} - // Represents the configuration for rollup registration. pub(crate) struct Registration { id: u32, genesis_state: PathBuf, genesis_code: PathBuf, chain: Chain, - proxy: ProxyConfig, + proxy: Proxy, } impl Registration { // Registers by submitting an extrinsic. @@ -179,7 +169,7 @@ impl Registration { ..Default::default() } .prepare_extrinsic(&chain.client, cli)?; - if let ProxyConfig::Address(addr) = proxy { + if let Some(addr) = proxy { xt = construct_proxy_extrinsic(&chain.pallets, addr.to_string(), xt)?; } Ok(xt.encode_call_data(&chain.client.metadata())?) @@ -187,7 +177,7 @@ impl Registration { } // Reserves an ID by submitting an extrinsic. -async fn reserve(chain: &Chain, proxy: &ProxyConfig, cli: &mut impl Cli) -> Result { +async fn reserve(chain: &Chain, proxy: &Proxy, cli: &mut impl Cli) -> Result { let call_data = prepare_reserve_call_data(chain, proxy, cli)?; let events = submit_extrinsic(&chain.client, &chain.url, call_data, cli) .await @@ -201,11 +191,7 @@ async fn reserve(chain: &Chain, proxy: &ProxyConfig, cli: &mut impl Cli) -> Resu } // Prepares and returns the encoded call data for reserving an ID. -fn prepare_reserve_call_data( - chain: &Chain, - proxy: &ProxyConfig, - cli: &mut impl Cli, -) -> Result> { +fn prepare_reserve_call_data(chain: &Chain, proxy: &Proxy, cli: &mut impl Cli) -> Result> { let dispatchable = find_dispatchable_by_name( &chain.pallets, Action::Reserve.pallet_name(), @@ -213,7 +199,7 @@ fn prepare_reserve_call_data( )?; let mut xt = Call { function: dispatchable.clone(), use_wallet: true, ..Default::default() } .prepare_extrinsic(&chain.client, cli)?; - if let ProxyConfig::Address(addr) = proxy { + if let Some(addr) = proxy { xt = construct_proxy_extrinsic(&chain.pallets, addr.to_string(), xt)?; } Ok(xt.encode_call_data(&chain.client.metadata())?) @@ -282,9 +268,7 @@ mod tests { assert_eq!(chain_config.chain.url, Url::parse(POLKADOT_NETWORK_URL)?); assert_eq!( chain_config.proxy, - ProxyConfig::Address( - "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string() - ) + Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()) ); cli.verify() } @@ -324,9 +308,7 @@ mod tests { let proxied_address = UpCommand::default().resolve_proxied_address(&chain, &mut cli)?; assert_eq!( proxied_address, - ProxyConfig::Address( - "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string() - ) + Some("Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string()) ); cli.verify()?; @@ -335,7 +317,7 @@ mod tests { false, ); let proxied_address = UpCommand::default().resolve_proxied_address(&chain, &mut cli)?; - assert_eq!(proxied_address, ProxyConfig::None); + assert_eq!(proxied_address, None); cli.verify()?; cli = MockCli::new(); @@ -346,9 +328,7 @@ mod tests { .resolve_proxied_address(&chain, &mut cli)?; assert_eq!( proxied_address, - ProxyConfig::Address( - "Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string() - ) + Some("Id(5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty)".to_string()) ); cli.verify() } @@ -363,7 +343,7 @@ mod tests { &mut cli, ) .await?; - let call_data = prepare_reserve_call_data(&chain, &ProxyConfig::None, &mut cli)?; + let call_data = prepare_reserve_call_data(&chain, &None, &mut cli)?; // Encoded call data for a reserve extrinsic. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x4605 let encoded_reserve_extrinsic: &str = "0x4605"; @@ -372,9 +352,7 @@ mod tests { // Ensure `prepare_reserve_call_data` works with a proxy. let call_data = prepare_reserve_call_data( &chain, - &ProxyConfig::Address( - "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), - ), + &Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()), &mut cli, )?; // Encoded call data for a proxy extrinsic with reserve as the call. @@ -446,7 +424,7 @@ mod tests { genesis_state: genesis_state_path.clone(), genesis_code: genesis_code_path.clone(), chain, - proxy: ProxyConfig::None, + proxy: None, }; // Expect failure when the genesis state file cannot be read. @@ -472,9 +450,7 @@ mod tests { ); // Ensure `prepare_register_call_data` works with a proxy. - up_chain.proxy = ProxyConfig::Address( - "Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string(), - ); + up_chain.proxy = Some("Id(13czcAAt6xgLwZ8k6ZpkrRL5V2pjKEui3v9gHAN9PoxYZDbf)".to_string()); let call_data = up_chain.prepare_register_call_data(&mut cli)?; // Encoded call data for a proxy extrinsic with register as the call. // Reference: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x1d000073ebf9c947490b9170ea4fd3031ae039452e428531317f76bf0a02124f8166de004600d0070000081234081234 From 59b5602cb2d9e4e5ac9a3d15d8d5fe14553bc3ff Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 16:44:39 +0100 Subject: [PATCH 115/117] fix: update version deny --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bda337941..f7c2dac67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v1 + - uses: EmbarkStudios/cargo-deny-action@v2 docker: runs-on: ubuntu-latest From 5d3d931a3028e8d277c7ce23bfc359347b9be2f6 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 16:47:24 +0100 Subject: [PATCH 116/117] fix: update .toml --- deny.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deny.toml b/deny.toml index b1e67ce44..1b156848e 100644 --- a/deny.toml +++ b/deny.toml @@ -3,7 +3,7 @@ all-features = true # This section is considered when running `cargo deny check advisories` [advisories] -unmaintained = "warn" +unmaintained = "deny" ignore = [ { id = "RUSTSEC-2024-0344", reason = "No upgrade available. Tracking the vulnerability: https://github.com/r0gue-io/pop-cli/issues/214" }, ] From 98824b114720b2845e04acac70eda80907e57a66 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Mon, 3 Mar 2025 16:50:24 +0100 Subject: [PATCH 117/117] fix: remove unmantained --- deny.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/deny.toml b/deny.toml index 1b156848e..a16cd8dc3 100644 --- a/deny.toml +++ b/deny.toml @@ -3,7 +3,6 @@ all-features = true # This section is considered when running `cargo deny check advisories` [advisories] -unmaintained = "deny" ignore = [ { id = "RUSTSEC-2024-0344", reason = "No upgrade available. Tracking the vulnerability: https://github.com/r0gue-io/pop-cli/issues/214" }, ]