iota_sdk_types/effects/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5mod v1;
6
7pub use v1::{
8    ChangedObject, IdOperation, ObjectIn, ObjectOut, TransactionEffectsV1, UnchangedSharedKind,
9    UnchangedSharedObject,
10};
11
12use crate::{SignedTransaction, TypeTag, execution_status::ExecutionStatus};
13
14/// The output or effects of executing a transaction
15///
16/// # BCS
17///
18/// The BCS serialized form for this type is defined by the following ABNF:
19///
20/// ```text
21/// transaction-effects =  %x00 effects-v1
22///                     =/ %x01 effects-v2
23/// ```
24#[derive(Eq, PartialEq, Clone, Debug)]
25#[cfg_attr(
26    feature = "schemars",
27    derive(schemars::JsonSchema),
28    schemars(tag = "version")
29)]
30#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
31pub enum TransactionEffects {
32    #[cfg_attr(feature = "schemars", schemars(rename = "1"))]
33    V1(Box<TransactionEffectsV1>),
34}
35
36impl TransactionEffects {
37    crate::def_is!(V1);
38
39    pub fn as_v1(&self) -> &TransactionEffectsV1 {
40        let Self::V1(effects) = self;
41        effects
42    }
43
44    pub fn into_v1(self) -> TransactionEffectsV1 {
45        let Self::V1(effects) = self;
46        *effects
47    }
48
49    /// Return the status of the transaction.
50    pub fn status(&self) -> &ExecutionStatus {
51        match self {
52            TransactionEffects::V1(e) => e.status(),
53        }
54    }
55
56    /// Return the epoch in which this transaction was executed.
57    pub fn epoch(&self) -> u64 {
58        match self {
59            TransactionEffects::V1(e) => e.epoch(),
60        }
61    }
62
63    /// Return the gas cost summary of the transaction.
64    pub fn gas_summary(&self) -> &crate::gas::GasCostSummary {
65        match self {
66            TransactionEffects::V1(e) => e.gas_summary(),
67        }
68    }
69}
70
71/// The result of a simulation (dry run), which includes the effects of the
72/// transaction and intermediate results for each command.
73#[derive(Clone, Debug, PartialEq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub struct DryRunResult {
76    /// The error that occurred during dry run execution, if any.
77    pub error: Option<String>,
78    /// The intermediate results for each command of the dry run execution,
79    /// including contents of mutated references and return values.
80    pub results: Vec<DryRunEffect>,
81    /// The transaction block representing the dry run execution.
82    pub transaction: Option<SignedTransaction>,
83    /// The effects of the transaction execution.
84    pub effects: Option<TransactionEffects>,
85}
86
87/// Effects of a single command in the dry run, including mutated references
88/// and return values.
89#[derive(Clone, Debug, PartialEq)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct DryRunEffect {
92    /// Changes made to arguments that were mutably borrowed by this
93    /// command.
94    pub mutated_references: Vec<DryRunMutation>,
95    /// Return results of this command.
96    pub return_values: Vec<DryRunReturn>,
97}
98
99/// A mutation to an argument that was mutably borrowed by a command.
100#[derive(Clone, Debug, PartialEq)]
101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102pub struct DryRunMutation {
103    /// The transaction argument that was mutated.
104    pub input: TransactionArgument,
105    /// The Move type of the mutated value.
106    pub type_tag: TypeTag,
107    /// The BCS representation of the mutated value.
108    pub bcs: Vec<u8>,
109}
110
111/// A return value from a command in the dry run.
112#[derive(Clone, Debug, PartialEq)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct DryRunReturn {
115    /// The Move type of the return value.
116    pub type_tag: TypeTag,
117    /// The BCS representation of the return value.
118    pub bcs: Vec<u8>,
119}
120
121/// A transaction argument used in programmable transactions.
122#[derive(Clone, Debug, PartialEq)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124pub enum TransactionArgument {
125    /// Reference to the gas coin.
126    GasCoin,
127    /// An input to the programmable transaction block.
128    Input {
129        /// Index of the programmable transaction block input (0-indexed).
130        ix: u32,
131    },
132    /// The result of another transaction command.
133    Result {
134        /// The index of the previous command (0-indexed) that returned this
135        /// result.
136        cmd: u32,
137        /// If the previous command returns multiple values, this is the
138        /// index of the individual result among the multiple
139        /// results from that command (also 0-indexed).
140        ix: Option<u32>,
141    },
142}
143
144#[cfg(feature = "serde")]
145#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
146mod serialization {
147    use serde::{Deserialize, Deserializer, Serialize, Serializer};
148
149    use super::{TransactionEffects, TransactionEffectsV1};
150
151    #[derive(serde::Serialize)]
152    #[serde(tag = "version")]
153    enum ReadableEffectsRef<'a> {
154        #[serde(rename = "1")]
155        V1(&'a TransactionEffectsV1),
156    }
157
158    #[derive(serde::Deserialize)]
159    #[serde(tag = "version")]
160    pub enum ReadableEffects {
161        #[serde(rename = "1")]
162        V1(Box<TransactionEffectsV1>),
163    }
164
165    #[derive(serde::Serialize)]
166    enum BinaryEffectsRef<'a> {
167        V1(&'a TransactionEffectsV1),
168    }
169
170    #[derive(serde::Deserialize)]
171    pub enum BinaryEffects {
172        V1(Box<TransactionEffectsV1>),
173    }
174
175    impl Serialize for TransactionEffects {
176        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177        where
178            S: Serializer,
179        {
180            if serializer.is_human_readable() {
181                let readable = match self {
182                    TransactionEffects::V1(fx) => ReadableEffectsRef::V1(fx),
183                };
184                readable.serialize(serializer)
185            } else {
186                let binary = match self {
187                    TransactionEffects::V1(fx) => BinaryEffectsRef::V1(fx),
188                };
189                binary.serialize(serializer)
190            }
191        }
192    }
193
194    impl<'de> Deserialize<'de> for TransactionEffects {
195        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
196        where
197            D: Deserializer<'de>,
198        {
199            if deserializer.is_human_readable() {
200                ReadableEffects::deserialize(deserializer).map(|readable| match readable {
201                    ReadableEffects::V1(fx) => Self::V1(fx),
202                })
203            } else {
204                BinaryEffects::deserialize(deserializer).map(|binary| match binary {
205                    BinaryEffects::V1(fx) => Self::V1(fx),
206                })
207            }
208        }
209    }
210
211    #[cfg(test)]
212    mod tests {
213        use base64ct::{Base64, Encoding};
214        #[cfg(target_arch = "wasm32")]
215        use wasm_bindgen_test::wasm_bindgen_test as test;
216
217        use super::TransactionEffects;
218
219        #[test]
220        fn effects_fixtures() {
221            // The files contain the bas64 encoded raw effects of transactions
222            const GENESIS_EFFECTS: &str = include_str!("fixtures/genesis-transaction-effects");
223            const SPONSOR_TX_EFFECTS: &str = include_str!("fixtures/sponsor-tx-effects");
224
225            for fixture in [GENESIS_EFFECTS, SPONSOR_TX_EFFECTS] {
226                let fixture = Base64::decode_vec(fixture.trim()).unwrap();
227                let fx: TransactionEffects = bcs::from_bytes(&fixture).unwrap();
228                assert_eq!(bcs::to_bytes(&fx).unwrap(), fixture);
229
230                let json = serde_json::to_string_pretty(&fx).unwrap();
231                println!("{json}");
232                assert_eq!(fx, serde_json::from_str(&json).unwrap());
233            }
234        }
235    }
236}