near_fetch/
result.rs

1//! Result and execution types from results of RPC calls to the network.
2
3use near_gas::NearGas;
4use near_primitives::borsh;
5use near_primitives::views::{
6    ExecutionOutcomeWithIdView, ExecutionStatusView, FinalExecutionOutcomeView,
7    FinalExecutionStatus, SignedTransactionView,
8};
9
10use base64::{engine::general_purpose, Engine as _};
11
12use crate::error::Result;
13use crate::Error;
14
15/// Execution related info as a result of performing a successful transaction
16/// execution on the network. This value can be converted into the returned
17/// value of the transaction via [`ExecutionSuccess::json`] or [`ExecutionSuccess::borsh`]
18pub type ExecutionSuccess = ExecutionResult<Value>;
19
20/// The transaction/receipt details of a transaction execution. This object
21/// can be used to retrieve data such as logs and gas burnt per transaction
22/// or receipt.
23#[derive(PartialEq, Eq, Clone, Debug)]
24#[non_exhaustive]
25pub struct ExecutionDetails {
26    /// Original signed transaction.
27    pub transaction: SignedTransactionView,
28    /// The execution outcome of the signed transaction.
29    pub transaction_outcome: ExecutionOutcomeWithIdView,
30    /// The execution outcome of receipts.
31    pub receipts_outcome: Vec<ExecutionOutcomeWithIdView>,
32}
33
34impl ExecutionDetails {
35    /// Returns just the transaction outcome.
36    pub fn outcome(&self) -> &ExecutionOutcomeWithIdView {
37        &self.transaction_outcome
38    }
39
40    /// Grab all outcomes after the execution of the transaction. This includes outcomes
41    /// from the transaction and all the receipts it generated.
42    pub fn outcomes(&self) -> Vec<&ExecutionOutcomeWithIdView> {
43        let mut outcomes = vec![self.outcome()];
44        outcomes.extend(self.receipt_outcomes());
45        outcomes
46    }
47
48    /// Grab all outcomes after the execution of the transaction. This includes outcomes
49    /// only from receipts generated by this transaction.
50    pub fn receipt_outcomes(&self) -> &[ExecutionOutcomeWithIdView] {
51        &self.receipts_outcome
52    }
53
54    /// Grab all outcomes that did not succeed the execution of this transaction. This
55    /// will also include the failures from receipts as well.
56    pub fn failures(&self) -> Vec<&ExecutionOutcomeWithIdView> {
57        let mut failures = Vec::new();
58        if matches!(
59            self.transaction_outcome.outcome.status,
60            ExecutionStatusView::Failure(_)
61        ) {
62            failures.push(&self.transaction_outcome);
63        }
64        failures.extend(self.receipt_failures());
65        failures
66    }
67
68    /// Just like `failures`, grab only failed receipt outcomes.
69    pub fn receipt_failures(&self) -> Vec<&ExecutionOutcomeWithIdView> {
70        self.receipt_outcomes()
71            .iter()
72            .filter(|receipt| matches!(receipt.outcome.status, ExecutionStatusView::Failure(_)))
73            .collect()
74    }
75
76    /// Grab all logs from both the transaction and receipt outcomes.
77    pub fn logs(&self) -> Vec<&str> {
78        self.outcomes()
79            .into_iter()
80            .flat_map(|outcome| &outcome.outcome.logs)
81            .map(String::as_str)
82            .collect()
83    }
84}
85
86/// The result after evaluating the status of an execution. This can be [`ExecutionSuccess`]
87/// for successful executions or a [`ExecutionFailure`] for failed ones.
88#[derive(PartialEq, Eq, Debug, Clone)]
89#[non_exhaustive]
90pub struct ExecutionResult<T> {
91    /// Total gas burnt by the execution
92    pub total_gas_burnt: NearGas,
93
94    /// Value returned from an execution. This is a base64 encoded str for a successful
95    /// execution or a `TxExecutionError` if a failed one.
96    pub value: T,
97
98    /// Additional details related to the execution.
99    pub details: ExecutionDetails,
100}
101
102/// Execution related info found after performing a transaction. Can be converted
103/// into [`ExecutionSuccess`] or [`ExecutionFailure`] through [`into_result`]
104///
105/// [`into_result`]: crate::result::ExecutionFinalResult::into_result
106#[derive(PartialEq, Eq, Clone, Debug)]
107// #[must_use = "use `into_result()` to handle potential execution errors"]
108pub struct ExecutionFinalResult {
109    status: FinalExecutionStatus,
110    pub details: ExecutionDetails,
111}
112
113impl ExecutionFinalResult {
114    pub(crate) fn from_view(view: FinalExecutionOutcomeView) -> Self {
115        Self {
116            status: view.status,
117            details: ExecutionDetails {
118                transaction: view.transaction,
119                transaction_outcome: view.transaction_outcome,
120                receipts_outcome: view.receipts_outcome,
121            },
122        }
123    }
124
125    /// Converts this object into a [`Result`] holding either [`ExecutionSuccess`] or [`ExecutionFailure`].
126    pub fn into_result(self) -> Result<ExecutionSuccess> {
127        let total_gas_burnt = self.total_gas_burnt();
128        match self.status {
129            FinalExecutionStatus::SuccessValue(value) => Ok(ExecutionResult {
130                total_gas_burnt,
131                value: Value::from_string(general_purpose::STANDARD.encode(value)),
132                details: self.details,
133            }),
134            FinalExecutionStatus::Failure(tx_error) => Err(Error::TxExecution(Box::new(tx_error))),
135            FinalExecutionStatus::NotStarted => Err(Error::TxStatus("NotStarted")),
136            FinalExecutionStatus::Started => Err(Error::TxStatus("Started")),
137        }
138    }
139
140    pub fn total_gas_burnt(&self) -> NearGas {
141        NearGas::from_gas(
142            self.details.transaction_outcome.outcome.gas_burnt
143                + self
144                    .details
145                    .receipts_outcome
146                    .iter()
147                    .map(|t| t.outcome.gas_burnt)
148                    .sum::<u64>(),
149        )
150    }
151
152    /// Returns the contained Ok value, consuming the self value.
153    ///
154    /// Because this function may panic, its use is generally discouraged. Instead, prefer
155    /// to call into [`into_result`] then pattern matching and handle the Err case explicitly.
156    ///
157    /// [`into_result`]: crate::result::ExecutionFinalResult::into_result
158    pub fn unwrap(self) -> ExecutionSuccess {
159        self.into_result().unwrap()
160    }
161
162    /// Deserialize an instance of type `T` from bytes of JSON text sourced from the
163    /// execution result of this call. This conversion can fail if the structure of
164    /// the internal state does not meet up with [`serde::de::DeserializeOwned`]'s
165    /// requirements.
166    pub fn json<T: serde::de::DeserializeOwned>(self) -> Result<T> {
167        self.into_result()?.json()
168    }
169
170    /// Deserialize an instance of type `T` from bytes sourced from the execution
171    /// result. This conversion can fail if the structure of the internal state does
172    /// not meet up with [`borsh::BorshDeserialize`]'s requirements.
173    pub fn borsh<T: borsh::BorshDeserialize>(self) -> Result<T> {
174        self.into_result()?.borsh()
175    }
176
177    /// Grab the underlying raw bytes returned from calling into a contract's function.
178    /// If we want to deserialize these bytes into a rust datatype, use [`ExecutionResult::json`]
179    /// or [`ExecutionResult::borsh`] instead.
180    pub fn raw_bytes(self) -> Result<Vec<u8>> {
181        self.into_result()?.raw_bytes()
182    }
183
184    /// Checks whether the transaction was successful. Returns true if
185    /// the transaction has a status of [`FinalExecutionStatus::SuccessValue`].
186    pub fn is_success(&self) -> bool {
187        matches!(self.status(), FinalExecutionStatus::SuccessValue(_))
188    }
189
190    /// Checks whether the transaction has failed. Returns true if
191    /// the transaction has a status of [`FinalExecutionStatus::Failure`].
192    pub fn is_failure(&self) -> bool {
193        matches!(self.status(), FinalExecutionStatus::Failure(_))
194    }
195
196    /// Returns just the transaction outcome.
197    pub fn outcome(&self) -> &ExecutionOutcomeWithIdView {
198        self.details.outcome()
199    }
200
201    /// Grab all outcomes after the execution of the transaction. This includes outcomes
202    /// from the transaction and all the receipts it generated.
203    pub fn outcomes(&self) -> Vec<&ExecutionOutcomeWithIdView> {
204        self.details.outcomes()
205    }
206
207    /// Grab all outcomes after the execution of the transaction. This includes outcomes
208    /// only from receipts generated by this transaction.
209    pub fn receipt_outcomes(&self) -> &[ExecutionOutcomeWithIdView] {
210        self.details.receipt_outcomes()
211    }
212
213    /// Grab all outcomes that did not succeed the execution of this transaction. This
214    /// will also include the failures from receipts as well.
215    pub fn failures(&self) -> Vec<&ExecutionOutcomeWithIdView> {
216        self.details.failures()
217    }
218
219    /// Just like `failures`, grab only failed receipt outcomes.
220    pub fn receipt_failures(&self) -> Vec<&ExecutionOutcomeWithIdView> {
221        self.details.receipt_failures()
222    }
223
224    /// Grab all logs from both the transaction and receipt outcomes.
225    pub fn logs(&self) -> Vec<&str> {
226        self.details.logs()
227    }
228
229    pub fn status(&self) -> &FinalExecutionStatus {
230        &self.status
231    }
232}
233
234impl ExecutionSuccess {
235    /// Deserialize an instance of type `T` from bytes of JSON text sourced from the
236    /// execution result of this call. This conversion can fail if the structure of
237    /// the internal state does not meet up with [`serde::de::DeserializeOwned`]'s
238    /// requirements.
239    pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T> {
240        self.value.json()
241    }
242
243    /// Deserialize an instance of type `T` from bytes sourced from the execution
244    /// result. This conversion can fail if the structure of the internal state does
245    /// not meet up with [`borsh::BorshDeserialize`]'s requirements.
246    pub fn borsh<T: borsh::BorshDeserialize>(&self) -> Result<T> {
247        self.value.borsh()
248    }
249
250    /// Grab the underlying raw bytes returned from calling into a contract's function.
251    /// If we want to deserialize these bytes into a rust datatype, use [`ExecutionResult::json`]
252    /// or [`ExecutionResult::borsh`] instead.
253    pub fn raw_bytes(&self) -> Result<Vec<u8>> {
254        self.value.raw_bytes()
255    }
256}
257
258impl<T> ExecutionResult<T> {
259    /// Returns just the transaction outcome.
260    pub fn outcome(&self) -> &ExecutionOutcomeWithIdView {
261        &self.details.transaction_outcome
262    }
263
264    /// Grab all outcomes after the execution of the transaction. This includes outcomes
265    /// from the transaction and all the receipts it generated.
266    pub fn outcomes(&self) -> Vec<&ExecutionOutcomeWithIdView> {
267        let mut outcomes = vec![self.outcome()];
268        outcomes.extend(self.receipt_outcomes());
269        outcomes
270    }
271
272    /// Grab all outcomes after the execution of the transaction. This includes outcomes
273    /// only from receipts generated by this transaction.
274    pub fn receipt_outcomes(&self) -> &[ExecutionOutcomeWithIdView] {
275        &self.details.receipts_outcome
276    }
277
278    /// Grab all outcomes that did not succeed the execution of this transaction. This
279    /// will also include the failures from receipts as well.
280    pub fn failures(&self) -> Vec<&ExecutionOutcomeWithIdView> {
281        let mut failures = Vec::new();
282        if matches!(
283            self.details.transaction_outcome.outcome.status,
284            ExecutionStatusView::Failure(_)
285        ) {
286            failures.push(&self.details.transaction_outcome);
287        }
288        failures.extend(self.receipt_failures());
289        failures
290    }
291
292    /// Just like `failures`, grab only failed receipt outcomes.
293    pub fn receipt_failures(&self) -> Vec<&ExecutionOutcomeWithIdView> {
294        self.receipt_outcomes()
295            .iter()
296            .filter(|receipt| matches!(receipt.outcome.status, ExecutionStatusView::Failure(_)))
297            .collect()
298    }
299
300    /// Grab all logs from both the transaction and receipt outcomes.
301    pub fn logs(&self) -> Vec<&str> {
302        self.outcomes()
303            .into_iter()
304            .flat_map(|outcome| &outcome.outcome.logs)
305            .map(String::as_str)
306            .collect()
307    }
308}
309
310/// Value type returned from an [`ExecutionOutcome`] or receipt result. This value
311/// can be converted into the underlying Rust datatype, or directly grab the raw
312/// bytes associated to the value.
313#[derive(Debug)]
314pub struct Value {
315    repr: String,
316}
317
318impl Value {
319    fn from_string(value: String) -> Self {
320        Self { repr: value }
321    }
322
323    /// Deserialize an instance of type `T` from bytes of JSON text sourced from the
324    /// execution result of this call. This conversion can fail if the structure of
325    /// the internal state does not meet up with [`serde::de::DeserializeOwned`]'s
326    /// requirements.
327    pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T> {
328        let buf = self.raw_bytes()?;
329        Ok(serde_json::from_slice(&buf)?)
330    }
331
332    /// Deserialize an instance of type `T` from bytes sourced from the execution
333    /// result. This conversion can fail if the structure of the internal state does
334    /// not meet up with [`borsh::BorshDeserialize`]'s requirements.
335    pub fn borsh<T: borsh::BorshDeserialize>(&self) -> Result<T> {
336        let buf = self.raw_bytes()?;
337        Ok(borsh::BorshDeserialize::try_from_slice(&buf)?)
338    }
339
340    /// Grab the underlying raw bytes returned from calling into a contract's function.
341    /// If we want to deserialize these bytes into a rust datatype, use [`json`]
342    /// or [`borsh`] instead.
343    ///
344    /// [`json`]: Value::json
345    /// [`borsh`]: Value::borsh
346    pub fn raw_bytes(&self) -> Result<Vec<u8>> {
347        Ok(general_purpose::STANDARD.decode(&self.repr)?)
348    }
349}