fuel_tx/
transaction.rs

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