From edbf35cb34d2bdcb92c91a61076f39fa47d98298 Mon Sep 17 00:00:00 2001 From: Pierre Dommerc Date: Fri, 12 May 2023 10:37:08 +0200 Subject: [PATCH] feat(wallet): select validator (#3375) --- .../validator-client/src/connection_tester.rs | 27 ++- nym-wallet/src-tauri/src/config/mod.rs | 61 +++++- nym-wallet/src-tauri/src/error.rs | 4 + nym-wallet/src-tauri/src/main.rs | 3 + nym-wallet/src-tauri/src/network_config.rs | 36 +++- .../src/operations/mixnet/account.rs | 94 ++++++--- nym-wallet/src-tauri/src/state.rs | 38 +++- .../components/Settings/SelectValidator.tsx | 185 ++++++++++++++++++ nym-wallet/src/context/bonding.tsx | 21 +- .../src/pages/settings/AdvancedSettings.tsx | 3 + nym-wallet/src/requests/network.ts | 11 ++ nym-wallet/src/utils/fireRequests.ts | 22 +++ nym-wallet/src/utils/index.ts | 3 + 13 files changed, 452 insertions(+), 56 deletions(-) create mode 100644 nym-wallet/src/components/Settings/SelectValidator.tsx create mode 100644 nym-wallet/src/utils/fireRequests.ts diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index 570d94f986..5d89e86d05 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,6 +1,6 @@ use crate::nyxd::error::NyxdError; use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient}; -use crate::NymApiClient; +use crate::{NymApiClient, ValidatorClientError}; use crate::nyxd::traits::MixnetQueryClient; use colored::Colorize; @@ -45,6 +45,23 @@ pub async fn run_validator_connection_test( ) } +pub async fn test_nyxd_url_connection( + network: NymNetworkDetails, + nyxd_url: Url, + address: cosmrs::AccountId, +) -> Result { + let config = ClientConfig::try_from_nym_network_details(&network) + .expect("failed to create valid nyxd client config"); + + let mut nyxd_client = NyxdClient::::connect(config, nyxd_url.as_str())?; + // possibly redundant, but lets just leave it here + nyxd_client.set_mixnet_contract_address(address); + match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await { + ConnectionResult::Nyxd(_, _, res) => Ok(res), + _ => Ok(false), // ✶ not possible to happens + } +} + fn setup_connection_tests( nyxd_urls: impl Iterator, api_urls: impl Iterator, @@ -105,7 +122,7 @@ async fn test_nyxd_connection( { Ok(Err(NyxdError::TendermintError(e))) => { // If we get a tendermint-rpc error, we classify the node as not contactable - log::debug!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); + log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); false } Ok(Err(NyxdError::AbciError { code, log, .. })) => { @@ -118,12 +135,12 @@ async fn test_nyxd_connection( code == 18 } Ok(Err(error @ NyxdError::NoContractAddressAvailable(_))) => { - log::debug!("Checking: nyxd url: {url}: {}: {error}", "failed".red()); + log::warn!("Checking: nyxd url: {url}: {}: {error}", "failed".red()); false } Ok(Err(e)) => { // For any other error, we're optimistic and just try anyway. - log::debug!( + log::warn!( "Checking: nyxd_url: {url}: {}, but with error: {e}", "success".green() ); @@ -134,7 +151,7 @@ async fn test_nyxd_connection( true } Err(e) => { - log::debug!("Checking: nyxd_url: {url}: {}: {e}", "failed".red()); + log::warn!("Checking: nyxd_url: {url}: {}: {e}", "failed".red()); false } }; diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index f560b89c36..24e8a7d03a 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -57,6 +57,10 @@ pub struct NetworkConfig { // User selected urls selected_nyxd_url: Option, + // Default nyxd URL assigned during login, can be used when the user wants + // to revert back its selected validator URL + default_nyxd_url: Option, + selected_api_url: Option, // Additional user provided validators. @@ -85,6 +89,7 @@ impl Default for NetworkConfig { fn default() -> Self { Self { version: Some(CURRENT_NETWORK_CONFIG_VERSION), + default_nyxd_url: None, selected_nyxd_url: None, selected_api_url: None, nyxd_urls: None, @@ -241,6 +246,30 @@ impl Config { .expect("Wrong format for bandwidth claim contract address") } + pub fn set_default_nyxd_urls(&mut self, urls: &HashMap) { + for (network, url) in urls { + self.set_default_nyxd_url(url.to_owned(), network); + } + } + + pub fn set_default_nyxd_url(&mut self, nyxd_url: Url, network: &WalletNetwork) { + log::debug!( + "set default nyxd URL for {network} {}", + nyxd_url.to_string() + ); + if let Some(net) = self.networks.get_mut(&network.as_key()) { + net.default_nyxd_url = Some(nyxd_url); + } else { + self.networks.insert( + network.as_key(), + NetworkConfig { + default_nyxd_url: Some(nyxd_url), + ..NetworkConfig::default() + }, + ); + } + } + pub fn select_nyxd_url(&mut self, nyxd_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_nyxd_url = Some(nyxd_url); @@ -255,6 +284,13 @@ impl Config { } } + pub fn reset_nyxd_url(&mut self, network: WalletNetwork) { + match self.networks.get_mut(&network.as_key()) { + Some(net) => net.selected_nyxd_url = None, + None => log::warn!("reset_nyxd_url: {network} network not found, ignoring"), + } + } + pub fn select_nym_api_url(&mut self, api_url: Url, network: WalletNetwork) { if let Some(net) = self.networks.get_mut(&network.as_key()) { net.selected_api_url = Some(api_url); @@ -262,7 +298,7 @@ impl Config { self.networks.insert( network.as_key(), NetworkConfig { - selected_nyxd_url: Some(api_url), + selected_api_url: Some(api_url), ..NetworkConfig::default() }, ); @@ -270,9 +306,25 @@ impl Config { } pub fn get_selected_validator_nyxd_url(&self, network: WalletNetwork) -> Option { - self.networks - .get(&network.as_key()) - .and_then(|config| config.selected_nyxd_url.clone()) + self.networks.get(&network.as_key()).and_then(|config| { + log::debug!( + "get selected nyxd url for {} {:?}", + network.to_string(), + config.selected_nyxd_url, + ); + config.selected_nyxd_url.clone() + }) + } + + pub fn get_default_nyxd_url(&self, network: WalletNetwork) -> Option { + self.networks.get(&network.as_key()).and_then(|config| { + log::debug!( + "get default nyxd url for {} {:?}", + network.to_string(), + config.default_nyxd_url, + ); + config.default_nyxd_url.clone() + }) } pub fn get_selected_nym_api_url(&self, network: &WalletNetwork) -> Option { @@ -573,6 +625,7 @@ api_url = 'https://baz/api' r#"{ "version": 1, "selected_nyxd_url": null, + "default_nyxd_url": null, "selected_api_url": "https://my_api_url.com/", "nyxd_urls": [ { diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index d7c552bf15..8cee3018d5 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -130,6 +130,10 @@ pub enum BackendError { NewWindowError, #[error("Failed to check for application update")] CheckAppVersionError, + #[error("Failed to connect to the provided validator URL")] + WalletValidatorConnectionFailed, + #[error("No defined default validator URL")] + WalletNoDefaultValidator, #[error(transparent)] WalletError { diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index e0842fa096..87c961b38e 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -94,6 +94,9 @@ fn main() { network_config::remove_validator, network_config::select_nym_api_url, network_config::select_nyxd_url, + network_config::reset_nyxd_url, + network_config::get_default_nyxd_url, + network_config::get_selected_nyxd_url, network_config::update_nyxd_urls, state::load_config_from_files, state::save_config_to_files, diff --git a/nym-wallet/src-tauri/src/network_config.rs b/nym-wallet/src-tauri/src/network_config.rs index bcac19bb62..6f75b6e5fa 100644 --- a/nym-wallet/src-tauri/src/network_config.rs +++ b/nym-wallet/src-tauri/src/network_config.rs @@ -26,6 +26,28 @@ pub async fn get_nym_api_urls( Ok(ValidatorUrls { urls }) } +#[tauri::command] +pub async fn get_selected_nyxd_url( + network: WalletNetwork, + state: tauri::State<'_, WalletState>, +) -> Result, BackendError> { + let state = state.read().await; + let url = state.get_selected_nyxd_url(&network).map(String::from); + log::info!("Selected nyxd url for {network}: {:?}", url); + Ok(url) +} + +#[tauri::command] +pub async fn get_default_nyxd_url( + network: WalletNetwork, + state: tauri::State<'_, WalletState>, +) -> Result { + let state = state.read().await; + let url = state.get_default_nyxd_url(&network).map(String::from); + log::info!("Default nyxd url for {network}: {:?}", url); + url.ok_or_else(|| BackendError::WalletNoDefaultValidator) +} + #[tauri::command] pub async fn select_nyxd_url( url: &str, @@ -33,7 +55,19 @@ pub async fn select_nyxd_url( state: tauri::State<'_, WalletState>, ) -> Result<(), BackendError> { log::debug!("Selecting new nyxd url for {network}: {url}"); - state.write().await.select_nyxd_url(url, network)?; + state.write().await.select_nyxd_url(url, network).await?; + state.read().await.save_config_files()?; + Ok(()) +} + +#[tauri::command] +pub async fn reset_nyxd_url( + network: WalletNetwork, + state: tauri::State<'_, WalletState>, +) -> Result<(), BackendError> { + log::debug!("Resetting nyxd url for {network} to default"); + state.write().await.reset_nyxd_url(network)?; + state.read().await.save_config_files()?; Ok(()) } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 35265f54f2..a15732e5ee 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -3,8 +3,8 @@ use crate::error::BackendError; use crate::network_config; use crate::state::{WalletAccountIds, WalletState}; use crate::wallet_storage::{self, UserPassword, DEFAULT_LOGIN_ID}; -use bip39::rand::{self, seq::SliceRandom}; -use bip39::{Language, Mnemonic}; +use bip39::rand::seq::SliceRandom; +use bip39::{rand, Language, Mnemonic}; use cosmrs::bip32::DerivationPath; use itertools::Itertools; use nym_config::defaults::{NymNetworkDetails, COSMOS_DERIVATION_PATH}; @@ -118,6 +118,14 @@ async fn _connect_with_mnemonic( let state = state.read().await; (state.get_all_nyxd_urls(), state.get_all_api_urls()) }; + + let (nyxd_urls, api_urls) = run_connection_test( + untested_nyxd_urls.clone(), + untested_api_urls.clone(), + &config, + ) + .await; + let default_nyxd_urls: HashMap = untested_nyxd_urls .iter() .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) @@ -127,19 +135,17 @@ async fn _connect_with_mnemonic( .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) .collect(); - // Run connection tests on all nyxd and nym-api endpoints - let (nyxd_urls, api_urls) = - run_connection_test(untested_nyxd_urls, untested_api_urls, &config).await; + let nyxd_urls = pick_good_nyxd_urls(&default_nyxd_urls, &nyxd_urls).await?; + let api_urls = pick_good_api_urls(&default_api_urls, &api_urls).await?; + + { + let mut w_state = state.write().await; + // Save the checked nyxd URLs + w_state.set_default_nyxd_urls(&nyxd_urls); + } // Create clients for all networks - let clients = create_clients( - &nyxd_urls, - &api_urls, - &default_nyxd_urls, - &default_api_urls, - &config, - &mnemonic, - )?; + let clients = create_clients(&nyxd_urls, &api_urls, &config, &mnemonic)?; // Set the default account let default_network = WalletNetwork::MAINNET; @@ -196,9 +202,51 @@ async fn run_connection_test( .await } -fn create_clients( +async fn pick_good_nyxd_urls( + default_nyxd_urls: &HashMap, nyxd_urls: &HashMap>, +) -> Result, BackendError> { + let nyxd_urls: HashMap = WalletNetwork::iter() + .map(|network| { + let default_nyxd_url = default_nyxd_urls + .get(&network) + .expect("Expected at least one nyxd_url"); + let url = select_random_responding_url(nyxd_urls, network).unwrap_or_else(|| { + log::warn!( + "No successful nyxd_urls for {network}: using default: {default_nyxd_url}" + ); + default_nyxd_url.clone() + }); + log::info!("Set default nyxd_url for {network}: {url}"); + (network, url) + }) + .collect(); + + Ok(nyxd_urls) +} + +async fn pick_good_api_urls( + default_api_urls: &HashMap, api_urls: &HashMap>, +) -> Result, BackendError> { + let api_urls: HashMap = WalletNetwork::iter() + .map(|network| { + let default_api_url = default_api_urls + .get(&network) + .expect("Expected at least one api_url"); + let url = select_first_responding_url(api_urls, network).unwrap_or_else(|| { + log::warn!("No passing api_urls for {network}: using default: {default_api_url}"); + default_api_url.clone() + }); + log::info!("Set default api_url for {network}: {url}"); + (network, url) + }) + .collect(); + + Ok(api_urls) +} + +fn create_clients( default_nyxd_urls: &HashMap, default_api_urls: &HashMap, config: &Config, @@ -210,28 +258,22 @@ fn create_clients( log::debug!("Using selected nyxd_url for {network}: {url}"); url.clone() } else { - let default_nyxd_url = default_nyxd_urls + let url = default_nyxd_urls .get(&network) .expect("Expected at least one nyxd_url"); - select_random_responding_url(nyxd_urls, network).unwrap_or_else(|| { - log::debug!( - "No successful nyxd_urls for {network}: using default: {default_nyxd_url}" - ); - default_nyxd_url.clone() - }) + log::debug!("Using default nyxd_url for {network}: {url}"); + url.to_owned() }; let api_url = if let Some(url) = config.get_selected_nym_api_url(&network) { log::debug!("Using selected api_url for {network}: {url}"); url.clone() } else { - let default_api_url = default_api_urls + let url = default_api_urls .get(&network) .expect("Expected at least one api url"); - select_first_responding_url(api_urls, network).unwrap_or_else(|| { - log::debug!("No passing api_urls for {network}: using default: {default_api_url}"); - default_api_url.clone() - }) + log::debug!("Using default api_url for {network}: {url}"); + url.to_owned() }; log::info!("Connecting to: nyxd_url: {nyxd_url} for {network}"); diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index c10bbf486f..bfc2d85cb6 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -437,7 +437,20 @@ impl WalletStateInner { } } - pub fn select_nyxd_url(&mut self, url: &str, network: Network) -> Result<(), BackendError> { + pub async fn select_nyxd_url( + &mut self, + url: &str, + network: Network, + ) -> Result<(), BackendError> { + if !nym_validator_client::connection_tester::test_nyxd_url_connection( + network.into(), + url.parse()?, + self.config.get_mixnet_contract_address(network), + ) + .await? + { + return Err(BackendError::WalletValidatorConnectionFailed); + } self.config.select_nyxd_url(url.parse()?, network); if let Ok(client) = self.client_mut(network) { client.change_nyxd(url.parse()?)?; @@ -445,6 +458,29 @@ impl WalletStateInner { Ok(()) } + pub fn reset_nyxd_url(&mut self, network: Network) -> Result<(), BackendError> { + self.config.reset_nyxd_url(network); + let default_nyxd = self.config.get_default_nyxd_url(network); + if let Ok(client) = self.client_mut(network) { + if let Some(url) = default_nyxd { + client.change_nyxd(url)?; + } + } + Ok(()) + } + + pub fn set_default_nyxd_urls(&mut self, urls: &HashMap) { + self.config.set_default_nyxd_urls(urls); + } + + pub fn get_selected_nyxd_url(&self, network: &Network) -> Option { + self.config.get_selected_validator_nyxd_url(*network) + } + + pub fn get_default_nyxd_url(&self, network: &Network) -> Option { + self.config.get_default_nyxd_url(*network) + } + pub fn select_nym_api_url(&mut self, url: &str, network: Network) -> Result<(), BackendError> { self.config.select_nym_api_url(url.parse()?, network); if let Ok(client) = self.client_mut(network) { diff --git a/nym-wallet/src/components/Settings/SelectValidator.tsx b/nym-wallet/src/components/Settings/SelectValidator.tsx new file mode 100644 index 0000000000..c52cc2db3e --- /dev/null +++ b/nym-wallet/src/components/Settings/SelectValidator.tsx @@ -0,0 +1,185 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { Button, FormControl, Grid, Stack, Switch, TextField, Typography } from '@mui/material'; +import { useSnackbar } from 'notistack'; +import { + checkMixnodeOwnership, + getDefaultValidatorUrl, + getSelectedValidatorUrl, + resetValidatorUrl, + setSelectedValidatorUrl as setSelectedValidatorUrlReq, +} from '../../requests'; +import { AppContext } from '../../context'; +import { Console } from '../../utils/console'; +import { Network } from '../../types'; + +const SelectValidator = () => { + const [customValidatorEnabled, setCustomValidatorEnabled] = useState(false); + const [selectedValidatorUrl, setSelectedValidatorUrl] = useState(); + const [defaultValidatorUrl, setDefaultValidatorUrl] = useState(); + const [validatorUrlInput, setValidatorUrlInput] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const { network } = useContext(AppContext); + + const { enqueueSnackbar } = useSnackbar(); + + const getDefaultValidator = async (net: Network) => { + if (!network) { + return; + } + try { + const defaultValidator = await getDefaultValidatorUrl(net); + setDefaultValidatorUrl(defaultValidator); + } catch (e) { + Console.error(`an error occurred while requesting the default validator URL: ${e}`); + } + }; + + const getSelectedValidator = async (net: Network) => { + if (!network) { + return null; + } + try { + const selectedValidator = await getSelectedValidatorUrl(net); + setSelectedValidatorUrl(selectedValidator); + } catch (e) { + Console.error(`an error occurred while requesting the selected validator URL: ${e}`); + } + return null; + }; + + useEffect(() => { + if (network) { + getDefaultValidator(network); + getSelectedValidator(network); + } + }, [network, customValidatorEnabled]); + + useEffect(() => { + // on network change, turn off the custom val switch if there is no selected val + // for this network + if (!selectedValidatorUrl) { + setCustomValidatorEnabled(false); + setValidatorUrlInput(''); + } + }, [network, selectedValidatorUrl]); + + useEffect(() => { + if (selectedValidatorUrl && selectedValidatorUrl !== defaultValidatorUrl) { + setCustomValidatorEnabled(true); + } + + if (selectedValidatorUrl) { + setValidatorUrlInput(selectedValidatorUrl); + } + }, [selectedValidatorUrl, defaultValidatorUrl, network]); + + const onToggle = async () => { + if (!customValidatorEnabled) { + setCustomValidatorEnabled(true); + return; + } + setIsLoading(true); + try { + await resetValidatorUrl(network as Network); + setValidatorUrlInput(''); + setSelectedValidatorUrl(null); + setCustomValidatorEnabled(false); + } catch (e) { + Console.error(e); + } finally { + setIsLoading(false); + } + }; + + const saveValidator = async () => { + if (!network || !validatorUrlInput || validatorUrlInput === defaultValidatorUrl) { + return; + } + setIsLoading(true); + try { + // this tauri request also does a basic connection check + await setSelectedValidatorUrlReq({ network, url: validatorUrlInput }); + } catch (e) { + Console.error(e); + enqueueSnackbar(`Invalid validator URL: ${e}`, { variant: 'error' }); + setIsLoading(false); + return; + } + + // to enforce the validator URL is valid, try to query the node ownership + // if it fails, that means the endpoint is wrong + // TODO this check logic should be handled directly in the rust side, `select_nyxd_url` command + try { + await checkMixnodeOwnership(); + enqueueSnackbar('Validator URL saved', { variant: 'success' }); + } catch (e) { + Console.error(e); + enqueueSnackbar('The given validator URL is not valid for the currently selected network', { variant: 'error' }); + await resetValidatorUrl(network as Network); + setSelectedValidatorUrl(null); + } finally { + setIsLoading(false); + } + }; + + return ( + + + + Change validator + + You can use the validator of your choice by providing its RPC URL address + + + Turn Off + + Turn On + + + + + + {customValidatorEnabled ? ( + + + setValidatorUrlInput(e.target.value)} + error={false} + InputLabelProps={{ shrink: true }} + fullWidth + disabled={!customValidatorEnabled} + autoFocus + /> + + + + ) : ( + + Default validator address + {defaultValidatorUrl} + + )} + + + + ); +}; + +export default SelectValidator; diff --git a/nym-wallet/src/context/bonding.tsx b/nym-wallet/src/context/bonding.tsx index 4e17da9066..181f531a1e 100644 --- a/nym-wallet/src/context/bonding.tsx +++ b/nym-wallet/src/context/bonding.tsx @@ -57,6 +57,8 @@ import { import { useCheckOwnership } from '../hooks/useCheckOwnership'; import { AppContext } from './main'; import { + fireRequests, + TauriReq, attachDefaultOperatingCost, decCoinToDisplay, toPercentFloatString, @@ -162,12 +164,6 @@ export const BondingContext = createContext({ isVestingAccount: false, }); -type TauriReq Promise)> = { - name: Req['name']; - request: () => ReturnType; - onFulfilled: (value: Awaited>) => void; -}; - export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Element => { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); @@ -192,19 +188,6 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen setBondedNode(undefined); }; - const fireRequests = async (reqs: TauriReq[]) => { - const promises = await Promise.allSettled(reqs.map((r) => r.request())); - - promises.forEach((res, index) => { - if (res.status === 'rejected') { - Console.warn(`${reqs[index].name} request fails`, res.reason); - } - if (res.status === 'fulfilled') { - reqs[index].onFulfilled(res.value as any); - } - }); - }; - /** * Fetch mixnode **optional** data. * ⚠ The underlying queries are allowed to fail. diff --git a/nym-wallet/src/pages/settings/AdvancedSettings.tsx b/nym-wallet/src/pages/settings/AdvancedSettings.tsx index fd403a6166..76ec15c967 100644 --- a/nym-wallet/src/pages/settings/AdvancedSettings.tsx +++ b/nym-wallet/src/pages/settings/AdvancedSettings.tsx @@ -3,6 +3,7 @@ import { Box, Button, Divider, Stack, Typography } from '@mui/material'; import { helpLogToggleWindow } from '../../requests'; import { AppContext } from '../../context'; import { config } from '../../config'; +import SelectValidator from '../../components/Settings/SelectValidator'; const AdvancedSettings = () => { const { handleShowTerminal, appEnv } = useContext(AppContext); @@ -40,6 +41,8 @@ const AdvancedSettings = () => { + + ); }; diff --git a/nym-wallet/src/requests/network.ts b/nym-wallet/src/requests/network.ts index 931aa6a9e9..32380e924f 100644 --- a/nym-wallet/src/requests/network.ts +++ b/nym-wallet/src/requests/network.ts @@ -3,3 +3,14 @@ import { Network } from 'src/types'; import { invokeWrapper } from './wrapper'; export const selectNetwork = async (network: Network) => invokeWrapper('switch_network', { network }); + +export const getSelectedValidatorUrl = async (network: Network) => + invokeWrapper('get_selected_nyxd_url', { network }); + +export const getDefaultValidatorUrl = async (network: Network) => + invokeWrapper('get_default_nyxd_url', { network }); + +export const setSelectedValidatorUrl = async (args: { network: Network; url: string }) => + invokeWrapper('select_nyxd_url', args); + +export const resetValidatorUrl = async (network: Network) => invokeWrapper('reset_nyxd_url', { network }); diff --git a/nym-wallet/src/utils/fireRequests.ts b/nym-wallet/src/utils/fireRequests.ts new file mode 100644 index 0000000000..23117e13eb --- /dev/null +++ b/nym-wallet/src/utils/fireRequests.ts @@ -0,0 +1,22 @@ +import { Console } from 'src/utils/console'; + +export type TauriReq Promise)> = { + name: Req['name']; + request: () => ReturnType; + onFulfilled: (value: Awaited>) => void; +}; + +async function fireRequests(requests: TauriReq[]) { + const promises = await Promise.allSettled(requests.map((r) => r.request())); + + promises.forEach((res, index) => { + if (res.status === 'rejected') { + Console.warn(`${requests[index].name} request fails`, res.reason); + } + if (res.status === 'fulfilled') { + requests[index].onFulfilled(res.value as any); + } + }); +} + +export default fireRequests; diff --git a/nym-wallet/src/utils/index.ts b/nym-wallet/src/utils/index.ts index 28192993dc..1f713a7b1d 100644 --- a/nym-wallet/src/utils/index.ts +++ b/nym-wallet/src/utils/index.ts @@ -228,3 +228,6 @@ export const getIntervalAsDate = async () => { return { nextEpoch, nextInterval }; }; + +export * from './fireRequests'; +export { default as fireRequests } from './fireRequests';