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
91pub type TxId = Bytes32;
93
94#[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 #[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 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 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 #[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 #[cfg(test)]
359 pub fn from_json<J>(json: J) -> Option<Self>
360 where
361 J: AsRef<str>,
362 {
363 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 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 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 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 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 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
742pub 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 fn storage_slots_offset_at(&self, idx: usize) -> Option<usize>;
979 }
980
981 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 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 fn inputs_offset_at(&self, idx: usize) -> Option<usize>;
1021
1022 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 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 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 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}