diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index dc1acb8cd9..30e57ff95e 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -108,7 +108,7 @@ async fn test_nymd_connection( log::debug!("Checking: nymd_url: {url}: {}: {}", "failed".red(), e); false } - Ok(Err(NymdError::AbciError(code, log))) => { + Ok(Err(NymdError::AbciError { code, log, .. })) => { // We accept the mixnet contract not found as ok from a connection standpoint. This happens // for example on a pre-launch network. log::debug!( diff --git a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs index 8da9d3192c..580a8c7c05 100644 --- a/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nymd/cosmwasm_client/client.rs @@ -1,6 +1,7 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::nymd; use crate::nymd::coin::Coin; use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key}; use crate::nymd::cosmwasm_client::types::{ @@ -23,7 +24,6 @@ use cosmrs::rpc::endpoint::broadcast; use cosmrs::rpc::endpoint::tx::Response as TxResponse; use cosmrs::rpc::query::Query; use cosmrs::rpc::{self, HttpClient, Order}; -use cosmrs::tendermint::abci::Code as AbciCode; use cosmrs::tendermint::abci::Transaction; use cosmrs::tendermint::{abci, block, chain}; use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx}; @@ -66,13 +66,9 @@ pub trait CosmWasmClient: rpc::Client { req.encode(&mut buf)?; let res = self.abci_query(path, buf, None, false).await?; + let res_success = nymd::error::parse_abci_query_result(res)?; - match res.code { - AbciCode::Err(code) => return Err(NymdError::AbciError(code, res.log)), - AbciCode::Ok => (), - } - - Ok(Res::decode(res.value.as_ref())?) + Ok(Res::decode(res_success.value.as_ref())?) } async fn get_chain_id(&self) -> Result { diff --git a/common/client-libs/validator-client/src/nymd/error.rs b/common/client-libs/validator-client/src/nymd/error.rs index 0b6dcfe22b..e530f6c54d 100644 --- a/common/client-libs/validator-client/src/nymd/error.rs +++ b/common/client-libs/validator-client/src/nymd/error.rs @@ -2,16 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nymd::cosmwasm_client::types::ContractCodeId; -use cosmrs::tendermint::{abci, block}; -use cosmrs::{bip32, tx, AccountId}; -use std::io; -use std::time::Duration; +use cosmrs::{ + bip32, + rpc::endpoint::abci_query::AbciQuery, + tendermint::{ + abci::{self, Code as AbciCode}, + block, + }, + tx, AccountId, +}; use thiserror::Error; -pub use cosmrs::rpc::error::{ - Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail, +use std::{io, time::Duration}; + +pub use cosmrs::rpc::{ + error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail}, + response_error::{Code, ResponseError}, }; -pub use cosmrs::rpc::response_error::{Code, ResponseError}; #[derive(Debug, Error)] pub enum NymdError { @@ -110,8 +117,12 @@ pub enum NymdError { #[error("Failed to estimate gas price for the transaction")] GasEstimationFailure, - #[error("Abci query failed with code {0} - {1}")] - AbciError(u32, abci::Log), + #[error("Abci query failed with code {code} - {log}")] + AbciError { + code: u32, + log: abci::Log, + pretty_log: Option, + }, #[error("Unsupported account type: {type_url}")] UnsupportedAccountType { type_url: String }, @@ -135,6 +146,32 @@ pub enum NymdError { UnexpectedBech32Prefix { got: String, expected: String }, } +// The purpose of parsing the abci query result is that we want to generate the `pretty_log` if +// possible. +pub fn parse_abci_query_result(query_result: AbciQuery) -> Result { + match query_result.code { + AbciCode::Ok => Ok(query_result), + AbciCode::Err(code) => Err(NymdError::AbciError { + code, + log: query_result.log.clone(), + pretty_log: try_parse_abci_log(&query_result.log), + }), + } +} + +// Some of the error strings returned by the query are a bit too technical to present to the +// enduser. So we special case some commonly encountered errors. +fn try_parse_abci_log(log: &abci::Log) -> Option { + if log + .value() + .contains("Maximum amount of locked coins has already been pledged") + { + Some("Maximum amount of locked tokens has alredy been used. You can only use up tp 10% of your locked tokens for bonding and delegating.".to_string()) + } else { + None + } +} + impl NymdError { pub fn is_tendermint_response_timeout(&self) -> bool { match &self { diff --git a/nym-wallet/CHANGELOG.md b/nym-wallet/CHANGELOG.md index d6ca3521d9..ef1fb52d15 100644 --- a/nym-wallet/CHANGELOG.md +++ b/nym-wallet/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## UNRELEASED + +- wallet: present some cases of the more technical errors (abci, ..) in a more human readable form. + ## [nym-wallet-v1.1.3](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.1.3) (2022-12-13) - wallet: improved unbond screen. diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index e5a8feeef5..7f5dfe0812 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -24,9 +24,10 @@ pub enum BackendError { #[from] source: tendermint_rpc::Error, }, - #[error("{source}")] + #[error("{pretty_error}")] NymdError { - #[from] + pretty_error: String, + #[source] source: NymdError, }, #[error("{source}")] @@ -131,6 +132,34 @@ impl Serialize for BackendError { } } +impl From for BackendError { + fn from(source: NymdError) -> Self { + match source { + NymdError::AbciError { + code: _, + log: _, + ref pretty_log, + } => { + if let Some(pretty_log) = pretty_log { + Self::NymdError { + pretty_error: pretty_log.to_string(), + source, + } + } else { + Self::NymdError { + pretty_error: source.to_string(), + source, + } + } + } + nymd_error => Self::NymdError { + pretty_error: nymd_error.to_string(), + source: nymd_error, + }, + } + } +} + impl From for BackendError { fn from(e: ValidatorClientError) -> Self { match e {