contract_extrinsics/
error.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17use sp_runtime::DispatchError;
18use std::fmt::{
19    self,
20    Debug,
21    Display,
22};
23
24#[derive(serde::Serialize)]
25pub enum ErrorVariant {
26    #[serde(rename = "module_error")]
27    Module(ModuleError),
28    #[serde(rename = "generic_error")]
29    Generic(GenericError),
30}
31
32impl From<subxt::Error> for ErrorVariant {
33    fn from(error: subxt::Error) -> Self {
34        match error {
35            subxt::Error::Runtime(subxt::error::DispatchError::Module(module_err)) => {
36                module_err
37                    .details()
38                    .map(|details| {
39                        ErrorVariant::Module(ModuleError {
40                            pallet: details.pallet.name().to_string(),
41                            error: details.variant.name.to_string(),
42                            docs: details.variant.docs.clone(),
43                        })
44                    })
45                    .unwrap_or_else(|err| {
46                        ErrorVariant::Generic(GenericError::from_message(format!(
47                            "Error extracting subxt error details: {}",
48                            err
49                        )))
50                    })
51            }
52            err => ErrorVariant::Generic(GenericError::from_message(err.to_string())),
53        }
54    }
55}
56
57impl From<anyhow::Error> for ErrorVariant {
58    fn from(error: anyhow::Error) -> Self {
59        Self::Generic(GenericError::from_message(format!("{error:?}")))
60    }
61}
62
63impl From<&str> for ErrorVariant {
64    fn from(err: &str) -> Self {
65        Self::Generic(GenericError::from_message(err.to_owned()))
66    }
67}
68
69impl From<std::io::Error> for ErrorVariant {
70    fn from(value: std::io::Error) -> Self {
71        Self::Generic(GenericError::from_message(value.to_string()))
72    }
73}
74
75impl From<serde_json::Error> for ErrorVariant {
76    fn from(error: serde_json::Error) -> Self {
77        Self::Generic(GenericError::from_message(format!("{error:?}")))
78    }
79}
80
81#[derive(serde::Serialize)]
82pub struct ModuleError {
83    pub pallet: String,
84    pub error: String,
85    pub docs: Vec<String>,
86}
87
88#[derive(serde::Serialize)]
89pub struct GenericError {
90    error: String,
91}
92
93impl GenericError {
94    pub fn from_message(error: String) -> Self {
95        GenericError { error }
96    }
97}
98
99impl ErrorVariant {
100    pub fn from_dispatch_error(
101        error: &DispatchError,
102        metadata: &subxt::Metadata,
103    ) -> anyhow::Result<ErrorVariant> {
104        match error {
105            DispatchError::Module(err) => {
106                let pallet = metadata.pallet_by_index_err(err.index)?;
107                let variant =
108                    pallet.error_variant_by_index(err.error[0]).ok_or_else(|| {
109                        anyhow::anyhow!("Error variant {} not found", err.error[0])
110                    })?;
111                Ok(ErrorVariant::Module(ModuleError {
112                    pallet: pallet.name().to_string(),
113                    error: variant.name.to_owned(),
114                    docs: variant.docs.to_owned(),
115                }))
116            }
117            err => {
118                Ok(ErrorVariant::Generic(GenericError::from_message(format!(
119                    "DispatchError: {err:?}"
120                ))))
121            }
122        }
123    }
124}
125
126impl Debug for ErrorVariant {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        <Self as Display>::fmt(self, f)
129    }
130}
131
132impl Display for ErrorVariant {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self {
135            ErrorVariant::Module(err) => {
136                f.write_fmt(format_args!(
137                    "ModuleError: {}::{}: {:?}",
138                    err.pallet, err.error, err.docs
139                ))
140            }
141            ErrorVariant::Generic(err) => write!(f, "{}", err.error),
142        }
143    }
144}