1use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct QoreTxError {
11 pub code: u32,
13 pub codespace: String,
16 pub reason: String,
19 pub raw_log: String,
21 pub tx_hash: String,
23}
24
25impl fmt::Display for QoreTxError {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 let cs = if self.codespace.is_empty() {
28 "sdk"
29 } else {
30 &self.codespace
31 };
32 write!(f, "tx failed: code {} ({}/{})", self.code, cs, self.reason)?;
33 if !self.tx_hash.is_empty() {
34 write!(f, " tx {}", self.tx_hash)?;
35 }
36 if !self.raw_log.is_empty() {
37 write!(f, ": {}", self.raw_log)?;
38 }
39 Ok(())
40 }
41}
42
43impl std::error::Error for QoreTxError {}
44
45pub fn decode_tx_error(code: u32, codespace: &str, raw_log: &str) -> Option<QoreTxError> {
52 if code == 0 {
53 return None;
54 }
55 Some(QoreTxError {
56 code,
57 codespace: codespace.to_string(),
58 reason: reason_for(code, codespace).to_string(),
59 raw_log: raw_log.to_string(),
60 tx_hash: String::new(),
61 })
62}
63
64fn sdk_reason(code: u32) -> Option<&'static str> {
66 Some(match code {
67 1 => "internal error",
68 2 => "tx parse error",
69 3 => "invalid sequence",
70 4 => "unauthorized",
71 5 => "insufficient funds",
72 6 => "unknown request",
73 7 => "invalid address",
74 8 => "invalid pubkey",
75 9 => "unknown address",
76 10 => "invalid coins",
77 11 => "out of gas",
78 12 => "memo too large",
79 13 => "insufficient fee",
80 14 => "maximum number of signatures exceeded",
81 15 => "no signatures supplied",
82 16 => "failed to marshal JSON bytes",
83 17 => "failed to unmarshal JSON bytes",
84 18 => "invalid request",
85 19 => "tx already in mempool",
86 20 => "mempool is full",
87 21 => "tx too large",
88 22 => "key not found",
89 23 => "invalid account password",
90 24 => "invalid signature",
91 25 => "no concrete type registered",
92 26 => "unpacking protobuf message failed",
93 27 => "invalid gas adjustment",
94 28 => "invalid height",
95 29 => "invalid version",
96 30 => "invalid chain id",
97 31 => "invalid type",
98 32 => "tx timeout height",
99 33 => "unknown extension options",
100 35 => "invalid gas limit",
101 _ => return None,
102 })
103}
104
105fn module_reason(codespace: &str, code: u32) -> Option<&'static str> {
108 let reason = match (codespace, code) {
109 ("bank", 2) => "no inputs to send transaction",
110 ("bank", 3) => "no outputs to send transaction",
111 ("bank", 4) => "sum inputs != sum outputs",
112 ("bank", 5) => "send transactions are disabled",
113 ("staking", 2) => "validator does not exist",
114 ("staking", 3) => "validator already exist for this operator address",
115 ("staking", 13) => "too many shares to undelegate",
116 ("staking", 15) => "insufficient delegation shares",
117 ("distribution", 2) => "no delegation distribution info",
118 ("distribution", 3) => "no validator distribution info",
119 ("distribution", 6) => "set withdraw address disabled",
120 ("gov", 2) => "unknown proposal",
121 ("gov", 3) => "inactive proposal",
122 ("gov", 4) => "already active proposal",
123 ("gov", 5) => "invalid proposal content",
124 ("authz", 2) => "authorization not found",
125 ("authz", 3) => "invalid expiration time",
126 ("feegrant", 2) => "fee limit exceeded",
127 ("feegrant", 3) => "fee allowance already exists",
128 ("feegrant", 4) => "fee allowance expired",
129 _ => return None,
130 };
131 Some(reason)
132}
133
134const QORE_CODESPACES: &[&str] = &[
138 "pqc",
139 "amm",
140 "bridge",
141 "rdk",
142 "multilayer",
143 "svm",
144 "lightnode",
145 "license",
146 "abstractaccount",
147 "crossvm",
148 "rlconsensus",
149];
150
151const KNOWN_MODULE_CODESPACES: &[&str] = &[
152 "bank",
153 "staking",
154 "distribution",
155 "gov",
156 "authz",
157 "feegrant",
158];
159
160fn reason_for(code: u32, codespace: &str) -> String {
161 if codespace.is_empty() || codespace == "sdk" {
162 return sdk_reason(code).unwrap_or("unknown error").to_string();
163 }
164 if let Some(reason) = module_reason(codespace, code) {
165 return reason.to_string();
166 }
167 if KNOWN_MODULE_CODESPACES.contains(&codespace) || QORE_CODESPACES.contains(&codespace) {
168 return format!("unknown {codespace} error");
169 }
170 format!("unknown {codespace} error")
171}