fuel_tx/transaction/types/
output.rs

1use fuel_crypto::Hasher;
2use fuel_types::{
3    canonical::{
4        self,
5        Serialize as _,
6    },
7    Address,
8    AssetId,
9    Bytes32,
10    ContractId,
11    Nonce,
12    Word,
13};
14
15mod consts;
16pub mod contract;
17mod repr;
18
19use contract::Contract;
20pub use repr::OutputRepr;
21
22#[derive(
23    Debug,
24    Clone,
25    Copy,
26    PartialEq,
27    Eq,
28    Hash,
29    strum_macros::EnumCount,
30    serde::Serialize,
31    serde::Deserialize,
32)]
33#[cfg_attr(
34    feature = "da-compression",
35    derive(fuel_compression::Compress, fuel_compression::Decompress)
36)]
37#[derive(canonical::Deserialize, canonical::Serialize)]
38pub enum Output {
39    Coin {
40        to: Address,
41        amount: Word,
42        asset_id: AssetId,
43    },
44
45    Contract(Contract),
46
47    Change {
48        to: Address,
49        #[cfg_attr(feature = "da-compression", compress(skip))]
50        amount: Word,
51        asset_id: AssetId,
52    },
53
54    Variable {
55        #[cfg_attr(feature = "da-compression", compress(skip))]
56        to: Address,
57        #[cfg_attr(feature = "da-compression", compress(skip))]
58        amount: Word,
59        #[cfg_attr(feature = "da-compression", compress(skip))]
60        asset_id: AssetId,
61    },
62
63    ContractCreated {
64        contract_id: ContractId,
65        state_root: Bytes32,
66    },
67}
68
69impl Default for Output {
70    fn default() -> Self {
71        Self::ContractCreated {
72            contract_id: Default::default(),
73            state_root: Default::default(),
74        }
75    }
76}
77
78impl Output {
79    pub const fn repr(&self) -> OutputRepr {
80        OutputRepr::from_output(self)
81    }
82
83    pub const fn coin(to: Address, amount: Word, asset_id: AssetId) -> Self {
84        Self::Coin {
85            to,
86            amount,
87            asset_id,
88        }
89    }
90
91    pub const fn contract(
92        input_index: u16,
93        balance_root: Bytes32,
94        state_root: Bytes32,
95    ) -> Self {
96        Self::Contract(Contract {
97            input_index,
98            balance_root,
99            state_root,
100        })
101    }
102
103    pub const fn change(to: Address, amount: Word, asset_id: AssetId) -> Self {
104        Self::Change {
105            to,
106            amount,
107            asset_id,
108        }
109    }
110
111    pub const fn variable(to: Address, amount: Word, asset_id: AssetId) -> Self {
112        Self::Variable {
113            to,
114            amount,
115            asset_id,
116        }
117    }
118
119    pub const fn contract_created(contract_id: ContractId, state_root: Bytes32) -> Self {
120        Self::ContractCreated {
121            contract_id,
122            state_root,
123        }
124    }
125
126    pub const fn asset_id(&self) -> Option<&AssetId> {
127        match self {
128            Output::Coin { asset_id, .. }
129            | Output::Change { asset_id, .. }
130            | Output::Variable { asset_id, .. } => Some(asset_id),
131            _ => None,
132        }
133    }
134
135    pub const fn to(&self) -> Option<&Address> {
136        match self {
137            Output::Coin { to, .. }
138            | Output::Change { to, .. }
139            | Output::Variable { to, .. } => Some(to),
140            _ => None,
141        }
142    }
143
144    pub const fn amount(&self) -> Option<Word> {
145        match self {
146            Output::Coin { amount, .. }
147            | Output::Change { amount, .. }
148            | Output::Variable { amount, .. } => Some(*amount),
149            _ => None,
150        }
151    }
152
153    pub const fn input_index(&self) -> Option<u16> {
154        match self {
155            Output::Contract(Contract { input_index, .. }) => Some(*input_index),
156            _ => None,
157        }
158    }
159
160    pub const fn balance_root(&self) -> Option<&Bytes32> {
161        match self {
162            Output::Contract(Contract { balance_root, .. }) => Some(balance_root),
163            _ => None,
164        }
165    }
166
167    pub const fn state_root(&self) -> Option<&Bytes32> {
168        match self {
169            Output::Contract(Contract { state_root, .. })
170            | Output::ContractCreated { state_root, .. } => Some(state_root),
171            _ => None,
172        }
173    }
174
175    pub const fn contract_id(&self) -> Option<&ContractId> {
176        match self {
177            Output::ContractCreated { contract_id, .. } => Some(contract_id),
178            _ => None,
179        }
180    }
181
182    pub const fn is_coin(&self) -> bool {
183        matches!(self, Self::Coin { .. })
184    }
185
186    pub const fn is_change(&self) -> bool {
187        matches!(self, Self::Change { .. })
188    }
189
190    pub const fn is_variable(&self) -> bool {
191        matches!(self, Self::Variable { .. })
192    }
193
194    pub const fn is_contract(&self) -> bool {
195        matches!(self, Self::Contract(_))
196    }
197
198    pub const fn is_contract_created(&self) -> bool {
199        matches!(self, Self::ContractCreated { .. })
200    }
201
202    pub fn message_nonce(txid: &Bytes32, idx: Word) -> Nonce {
203        (*Hasher::default()
204            .chain(txid)
205            .chain(idx.to_bytes())
206            .finalize())
207        .into()
208    }
209
210    pub fn message_digest(data: &[u8]) -> Bytes32 {
211        Hasher::hash(data)
212    }
213
214    /// Empties fields that should be zero during the signing.
215    pub fn prepare_sign(&mut self) {
216        match self {
217            Output::Contract(contract) => contract.prepare_sign(),
218
219            Output::Change { amount, .. } => {
220                *amount = 0;
221            }
222
223            Output::Variable {
224                to,
225                amount,
226                asset_id,
227                ..
228            } => {
229                *to = Address::default();
230                *amount = 0;
231                *asset_id = AssetId::default();
232            }
233
234            _ => (),
235        }
236    }
237
238    /// Prepare the output for VM initialization for script execution
239    /// or predicate verification
240    pub fn prepare_init_execute(&mut self) {
241        self.prepare_sign() // Currently does the same thing
242    }
243}
244
245#[cfg(feature = "typescript")]
246pub mod typescript {
247    use wasm_bindgen::prelude::*;
248
249    use super::*;
250
251    use fuel_types::{
252        Address,
253        AssetId,
254        Bytes32,
255        Word,
256    };
257
258    use alloc::{
259        boxed::Box,
260        format,
261        string::String,
262        vec::Vec,
263    };
264
265    #[derive(Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
266    #[wasm_bindgen]
267    pub struct Output(#[wasm_bindgen(skip)] pub Box<crate::Output>);
268
269    #[wasm_bindgen]
270    impl Output {
271        #[wasm_bindgen(js_name = toJSON)]
272        pub fn to_json(&self) -> String {
273            serde_json::to_string(&self.0).expect("unable to json format")
274        }
275
276        #[wasm_bindgen(js_name = toString)]
277        pub fn typescript_to_string(&self) -> String {
278            format!("{:?}", self.0)
279        }
280
281        #[wasm_bindgen(js_name = to_bytes)]
282        pub fn typescript_to_bytes(&self) -> Vec<u8> {
283            use fuel_types::canonical::Serialize;
284            self.0.to_bytes()
285        }
286
287        #[wasm_bindgen(js_name = from_bytes)]
288        pub fn typescript_from_bytes(value: &[u8]) -> Result<Output, js_sys::Error> {
289            use fuel_types::canonical::Deserialize;
290            crate::Output::from_bytes(value)
291                .map(|v| Output(Box::new(v)))
292                .map_err(|e| js_sys::Error::new(&format!("{:?}", e)))
293        }
294
295        #[wasm_bindgen]
296        pub fn coin(to: Address, amount: Word, asset_id: AssetId) -> Output {
297            Output(Box::new(crate::Output::coin(to, amount, asset_id)))
298        }
299
300        #[wasm_bindgen]
301        pub fn contract(
302            input_index: u16,
303            balance_root: Bytes32,
304            state_root: Bytes32,
305        ) -> Output {
306            Output(Box::new(crate::Output::contract(
307                input_index,
308                balance_root,
309                state_root,
310            )))
311        }
312
313        #[wasm_bindgen]
314        pub fn change(to: Address, amount: Word, asset_id: AssetId) -> Output {
315            Output(Box::new(crate::Output::change(to, amount, asset_id)))
316        }
317
318        #[wasm_bindgen]
319        pub fn variable(to: Address, amount: Word, asset_id: AssetId) -> Output {
320            Output(Box::new(crate::Output::variable(to, amount, asset_id)))
321        }
322
323        #[wasm_bindgen]
324        pub fn contract_created(contract_id: ContractId, state_root: Bytes32) -> Output {
325            Output(Box::new(crate::Output::contract_created(
326                contract_id,
327                state_root,
328            )))
329        }
330    }
331}