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}