fuel_tx/
transaction.rs

1use crate::{
2    TxPointer,
3    input::{
4        coin::{
5            CoinPredicate,
6            CoinSigned,
7        },
8        message::{
9            MessageCoinPredicate,
10            MessageDataPredicate,
11        },
12    },
13    policies::Policies,
14};
15use fuel_crypto::{
16    Hasher,
17    PublicKey,
18};
19use fuel_types::{
20    Address,
21    AssetId,
22    BlobId,
23    Bytes32,
24    Nonce,
25    Salt,
26    Word,
27    canonical::{
28        Deserialize,
29        Error,
30        Serialize,
31    },
32};
33
34use input::*;
35use output::*;
36
37#[cfg(feature = "typescript")]
38use self::{
39    input::typescript as input_ts,
40    output::typescript as output_ts,
41};
42
43use alloc::vec::{
44    IntoIter,
45    Vec,
46};
47use itertools::Itertools;
48
49mod fee;
50mod metadata;
51mod repr;
52mod types;
53mod validity;
54
55mod id;
56
57pub mod consensus_parameters;
58pub mod policies;
59
60pub use consensus_parameters::{
61    ConsensusParameters,
62    ContractParameters,
63    DependentCost,
64    FeeParameters,
65    GasCosts,
66    GasCostsValues,
67    PredicateParameters,
68    ScriptParameters,
69    TxParameters,
70};
71pub use fee::{
72    Chargeable,
73    TransactionFee,
74};
75use fuel_types::bytes::Bytes;
76pub use metadata::Cacheable;
77pub use repr::TransactionRepr;
78pub use types::*;
79pub use validity::{
80    FormatValidityChecks,
81    ValidityError,
82};
83
84#[cfg(feature = "alloc")]
85pub use id::Signable;
86
87pub use id::{
88    PrepareSign,
89    UniqueIdentifier,
90};
91
92/// Identification of transaction (also called transaction hash)
93pub type TxId = Bytes32;
94
95/// The fuel transaction entity <https://github.com/FuelLabs/fuel-specs/blob/master/src/tx-format/transaction.md>.
96#[derive(
97    Debug,
98    Clone,
99    PartialEq,
100    Eq,
101    Hash,
102    strum_macros::EnumCount,
103    serde::Serialize,
104    serde::Deserialize,
105)]
106#[cfg_attr(
107    feature = "da-compression",
108    derive(fuel_compression::Compress, fuel_compression::Decompress)
109)]
110#[allow(clippy::large_enum_variant)]
111pub enum Transaction {
112    Script(Script),
113    Create(Create),
114    Mint(Mint),
115    Upgrade(Upgrade),
116    Upload(Upload),
117    Blob(Blob),
118}
119
120#[cfg(feature = "test-helpers")]
121impl Default for Transaction {
122    fn default() -> Self {
123        Script::default().into()
124    }
125}
126
127impl Transaction {
128    /// Return default valid transaction useful for tests.
129    #[cfg(all(feature = "random", feature = "std", feature = "test-helpers"))]
130    pub fn default_test_tx() -> Self {
131        use crate::Finalizable;
132
133        crate::TransactionBuilder::script(vec![], vec![])
134            .max_fee_limit(0)
135            .add_fee_input()
136            .finalize()
137            .into()
138    }
139
140    pub const fn script(
141        gas_limit: Word,
142        script: Vec<u8>,
143        script_data: Vec<u8>,
144        policies: Policies,
145        inputs: Vec<Input>,
146        outputs: Vec<Output>,
147        witnesses: Vec<Witness>,
148    ) -> Script {
149        let receipts_root = Bytes32::zeroed();
150
151        Script {
152            body: ScriptBody {
153                script_gas_limit: gas_limit,
154                receipts_root,
155                script: ScriptCode::new(script),
156                script_data: Bytes::new(script_data),
157            },
158            policies,
159            inputs,
160            outputs,
161            witnesses,
162            metadata: None,
163        }
164    }
165
166    pub fn create(
167        bytecode_witness_index: u16,
168        policies: Policies,
169        salt: Salt,
170        mut storage_slots: Vec<StorageSlot>,
171        inputs: Vec<Input>,
172        outputs: Vec<Output>,
173        witnesses: Vec<Witness>,
174    ) -> Create {
175        // sort incoming storage slots
176        storage_slots.sort();
177
178        Create {
179            body: CreateBody {
180                bytecode_witness_index,
181                salt,
182                storage_slots,
183            },
184            policies,
185            inputs,
186            outputs,
187            witnesses,
188            metadata: None,
189        }
190    }
191
192    pub fn mint(
193        tx_pointer: TxPointer,
194        input_contract: input::contract::Contract,
195        output_contract: output::contract::Contract,
196        mint_amount: Word,
197        mint_asset_id: AssetId,
198        gas_price: Word,
199    ) -> Mint {
200        Mint {
201            tx_pointer,
202            input_contract,
203            output_contract,
204            mint_amount,
205            mint_asset_id,
206            gas_price,
207            metadata: None,
208        }
209    }
210
211    pub fn upgrade(
212        upgrade_purpose: UpgradePurpose,
213        policies: Policies,
214        inputs: Vec<Input>,
215        outputs: Vec<Output>,
216        witnesses: Vec<Witness>,
217    ) -> Upgrade {
218        Upgrade {
219            body: UpgradeBody {
220                purpose: upgrade_purpose,
221            },
222            policies,
223            inputs,
224            outputs,
225            witnesses,
226            metadata: None,
227        }
228    }
229
230    /// Creates an `Upgrade` transaction with the purpose of upgrading the consensus
231    /// parameters.
232    pub fn upgrade_consensus_parameters(
233        consensus_parameters: &ConsensusParameters,
234        policies: Policies,
235        inputs: Vec<Input>,
236        outputs: Vec<Output>,
237        mut witnesses: Vec<Witness>,
238    ) -> Result<Upgrade, ValidityError> {
239        let serialized_consensus_parameters = postcard::to_allocvec(consensus_parameters)
240            .map_err(|_| {
241                ValidityError::TransactionUpgradeConsensusParametersSerialization
242            })?;
243        let checksum = Hasher::hash(&serialized_consensus_parameters);
244        let witness_index = u16::try_from(witnesses.len())
245            .map_err(|_| ValidityError::TransactionWitnessesMax)?;
246        witnesses.push(serialized_consensus_parameters.into());
247
248        Ok(Upgrade {
249            body: UpgradeBody {
250                purpose: UpgradePurpose::ConsensusParameters {
251                    witness_index,
252                    checksum,
253                },
254            },
255            policies,
256            inputs,
257            outputs,
258            witnesses,
259            metadata: None,
260        })
261    }
262
263    pub fn upload(
264        upload_body: UploadBody,
265        policies: Policies,
266        inputs: Vec<Input>,
267        outputs: Vec<Output>,
268        witnesses: Vec<Witness>,
269    ) -> Upload {
270        Upload {
271            body: upload_body,
272            policies,
273            inputs,
274            outputs,
275            witnesses,
276            metadata: None,
277        }
278    }
279
280    pub fn upload_from_subsection(
281        subsection: UploadSubsection,
282        policies: Policies,
283        inputs: Vec<Input>,
284        outputs: Vec<Output>,
285        mut witnesses: Vec<Witness>,
286    ) -> Upload {
287        let body = UploadBody {
288            root: subsection.root,
289            witness_index: u16::try_from(witnesses.len()).unwrap_or(u16::MAX),
290            subsection_index: subsection.subsection_index,
291            subsections_number: subsection.subsections_number,
292            proof_set: subsection.proof_set,
293        };
294        witnesses.push(subsection.subsection.into());
295        Upload {
296            body,
297            policies,
298            inputs,
299            outputs,
300            witnesses,
301            metadata: None,
302        }
303    }
304
305    pub fn blob(
306        body: BlobBody,
307        policies: Policies,
308        inputs: Vec<Input>,
309        outputs: Vec<Output>,
310        witnesses: Vec<Witness>,
311    ) -> Blob {
312        Blob {
313            body,
314            policies,
315            inputs,
316            outputs,
317            witnesses,
318            metadata: None,
319        }
320    }
321
322    pub fn blob_from_bytes(
323        bytes: Vec<u8>,
324        policies: Policies,
325        inputs: Vec<Input>,
326        outputs: Vec<Output>,
327        mut witnesses: Vec<Witness>,
328    ) -> Blob {
329        let body = BlobBody {
330            id: BlobId::compute(&bytes),
331            witness_index: u16::try_from(witnesses.len()).unwrap_or(u16::MAX),
332        };
333        witnesses.push(bytes.into());
334        Blob {
335            body,
336            policies,
337            inputs,
338            outputs,
339            witnesses,
340            metadata: None,
341        }
342    }
343
344    /// Convert the type into a JSON string
345    ///
346    /// This is implemented as infallible because serde_json will fail only if the type
347    /// can't serialize one of its attributes. We don't have such case with the
348    /// transaction because all of its attributes are trivially serialized.
349    ///
350    /// If an error happens, a JSON string with the error description will be returned
351    #[cfg(test)]
352    pub fn to_json(&self) -> alloc::string::String {
353        serde_json::to_string(self)
354            .unwrap_or_else(|e| alloc::format!(r#"{{"error": "{e}"}}"#))
355    }
356
357    /// Attempt to deserialize a transaction from a JSON string, returning `None` if it
358    /// fails
359    #[cfg(test)]
360    pub fn from_json<J>(json: J) -> Option<Self>
361    where
362        J: AsRef<str>,
363    {
364        // we opt to return `Option` to not leak serde concrete error implementations in
365        // the crate. considering we don't expect to handle failures downstream
366        // (e.g. if a string is not a valid json, then we simply don't have a
367        // transaction out of that), then its not required to leak the type
368        serde_json::from_str(json.as_ref()).ok()
369    }
370
371    pub const fn is_script(&self) -> bool {
372        matches!(self, Self::Script { .. })
373    }
374
375    pub const fn is_create(&self) -> bool {
376        matches!(self, Self::Create { .. })
377    }
378
379    pub const fn is_mint(&self) -> bool {
380        matches!(self, Self::Mint { .. })
381    }
382
383    pub const fn is_upgrade(&self) -> bool {
384        matches!(self, Self::Upgrade { .. })
385    }
386
387    pub const fn is_upload(&self) -> bool {
388        matches!(self, Self::Upload { .. })
389    }
390
391    pub const fn is_blob(&self) -> bool {
392        matches!(self, Self::Blob { .. })
393    }
394
395    pub const fn as_script(&self) -> Option<&Script> {
396        match self {
397            Self::Script(script) => Some(script),
398            _ => None,
399        }
400    }
401
402    pub fn as_script_mut(&mut self) -> Option<&mut Script> {
403        match self {
404            Self::Script(script) => Some(script),
405            _ => None,
406        }
407    }
408
409    pub const fn as_create(&self) -> Option<&Create> {
410        match self {
411            Self::Create(create) => Some(create),
412            _ => None,
413        }
414    }
415
416    pub fn as_create_mut(&mut self) -> Option<&mut Create> {
417        match self {
418            Self::Create(create) => Some(create),
419            _ => None,
420        }
421    }
422
423    pub const fn as_mint(&self) -> Option<&Mint> {
424        match self {
425            Self::Mint(mint) => Some(mint),
426            _ => None,
427        }
428    }
429
430    pub fn as_mint_mut(&mut self) -> Option<&mut Mint> {
431        match self {
432            Self::Mint(mint) => Some(mint),
433            _ => None,
434        }
435    }
436
437    pub const fn as_upgrade(&self) -> Option<&Upgrade> {
438        match self {
439            Self::Upgrade(tx) => Some(tx),
440            _ => None,
441        }
442    }
443
444    pub fn as_upgrade_mut(&mut self) -> Option<&mut Upgrade> {
445        match self {
446            Self::Upgrade(tx) => Some(tx),
447            _ => None,
448        }
449    }
450
451    pub const fn as_upload(&self) -> Option<&Upload> {
452        match self {
453            Self::Upload(tx) => Some(tx),
454            _ => None,
455        }
456    }
457
458    pub fn as_upload_mut(&mut self) -> Option<&mut Upload> {
459        match self {
460            Self::Upload(tx) => Some(tx),
461            _ => None,
462        }
463    }
464
465    pub const fn as_blob(&self) -> Option<&Blob> {
466        match self {
467            Self::Blob(tx) => Some(tx),
468            _ => None,
469        }
470    }
471
472    pub fn as_blob_mut(&mut self) -> Option<&mut Blob> {
473        match self {
474            Self::Blob(tx) => Some(tx),
475            _ => None,
476        }
477    }
478}
479
480pub trait Executable: field::Inputs + field::Outputs + field::Witnesses {
481    /// Returns the assets' ids used in the inputs in the order of inputs.
482    fn input_asset_ids<'a>(
483        &'a self,
484        base_asset_id: &'a AssetId,
485    ) -> IntoIter<&'a AssetId> {
486        self.inputs()
487            .iter()
488            .filter_map(|input| match input {
489                Input::CoinPredicate(CoinPredicate { asset_id, .. })
490                | Input::CoinSigned(CoinSigned { asset_id, .. }) => Some(asset_id),
491                Input::MessageCoinSigned(_)
492                | Input::MessageCoinPredicate(_)
493                | Input::MessageDataPredicate(_)
494                | Input::MessageDataSigned(_) => Some(base_asset_id),
495                _ => None,
496            })
497            .collect_vec()
498            .into_iter()
499    }
500
501    /// Returns unique assets' ids used in the inputs.
502    fn input_asset_ids_unique<'a>(
503        &'a self,
504        base_asset_id: &'a AssetId,
505    ) -> IntoIter<&'a AssetId> {
506        let asset_ids = self.input_asset_ids(base_asset_id);
507
508        #[cfg(feature = "std")]
509        let asset_ids = asset_ids.unique();
510
511        #[cfg(not(feature = "std"))]
512        let asset_ids = asset_ids.sorted().dedup();
513
514        asset_ids.collect_vec().into_iter()
515    }
516
517    /// Checks that all owners of inputs in the predicates are valid.
518    fn check_predicate_owners(&self) -> bool {
519        self.inputs()
520            .iter()
521            .filter_map(|i| match i {
522                Input::CoinPredicate(CoinPredicate {
523                    owner, predicate, ..
524                }) => Some((owner, predicate)),
525                Input::MessageDataPredicate(MessageDataPredicate {
526                    recipient,
527                    predicate,
528                    ..
529                }) => Some((recipient, predicate)),
530                Input::MessageCoinPredicate(MessageCoinPredicate {
531                    recipient,
532                    predicate,
533                    ..
534                }) => Some((recipient, predicate)),
535                _ => None,
536            })
537            .fold(true, |result, (owner, predicate)| {
538                result && Input::is_predicate_owner_valid(owner, &**predicate)
539            })
540    }
541
542    /// Append a new unsigned coin input to the transaction.
543    ///
544    /// When the transaction is constructed, [`Signable::sign_inputs`] should
545    /// be called for every secret key used with this method.
546    ///
547    /// The production of the signatures can be done only after the full
548    /// transaction skeleton is built because the input of the hash message
549    /// is the ID of the final transaction.
550    fn add_unsigned_coin_input(
551        &mut self,
552        utxo_id: UtxoId,
553        owner: &PublicKey,
554        amount: Word,
555        asset_id: AssetId,
556        tx_pointer: TxPointer,
557        witness_index: u16,
558    ) {
559        let owner = Input::owner(owner);
560
561        let input = Input::coin_signed(
562            utxo_id,
563            owner,
564            amount,
565            asset_id,
566            tx_pointer,
567            witness_index,
568        );
569        self.inputs_mut().push(input);
570    }
571
572    /// Append a new unsigned message input to the transaction.
573    ///
574    /// When the transaction is constructed, [`Signable::sign_inputs`] should
575    /// be called for every secret key used with this method.
576    ///
577    /// The production of the signatures can be done only after the full
578    /// transaction skeleton is built because the input of the hash message
579    /// is the ID of the final transaction.
580    fn add_unsigned_message_input(
581        &mut self,
582        sender: Address,
583        recipient: Address,
584        nonce: Nonce,
585        amount: Word,
586        data: Vec<u8>,
587        witness_index: u16,
588    ) {
589        let input = if data.is_empty() {
590            Input::message_coin_signed(sender, recipient, amount, nonce, witness_index)
591        } else {
592            Input::message_data_signed(
593                sender,
594                recipient,
595                amount,
596                nonce,
597                witness_index,
598                data,
599            )
600        };
601
602        self.inputs_mut().push(input);
603    }
604}
605
606impl<T: field::Inputs + field::Outputs + field::Witnesses> Executable for T {}
607
608impl From<Script> for Transaction {
609    fn from(tx: Script) -> Self {
610        Self::Script(tx)
611    }
612}
613
614impl From<Create> for Transaction {
615    fn from(tx: Create) -> Self {
616        Self::Create(tx)
617    }
618}
619
620impl From<Mint> for Transaction {
621    fn from(tx: Mint) -> Self {
622        Self::Mint(tx)
623    }
624}
625
626impl From<Upgrade> for Transaction {
627    fn from(tx: Upgrade) -> Self {
628        Self::Upgrade(tx)
629    }
630}
631
632impl From<Upload> for Transaction {
633    fn from(tx: Upload) -> Self {
634        Self::Upload(tx)
635    }
636}
637
638impl From<Blob> for Transaction {
639    fn from(tx: Blob) -> Self {
640        Self::Blob(tx)
641    }
642}
643
644impl Serialize for Transaction {
645    fn size_static(&self) -> usize {
646        match self {
647            Self::Script(tx) => tx.size_static(),
648            Self::Create(tx) => tx.size_static(),
649            Self::Mint(tx) => tx.size_static(),
650            Self::Upgrade(tx) => tx.size_static(),
651            Self::Upload(tx) => tx.size_static(),
652            Self::Blob(tx) => tx.size_static(),
653        }
654    }
655
656    fn size_dynamic(&self) -> usize {
657        match self {
658            Self::Script(tx) => tx.size_dynamic(),
659            Self::Create(tx) => tx.size_dynamic(),
660            Self::Mint(tx) => tx.size_dynamic(),
661            Self::Upgrade(tx) => tx.size_dynamic(),
662            Self::Upload(tx) => tx.size_dynamic(),
663            Self::Blob(tx) => tx.size_dynamic(),
664        }
665    }
666
667    fn encode_static<O: fuel_types::canonical::Output + ?Sized>(
668        &self,
669        buffer: &mut O,
670    ) -> Result<(), Error> {
671        match self {
672            Self::Script(tx) => tx.encode_static(buffer),
673            Self::Create(tx) => tx.encode_static(buffer),
674            Self::Mint(tx) => tx.encode_static(buffer),
675            Self::Upgrade(tx) => tx.encode_static(buffer),
676            Self::Upload(tx) => tx.encode_static(buffer),
677            Self::Blob(tx) => tx.encode_static(buffer),
678        }
679    }
680
681    fn encode_dynamic<O: fuel_types::canonical::Output + ?Sized>(
682        &self,
683        buffer: &mut O,
684    ) -> Result<(), Error> {
685        match self {
686            Self::Script(tx) => tx.encode_dynamic(buffer),
687            Self::Create(tx) => tx.encode_dynamic(buffer),
688            Self::Mint(tx) => tx.encode_dynamic(buffer),
689            Self::Upgrade(tx) => tx.encode_dynamic(buffer),
690            Self::Upload(tx) => tx.encode_dynamic(buffer),
691            Self::Blob(tx) => tx.encode_dynamic(buffer),
692        }
693    }
694}
695
696impl Deserialize for Transaction {
697    fn decode_static<I: fuel_types::canonical::Input + ?Sized>(
698        buffer: &mut I,
699    ) -> Result<Self, Error> {
700        let mut discriminant_buffer = [0u8; 8];
701        buffer.peek(&mut discriminant_buffer)?;
702
703        let discriminant =
704            <TransactionRepr as Deserialize>::decode(&mut &discriminant_buffer[..])?;
705
706        match discriminant {
707            TransactionRepr::Script => {
708                Ok(<Script as Deserialize>::decode_static(buffer)?.into())
709            }
710            TransactionRepr::Create => {
711                Ok(<Create as Deserialize>::decode_static(buffer)?.into())
712            }
713            TransactionRepr::Mint => {
714                Ok(<Mint as Deserialize>::decode_static(buffer)?.into())
715            }
716            TransactionRepr::Upgrade => {
717                Ok(<Upgrade as Deserialize>::decode_static(buffer)?.into())
718            }
719            TransactionRepr::Upload => {
720                Ok(<Upload as Deserialize>::decode_static(buffer)?.into())
721            }
722            TransactionRepr::Blob => {
723                Ok(<Blob as Deserialize>::decode_static(buffer)?.into())
724            }
725        }
726    }
727
728    fn decode_dynamic<I: fuel_types::canonical::Input + ?Sized>(
729        &mut self,
730        buffer: &mut I,
731    ) -> Result<(), Error> {
732        match self {
733            Self::Script(tx) => tx.decode_dynamic(buffer),
734            Self::Create(tx) => tx.decode_dynamic(buffer),
735            Self::Mint(tx) => tx.decode_dynamic(buffer),
736            Self::Upgrade(tx) => tx.decode_dynamic(buffer),
737            Self::Upload(tx) => tx.decode_dynamic(buffer),
738            Self::Blob(tx) => tx.decode_dynamic(buffer),
739        }
740    }
741}
742
743/// The module contains traits for each possible field in the `Transaction`. Those traits
744/// can be used to write generic code based on the different combinations of the fields.
745pub mod field {
746    use crate::{
747        Input,
748        Output,
749        StorageSlot,
750        UpgradePurpose as UpgradePurposeType,
751        Witness,
752        input,
753        output,
754        policies,
755    };
756    use fuel_types::{
757        AssetId,
758        BlockHeight,
759        Bytes32,
760        Word,
761    };
762
763    use crate::policies::PolicyType;
764    use alloc::vec::Vec;
765    use core::ops::{
766        Deref,
767        DerefMut,
768    };
769
770    pub trait Tip {
771        fn tip(&self) -> Word;
772        fn set_tip(&mut self, value: Word);
773    }
774
775    impl<T: Policies + ?Sized> Tip for T {
776        #[inline(always)]
777        fn tip(&self) -> Word {
778            self.policies().get(PolicyType::Tip).unwrap_or_default()
779        }
780
781        #[inline(always)]
782        fn set_tip(&mut self, price: Word) {
783            self.policies_mut().set(PolicyType::Tip, Some(price))
784        }
785    }
786
787    pub trait WitnessLimit {
788        fn witness_limit(&self) -> Word;
789        fn set_witness_limit(&mut self, value: Word);
790    }
791
792    impl<T: Policies + ?Sized> WitnessLimit for T {
793        #[inline(always)]
794        fn witness_limit(&self) -> Word {
795            self.policies().get(PolicyType::WitnessLimit).unwrap_or(0)
796        }
797
798        #[inline(always)]
799        fn set_witness_limit(&mut self, value: Word) {
800            self.policies_mut()
801                .set(PolicyType::WitnessLimit, Some(value))
802        }
803    }
804
805    pub trait ScriptGasLimit {
806        fn script_gas_limit(&self) -> &Word;
807        fn script_gas_limit_mut(&mut self) -> &mut Word;
808        fn script_gas_limit_offset(&self) -> usize {
809            Self::script_gas_limit_offset_static()
810        }
811
812        fn script_gas_limit_offset_static() -> usize;
813    }
814
815    pub trait Maturity {
816        fn maturity(&self) -> BlockHeight;
817        fn set_maturity(&mut self, value: BlockHeight);
818    }
819
820    impl<T: Policies + ?Sized> Maturity for T {
821        #[inline(always)]
822        fn maturity(&self) -> BlockHeight {
823            self.policies()
824                .get(PolicyType::Maturity)
825                .map(|value| u32::try_from(value).unwrap_or(u32::MAX).into())
826                .unwrap_or_default()
827        }
828
829        #[inline(always)]
830        fn set_maturity(&mut self, block_height: BlockHeight) {
831            self.policies_mut()
832                .set(PolicyType::Maturity, Some(*block_height.deref() as u64))
833        }
834    }
835
836    pub trait Expiration {
837        fn expiration(&self) -> BlockHeight;
838        fn set_expiration(&mut self, value: BlockHeight);
839    }
840
841    impl<T: Policies + ?Sized> Expiration for T {
842        #[inline(always)]
843        fn expiration(&self) -> BlockHeight {
844            self.policies()
845                .get(PolicyType::Expiration)
846                .and_then(|value| u32::try_from(value).ok())
847                .unwrap_or(u32::MAX)
848                .into()
849        }
850
851        #[inline(always)]
852        fn set_expiration(&mut self, block_height: BlockHeight) {
853            self.policies_mut()
854                .set(PolicyType::Expiration, Some(*block_height.deref() as u64))
855        }
856    }
857
858    pub trait MaxFeeLimit {
859        fn max_fee_limit(&self) -> Word;
860        fn set_max_fee_limit(&mut self, value: Word);
861    }
862
863    impl<T: Policies + ?Sized> MaxFeeLimit for T {
864        #[inline(always)]
865        fn max_fee_limit(&self) -> Word {
866            self.policies().get(PolicyType::MaxFee).unwrap_or(0)
867        }
868
869        #[inline(always)]
870        fn set_max_fee_limit(&mut self, value: Word) {
871            self.policies_mut().set(PolicyType::MaxFee, Some(value))
872        }
873    }
874
875    pub trait Owner {
876        fn owner(&self) -> Option<Word>;
877        fn set_owner(&mut self, value: Word);
878    }
879
880    impl<T: Policies + ?Sized> Owner for T {
881        #[inline(always)]
882        fn owner(&self) -> Option<Word> {
883            self.policies().get(PolicyType::Owner)
884        }
885
886        #[inline(always)]
887        fn set_owner(&mut self, value: Word) {
888            self.policies_mut().set(PolicyType::Owner, Some(value))
889        }
890    }
891
892    pub trait TxPointer {
893        fn tx_pointer(&self) -> &crate::TxPointer;
894        fn tx_pointer_mut(&mut self) -> &mut crate::TxPointer;
895        fn tx_pointer_offset(&self) -> usize {
896            Self::tx_pointer_static()
897        }
898
899        fn tx_pointer_static() -> usize;
900    }
901
902    pub trait InputContract {
903        fn input_contract(&self) -> &input::contract::Contract;
904        fn input_contract_mut(&mut self) -> &mut input::contract::Contract;
905        fn input_contract_offset(&self) -> usize;
906    }
907
908    pub trait OutputContract {
909        fn output_contract(&self) -> &output::contract::Contract;
910        fn output_contract_mut(&mut self) -> &mut output::contract::Contract;
911        fn output_contract_offset(&self) -> usize;
912    }
913
914    pub trait MintAmount {
915        fn mint_amount(&self) -> &Word;
916        fn mint_amount_mut(&mut self) -> &mut Word;
917        fn mint_amount_offset(&self) -> usize;
918    }
919
920    pub trait MintAssetId {
921        fn mint_asset_id(&self) -> &AssetId;
922        fn mint_asset_id_mut(&mut self) -> &mut AssetId;
923        fn mint_asset_id_offset(&self) -> usize;
924    }
925
926    pub trait MintGasPrice {
927        fn gas_price(&self) -> &Word;
928        fn gas_price_mut(&mut self) -> &mut Word;
929        fn gas_price_offset(&self) -> usize;
930    }
931
932    pub trait ReceiptsRoot {
933        fn receipts_root(&self) -> &Bytes32;
934        fn receipts_root_mut(&mut self) -> &mut Bytes32;
935        fn receipts_root_offset(&self) -> usize {
936            Self::receipts_root_offset_static()
937        }
938
939        fn receipts_root_offset_static() -> usize;
940    }
941
942    pub trait Script {
943        fn script(&self) -> &Vec<u8>;
944        fn script_mut(&mut self) -> &mut Vec<u8>;
945        fn script_offset(&self) -> usize {
946            Self::script_offset_static()
947        }
948
949        fn script_offset_static() -> usize;
950    }
951
952    pub trait ScriptData {
953        fn script_data(&self) -> &Vec<u8>;
954        fn script_data_mut(&mut self) -> &mut Vec<u8>;
955        fn script_data_offset(&self) -> usize;
956    }
957
958    pub trait ChargeableBody<Body> {
959        fn body(&self) -> &Body;
960        fn body_mut(&mut self) -> &mut Body;
961        fn body_offset_end(&self) -> usize;
962    }
963
964    pub trait Policies {
965        fn policies(&self) -> &policies::Policies;
966        fn policies_mut(&mut self) -> &mut policies::Policies;
967        fn policies_offset(&self) -> usize;
968    }
969
970    pub trait BytecodeWitnessIndex {
971        fn bytecode_witness_index(&self) -> &u16;
972        fn bytecode_witness_index_mut(&mut self) -> &mut u16;
973        fn bytecode_witness_index_offset(&self) -> usize {
974            Self::bytecode_witness_index_offset_static()
975        }
976
977        fn bytecode_witness_index_offset_static() -> usize;
978    }
979
980    pub trait Salt {
981        fn salt(&self) -> &fuel_types::Salt;
982        fn salt_mut(&mut self) -> &mut fuel_types::Salt;
983        fn salt_offset(&self) -> usize {
984            Self::salt_offset_static()
985        }
986
987        fn salt_offset_static() -> usize;
988    }
989
990    pub trait StorageSlots {
991        fn storage_slots(&self) -> &Vec<StorageSlot>;
992        fn storage_slots_mut(&mut self) -> StorageSlotRef<'_>;
993        fn storage_slots_offset_static() -> usize;
994
995        /// Returns the offset to the `StorageSlot` at `idx` index, if any.
996        fn storage_slots_offset_at(&self, idx: usize) -> Option<usize>;
997    }
998
999    /// Reference object for mutating storage slots which will automatically
1000    /// sort the slots when dropped.
1001    pub struct StorageSlotRef<'a> {
1002        pub(crate) storage_slots: &'a mut Vec<StorageSlot>,
1003    }
1004
1005    impl AsMut<Vec<StorageSlot>> for StorageSlotRef<'_> {
1006        fn as_mut(&mut self) -> &mut Vec<StorageSlot> {
1007            self.storage_slots
1008        }
1009    }
1010
1011    impl Deref for StorageSlotRef<'_> {
1012        type Target = [StorageSlot];
1013
1014        fn deref(&self) -> &Self::Target {
1015            self.storage_slots.deref()
1016        }
1017    }
1018
1019    impl DerefMut for StorageSlotRef<'_> {
1020        fn deref_mut(&mut self) -> &mut Self::Target {
1021            self.storage_slots.deref_mut()
1022        }
1023    }
1024
1025    /// Ensure the storage slots are sorted after being set
1026    impl Drop for StorageSlotRef<'_> {
1027        fn drop(&mut self) {
1028            self.storage_slots.sort()
1029        }
1030    }
1031
1032    pub trait Inputs {
1033        fn inputs(&self) -> &Vec<Input>;
1034        fn inputs_mut(&mut self) -> &mut Vec<Input>;
1035        fn inputs_offset(&self) -> usize;
1036
1037        /// Returns the offset to the `Input` at `idx` index, if any.
1038        fn inputs_offset_at(&self, idx: usize) -> Option<usize>;
1039
1040        /// Returns predicate's offset and length of the `Input` at `idx`, if any.
1041        fn inputs_predicate_offset_at(&self, idx: usize) -> Option<(usize, usize)>;
1042    }
1043
1044    pub trait Outputs {
1045        fn outputs(&self) -> &Vec<Output>;
1046        fn outputs_mut(&mut self) -> &mut Vec<Output>;
1047        fn outputs_offset(&self) -> usize;
1048
1049        /// Returns the offset to the `Output` at `idx` index, if any.
1050        fn outputs_offset_at(&self, idx: usize) -> Option<usize>;
1051    }
1052
1053    pub trait Witnesses {
1054        fn witnesses(&self) -> &Vec<Witness>;
1055        fn witnesses_mut(&mut self) -> &mut Vec<Witness>;
1056        fn witnesses_offset(&self) -> usize;
1057
1058        /// Returns the offset to the `Witness` at `idx` index, if any.
1059        fn witnesses_offset_at(&self, idx: usize) -> Option<usize>;
1060    }
1061
1062    pub trait UpgradePurpose {
1063        fn upgrade_purpose(&self) -> &UpgradePurposeType;
1064        fn upgrade_purpose_mut(&mut self) -> &mut UpgradePurposeType;
1065        fn upgrade_purpose_offset(&self) -> usize {
1066            Self::upgrade_purpose_offset_static()
1067        }
1068
1069        fn upgrade_purpose_offset_static() -> usize;
1070    }
1071
1072    pub trait BytecodeRoot {
1073        fn bytecode_root(&self) -> &Bytes32;
1074        fn bytecode_root_mut(&mut self) -> &mut Bytes32;
1075        fn bytecode_root_offset(&self) -> usize {
1076            Self::bytecode_root_offset_static()
1077        }
1078
1079        fn bytecode_root_offset_static() -> usize;
1080    }
1081
1082    pub trait BlobId {
1083        fn blob_id(&self) -> &fuel_types::BlobId;
1084        fn blob_id_mut(&mut self) -> &mut fuel_types::BlobId;
1085        fn blob_id_offset(&self) -> usize {
1086            Self::blob_id_offset_static()
1087        }
1088
1089        fn blob_id_offset_static() -> usize;
1090    }
1091
1092    pub trait SubsectionIndex {
1093        fn subsection_index(&self) -> &u16;
1094        fn subsection_index_mut(&mut self) -> &mut u16;
1095        fn subsection_index_offset(&self) -> usize {
1096            Self::subsection_index_offset_static()
1097        }
1098
1099        fn subsection_index_offset_static() -> usize;
1100    }
1101
1102    pub trait SubsectionsNumber {
1103        fn subsections_number(&self) -> &u16;
1104        fn subsections_number_mut(&mut self) -> &mut u16;
1105        fn subsections_number_offset(&self) -> usize {
1106            Self::subsections_number_offset_static()
1107        }
1108
1109        fn subsections_number_offset_static() -> usize;
1110    }
1111
1112    pub trait ProofSet {
1113        fn proof_set(&self) -> &Vec<Bytes32>;
1114        fn proof_set_mut(&mut self) -> &mut Vec<Bytes32>;
1115        fn proof_set_offset(&self) -> usize {
1116            Self::proof_set_offset_static()
1117        }
1118
1119        fn proof_set_offset_static() -> usize;
1120
1121        /// Returns the offset to the `Bytes32` at `idx` index, if any.
1122        fn proof_set_offset_at(&self, idx: usize) -> Option<usize>;
1123    }
1124}
1125
1126#[cfg(feature = "typescript")]
1127pub mod typescript {
1128    use wasm_bindgen::prelude::*;
1129
1130    use crate::{
1131        AssetId,
1132        Witness,
1133        Word,
1134        transaction::{
1135            Policies,
1136            input_ts::Input,
1137            output_ts::Output,
1138        },
1139    };
1140    use alloc::{
1141        boxed::Box,
1142        format,
1143        string::String,
1144        vec::Vec,
1145    };
1146    use fuel_types::Bytes32;
1147
1148    #[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
1149    #[wasm_bindgen]
1150    pub struct Transaction(#[wasm_bindgen(skip)] pub Box<crate::Transaction>);
1151
1152    #[derive(
1153        Default, Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize,
1154    )]
1155    #[wasm_bindgen]
1156    pub struct Create(#[wasm_bindgen(skip)] pub Box<crate::Create>);
1157
1158    #[derive(
1159        Default, Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize,
1160    )]
1161    #[wasm_bindgen]
1162    pub struct Script(#[wasm_bindgen(skip)] pub Box<crate::Script>);
1163
1164    #[derive(
1165        Default, Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize,
1166    )]
1167    #[wasm_bindgen]
1168    pub struct Mint(#[wasm_bindgen(skip)] pub Box<crate::Mint>);
1169
1170    #[derive(
1171        Default, Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize,
1172    )]
1173    #[wasm_bindgen]
1174    pub struct Upgrade(#[wasm_bindgen(skip)] pub Box<crate::Upgrade>);
1175
1176    #[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
1177    #[wasm_bindgen]
1178    pub struct UpgradePurpose(#[wasm_bindgen(skip)] pub Box<crate::UpgradePurpose>);
1179
1180    #[derive(
1181        Default, Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize,
1182    )]
1183    #[wasm_bindgen]
1184    pub struct Upload(#[wasm_bindgen(skip)] pub Box<crate::Upload>);
1185
1186    #[wasm_bindgen]
1187    impl Transaction {
1188        #[wasm_bindgen(js_name = toJSON)]
1189        pub fn to_json(&self) -> String {
1190            serde_json::to_string(&self.0).expect("unable to json format")
1191        }
1192
1193        #[wasm_bindgen(js_name = toString)]
1194        pub fn typescript_to_string(&self) -> String {
1195            format!("{:?}", self.0)
1196        }
1197
1198        #[wasm_bindgen(js_name = to_bytes)]
1199        pub fn typescript_to_bytes(&self) -> Vec<u8> {
1200            use fuel_types::canonical::Serialize;
1201            self.0.to_bytes()
1202        }
1203
1204        #[wasm_bindgen(js_name = from_bytes)]
1205        pub fn typescript_from_bytes(value: &[u8]) -> Result<Transaction, js_sys::Error> {
1206            use fuel_types::canonical::Deserialize;
1207            crate::Transaction::from_bytes(value)
1208                .map(|v| Transaction(Box::new(v)))
1209                .map_err(|e| js_sys::Error::new(&format!("{:?}", e)))
1210        }
1211
1212        #[wasm_bindgen]
1213        pub fn script(
1214            gas_limit: Word,
1215            script: Vec<u8>,
1216            script_data: Vec<u8>,
1217            policies: Policies,
1218            inputs: Vec<Input>,
1219            outputs: Vec<Output>,
1220            witnesses: Vec<Witness>,
1221        ) -> Script {
1222            Script(
1223                crate::Transaction::script(
1224                    gas_limit,
1225                    script,
1226                    script_data,
1227                    policies,
1228                    inputs.into_iter().map(|v| *v.0).collect(),
1229                    outputs.into_iter().map(|v| *v.0).collect(),
1230                    witnesses,
1231                )
1232                .into(),
1233            )
1234        }
1235
1236        #[wasm_bindgen]
1237        pub fn create(
1238            bytecode_witness_index: u16,
1239            policies: Policies,
1240            salt: crate::Salt,
1241            storage_slots: Vec<crate::StorageSlot>,
1242            inputs: Vec<Input>,
1243            outputs: Vec<Output>,
1244            witnesses: Vec<Witness>,
1245        ) -> Create {
1246            Create(
1247                crate::Transaction::create(
1248                    bytecode_witness_index,
1249                    policies,
1250                    salt,
1251                    storage_slots,
1252                    inputs.into_iter().map(|v| *v.0).collect(),
1253                    outputs.into_iter().map(|v| *v.0).collect(),
1254                    witnesses,
1255                )
1256                .into(),
1257            )
1258        }
1259
1260        #[wasm_bindgen]
1261        pub fn mint(
1262            tx_pointer: crate::TxPointer,
1263            input_contract: crate::input::contract::Contract,
1264            output_contract: crate::output::contract::Contract,
1265            mint_amount: Word,
1266            mint_asset_id: AssetId,
1267            gas_price: Word,
1268        ) -> Mint {
1269            Mint(
1270                crate::Mint {
1271                    tx_pointer,
1272                    input_contract,
1273                    output_contract,
1274                    mint_amount,
1275                    mint_asset_id,
1276                    gas_price,
1277                    metadata: None,
1278                }
1279                .into(),
1280            )
1281        }
1282
1283        #[wasm_bindgen]
1284        pub fn upgrade(
1285            purpose: UpgradePurpose,
1286            policies: Policies,
1287            inputs: Vec<Input>,
1288            outputs: Vec<Output>,
1289            witnesses: Vec<Witness>,
1290        ) -> Upgrade {
1291            Upgrade(
1292                crate::Transaction::upgrade(
1293                    *purpose.0.as_ref(),
1294                    policies,
1295                    inputs.into_iter().map(|v| *v.0).collect(),
1296                    outputs.into_iter().map(|v| *v.0).collect(),
1297                    witnesses,
1298                )
1299                .into(),
1300            )
1301        }
1302
1303        #[wasm_bindgen]
1304        pub fn upload(
1305            root: Bytes32,
1306            witness_index: u16,
1307            subsection_index: u16,
1308            subsections_number: u16,
1309            proof_set: Vec<Bytes32>,
1310            policies: Policies,
1311            inputs: Vec<Input>,
1312            outputs: Vec<Output>,
1313            witnesses: Vec<Witness>,
1314        ) -> Upload {
1315            Upload(
1316                crate::Transaction::upload(
1317                    crate::UploadBody {
1318                        root,
1319                        witness_index,
1320                        subsection_index,
1321                        subsections_number,
1322                        proof_set,
1323                    },
1324                    policies,
1325                    inputs.into_iter().map(|v| *v.0).collect(),
1326                    outputs.into_iter().map(|v| *v.0).collect(),
1327                    witnesses,
1328                )
1329                .into(),
1330            )
1331        }
1332    }
1333
1334    macro_rules! ts_methods {
1335        ($t:ty, $tx:expr) => {
1336            #[wasm_bindgen]
1337            impl $t {
1338                #[wasm_bindgen(js_name = as_tx)]
1339                pub fn typescript_wrap_tx(self) -> Transaction {
1340                    Transaction(Box::new($tx(self.0.as_ref().clone())))
1341                }
1342
1343                #[wasm_bindgen(constructor)]
1344                pub fn typescript_new() -> $t {
1345                    <$t>::default()
1346                }
1347
1348                #[wasm_bindgen(js_name = toJSON)]
1349                pub fn to_json(&self) -> String {
1350                    serde_json::to_string(&self).expect("unable to json format")
1351                }
1352
1353                #[wasm_bindgen(js_name = toString)]
1354                pub fn typescript_to_string(&self) -> String {
1355                    format!("{:?}", self)
1356                }
1357
1358                #[wasm_bindgen(js_name = to_bytes)]
1359                pub fn typescript_to_bytes(&self) -> Vec<u8> {
1360                    use fuel_types::canonical::Serialize;
1361                    <_ as Serialize>::to_bytes(self.0.as_ref())
1362                }
1363
1364                #[wasm_bindgen(js_name = from_bytes)]
1365                pub fn typescript_from_bytes(value: &[u8]) -> Result<$t, js_sys::Error> {
1366                    use fuel_types::canonical::Deserialize;
1367                    let res = <_ as Deserialize>::from_bytes(value)
1368                        .map_err(|e| js_sys::Error::new(&format!("{:?}", e)))?;
1369                    Ok(Self(Box::new(res)))
1370                }
1371            }
1372        };
1373    }
1374
1375    ts_methods!(Script, crate::Transaction::Script);
1376    ts_methods!(Create, crate::Transaction::Create);
1377    ts_methods!(Mint, crate::Transaction::Mint);
1378    ts_methods!(Upgrade, crate::Transaction::Upgrade);
1379    ts_methods!(Upload, crate::Transaction::Upload);
1380}
1381
1382#[allow(non_snake_case)]
1383#[cfg(test)]
1384mod tests {
1385    use super::*;
1386
1387    #[test]
1388    fn script__metered_bytes_size___includes_witnesses() {
1389        let witness = [0u8; 64].to_vec();
1390        let script_with_no_witnesses = Transaction::script(
1391            Default::default(),
1392            vec![],
1393            vec![],
1394            Default::default(),
1395            vec![],
1396            vec![],
1397            vec![],
1398        );
1399        let script_with_witnesses = Transaction::script(
1400            Default::default(),
1401            vec![],
1402            vec![],
1403            Default::default(),
1404            vec![],
1405            vec![],
1406            vec![witness.clone().into()],
1407        );
1408
1409        assert_eq!(
1410            script_with_witnesses.metered_bytes_size(),
1411            script_with_no_witnesses.metered_bytes_size() + witness.size()
1412        );
1413    }
1414
1415    #[test]
1416    fn create__metered_bytes_size___includes_witnesses() {
1417        let witness = [0u8; 64].to_vec();
1418        let create_with_no_witnesses = Transaction::create(
1419            0,
1420            Default::default(),
1421            Default::default(),
1422            vec![],
1423            vec![],
1424            vec![],
1425            vec![],
1426        );
1427        let create_with_witnesses = Transaction::create(
1428            0,
1429            Default::default(),
1430            Default::default(),
1431            vec![],
1432            vec![],
1433            vec![],
1434            vec![witness.clone().into()],
1435        );
1436        assert_eq!(
1437            create_with_witnesses.metered_bytes_size(),
1438            create_with_no_witnesses.metered_bytes_size() + witness.size()
1439        );
1440    }
1441
1442    #[test]
1443    fn upgrade__metered_bytes_size___includes_witnesses() {
1444        let witness = [0u8; 64].to_vec();
1445        let tx_with_no_witnesses = Transaction::upgrade(
1446            UpgradePurpose::StateTransition {
1447                root: Default::default(),
1448            },
1449            Default::default(),
1450            vec![],
1451            vec![],
1452            vec![],
1453        );
1454        let tx_with_witnesses = Transaction::upgrade(
1455            UpgradePurpose::StateTransition {
1456                root: Default::default(),
1457            },
1458            Default::default(),
1459            vec![],
1460            vec![],
1461            vec![witness.clone().into()],
1462        );
1463        assert_eq!(
1464            tx_with_witnesses.metered_bytes_size(),
1465            tx_with_no_witnesses.metered_bytes_size() + witness.size()
1466        );
1467    }
1468
1469    #[test]
1470    fn upload__metered_bytes_size__includes_witness() {
1471        let witness = [0u8; 64].to_vec();
1472        let tx_with_no_witnesses = Transaction::upload(
1473            Default::default(),
1474            Default::default(),
1475            vec![],
1476            vec![],
1477            vec![],
1478        );
1479        let tx_with_witnesses = Transaction::upload(
1480            Default::default(),
1481            Default::default(),
1482            vec![],
1483            vec![],
1484            vec![witness.clone().into()],
1485        );
1486        assert_eq!(
1487            tx_with_witnesses.metered_bytes_size(),
1488            tx_with_no_witnesses.metered_bytes_size() + witness.size()
1489        );
1490    }
1491}