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
92pub type TxId = Bytes32;
94
95#[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 #[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 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 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 #[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 #[cfg(test)]
360 pub fn from_json<J>(json: J) -> Option<Self>
361 where
362 J: AsRef<str>,
363 {
364 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 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 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 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 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 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
743pub 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 fn storage_slots_offset_at(&self, idx: usize) -> Option<usize>;
997 }
998
999 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 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 fn inputs_offset_at(&self, idx: usize) -> Option<usize>;
1039
1040 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 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 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 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}