sui_transaction_builder/
error.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use sui_sdk_types::Address;
5
6/// Errors that can occur when building or resolving a transaction.
7#[derive(thiserror::Error, Debug, Clone)]
8#[non_exhaustive]
9pub enum Error {
10    #[error("Conversion error due to input issue: {0}")]
11    Input(String),
12    #[error("Gas object should be an immutable or owned object")]
13    WrongGasObject,
14    #[error("Missing object id")]
15    MissingObjectId,
16    #[error("Missing version for object {0}")]
17    MissingVersion(Address),
18    #[error("Missing digest for object {0}")]
19    MissingDigest(Address),
20    #[error("Missing sender")]
21    MissingSender,
22    #[error("Missing gas objects")]
23    MissingGasObjects,
24    #[error("Missing gas budget")]
25    MissingGasBudget,
26    #[error("Missing gas price")]
27    MissingGasPrice,
28    #[error("Missing object kind for object {0}")]
29    MissingObjectKind(Address),
30    #[error("Unknown shared object mutability for object {0}")]
31    SharedObjectMutability(Address),
32    #[error("{0}")]
33    #[cfg(feature = "intents")]
34    #[cfg_attr(doc_cfg, doc(cfg(feature = "intents")))]
35    SimulationFailure(Box<SimulationFailure>),
36}
37
38/// Rich error information from a failed transaction simulation.
39#[derive(Debug, Clone)]
40#[cfg(feature = "intents")]
41#[cfg_attr(doc_cfg, doc(cfg(feature = "intents")))]
42pub struct SimulationFailure {
43    /// The execution error returned by the simulate response.
44    error: sui_rpc::proto::sui::rpc::v2::ExecutionError,
45}
46
47#[cfg(feature = "intents")]
48impl SimulationFailure {
49    pub(crate) fn new(error: sui_rpc::proto::sui::rpc::v2::ExecutionError) -> Self {
50        Self { error }
51    }
52
53    /// Returns the underlying execution error.
54    pub fn execution_error(&self) -> &sui_rpc::proto::sui::rpc::v2::ExecutionError {
55        &self.error
56    }
57
58    /// Returns the human-readable error description, if available.
59    pub fn description(&self) -> Option<&str> {
60        self.error.description_opt()
61    }
62
63    /// Returns the command index that failed, if available.
64    pub fn command(&self) -> Option<u64> {
65        self.error.command_opt()
66    }
67
68    /// Returns the error kind, if available.
69    pub fn kind(
70        &self,
71    ) -> Option<sui_rpc::proto::sui::rpc::v2::execution_error::ExecutionErrorKind> {
72        self.error.kind.and_then(|k| {
73            sui_rpc::proto::sui::rpc::v2::execution_error::ExecutionErrorKind::try_from(k).ok()
74        })
75    }
76
77    /// Returns the Move abort details, if this was a `MoveAbort` error.
78    pub fn move_abort(&self) -> Option<&sui_rpc::proto::sui::rpc::v2::MoveAbort> {
79        self.error.abort_opt()
80    }
81
82    /// Returns the clever error details, if available.
83    ///
84    /// Clever errors provide structured information about Move abort
85    /// codes, including the error constant name, type, and value.
86    pub fn clever_error(&self) -> Option<&sui_rpc::proto::sui::rpc::v2::CleverError> {
87        self.error.abort_opt()?.clever_error.as_ref()
88    }
89}
90
91#[cfg(feature = "intents")]
92impl std::fmt::Display for SimulationFailure {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        write!(f, "transaction simulation failed")?;
95
96        if let Some(cmd) = self.command() {
97            write!(f, " in command {cmd}")?;
98        }
99
100        if let Some(kind) = self.kind() {
101            write!(f, " ({})", kind.as_str_name())?;
102        }
103
104        if let Some(desc) = self.description()
105            && !desc.is_empty()
106        {
107            write!(f, ": {desc}")?;
108        }
109
110        if let Some(abort) = self.move_abort() {
111            if let Some(loc) = &abort.location
112                && let (Some(pkg), Some(module)) = (loc.package.as_deref(), loc.module.as_deref())
113            {
114                write!(f, " at {pkg}::{module}")?;
115                if let Some(func) = loc.function_name.as_deref() {
116                    write!(f, "::{func}")?;
117                }
118            }
119
120            if let Some(clever) = &abort.clever_error
121                && let Some(name) = clever.constant_name.as_deref()
122            {
123                write!(f, " [{name}")?;
124                if let Some(sui_rpc::proto::sui::rpc::v2::clever_error::Value::Rendered(v)) =
125                    &clever.value
126                {
127                    write!(f, " = {v}")?;
128                }
129                write!(f, "]")?;
130            }
131        }
132
133        Ok(())
134    }
135}