1use pallas_addresses::Address as PallasAddress;
2use pallas_crypto::{
3 hash::{Hash, Hasher},
4 key::ed25519,
5};
6use pallas_primitives::{
7 Fragment, NonEmptySet,
8 conway::{self, AuxiliaryData},
9};
10
11use std::{collections::HashMap, ops::Deref};
12
13use serde::{Deserialize, Serialize};
14
15use crate::TxBuilderError;
16
17use super::{
18 AssetName, Bytes, Bytes32, Bytes64, DatumBytes, DatumHash, Hash28, PolicyId, PubKeyHash,
19 PublicKey, ScriptBytes, ScriptHash, Signature, TransactionStatus, TxHash,
20};
21use pallas_codec::minicbor;
22#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
25pub struct StagingTransaction {
26 pub version: String,
28 pub status: TransactionStatus,
30 pub inputs: Option<Vec<Input>>,
32 pub reference_inputs: Option<Vec<Input>>,
34 pub outputs: Option<Vec<Output>>,
36 pub fee: Option<u64>,
38 pub mint: Option<MintAssets>,
40 pub valid_from_slot: Option<u64>,
42 pub invalid_from_slot: Option<u64>,
44 pub network_id: Option<u8>,
46 pub collateral_inputs: Option<Vec<Input>>,
48 pub collateral_output: Option<Output>,
50 pub disclosed_signers: Option<Vec<PubKeyHash>>,
52 pub scripts: Option<HashMap<ScriptHash, Script>>,
54 pub datums: Option<HashMap<DatumHash, DatumBytes>>,
56 pub redeemers: Option<Redeemers>,
58 pub script_data_hash: Option<Bytes32>,
60 pub signature_amount_override: Option<u8>,
62 pub change_address: Option<Address>,
64 pub language_views: Option<pallas_primitives::conway::LanguageViews>,
66 pub auxiliary_data: Option<AuxiliaryData>,
68 }
73
74impl StagingTransaction {
75 pub fn new() -> Self {
77 Self {
78 version: String::from("v1"),
79 status: TransactionStatus::Staging,
80 ..Default::default()
81 }
82 }
83
84 pub fn input(mut self, input: Input) -> Self {
86 let mut txins = self.inputs.unwrap_or_default();
87 txins.push(input);
88 self.inputs = Some(txins);
89 self
90 }
91
92 pub fn remove_input(mut self, input: Input) -> Self {
94 let mut txins = self.inputs.unwrap_or_default();
95 txins.retain(|x| *x != input);
96 self.inputs = Some(txins);
97 self
98 }
99
100 pub fn reference_input(mut self, input: Input) -> Self {
102 let mut ref_txins = self.reference_inputs.unwrap_or_default();
103 ref_txins.push(input);
104 self.reference_inputs = Some(ref_txins);
105 self
106 }
107
108 pub fn remove_reference_input(mut self, input: Input) -> Self {
110 let mut ref_txins = self.reference_inputs.unwrap_or_default();
111 ref_txins.retain(|x| *x != input);
112 self.reference_inputs = Some(ref_txins);
113 self
114 }
115
116 pub fn output(mut self, output: Output) -> Self {
118 let mut txouts = self.outputs.unwrap_or_default();
119 txouts.push(output);
120 self.outputs = Some(txouts);
121 self
122 }
123
124 pub fn remove_output(mut self, index: usize) -> Self {
126 let mut txouts = self.outputs.unwrap_or_default();
127 txouts.remove(index);
128 self.outputs = Some(txouts);
129 self
130 }
131
132 pub fn fee(mut self, fee: u64) -> Self {
134 self.fee = Some(fee);
135 self
136 }
137
138 pub fn clear_fee(mut self) -> Self {
140 self.fee = None;
141 self
142 }
143
144 pub fn mint_asset(
147 mut self,
148 policy: Hash<28>,
149 name: Vec<u8>,
150 amount: i64,
151 ) -> Result<Self, TxBuilderError> {
152 if name.len() > 32 {
153 return Err(TxBuilderError::AssetNameTooLong);
154 }
155
156 let mut mint = self.mint.map(|x| x.0).unwrap_or_default();
157
158 mint.entry(Hash28(*policy))
159 .and_modify(|policy_map| {
160 policy_map
161 .entry(name.clone().into())
162 .and_modify(|asset_map| {
163 *asset_map += amount;
164 })
165 .or_insert(amount);
166 })
167 .or_insert_with(|| {
168 let mut map: HashMap<Bytes, i64> = HashMap::new();
169 map.insert(name.clone().into(), amount);
170 map
171 });
172
173 self.mint = Some(MintAssets(mint));
174
175 Ok(self)
176 }
177
178 pub fn remove_mint_asset(mut self, policy: Hash<28>, name: Vec<u8>) -> Self {
180 let mut mint = if let Some(mint) = self.mint {
181 mint.0
182 } else {
183 return self;
184 };
185
186 if let Some(assets) = mint.get_mut(&Hash28(*policy)) {
187 assets.remove(&name.into());
188 if assets.is_empty() {
189 mint.remove(&Hash28(*policy));
190 }
191 }
192
193 self.mint = Some(MintAssets(mint));
194
195 self
196 }
197
198 pub fn valid_from_slot(mut self, slot: u64) -> Self {
200 self.valid_from_slot = Some(slot);
201 self
202 }
203
204 pub fn clear_valid_from_slot(mut self) -> Self {
206 self.valid_from_slot = None;
207 self
208 }
209
210 pub fn invalid_from_slot(mut self, slot: u64) -> Self {
212 self.invalid_from_slot = Some(slot);
213 self
214 }
215
216 pub fn clear_invalid_from_slot(mut self) -> Self {
218 self.invalid_from_slot = None;
219 self
220 }
221
222 pub fn network_id(mut self, id: u8) -> Self {
224 self.network_id = Some(id);
225 self
226 }
227
228 pub fn clear_network_id(mut self) -> Self {
230 self.network_id = None;
231 self
232 }
233
234 pub fn collateral_input(mut self, input: Input) -> Self {
236 let mut coll_ins = self.collateral_inputs.unwrap_or_default();
237 coll_ins.push(input);
238 self.collateral_inputs = Some(coll_ins);
239 self
240 }
241
242 pub fn remove_collateral_input(mut self, input: Input) -> Self {
244 let mut coll_ins = self.collateral_inputs.unwrap_or_default();
245 coll_ins.retain(|x| *x != input);
246 self.collateral_inputs = Some(coll_ins);
247 self
248 }
249
250 pub fn collateral_output(mut self, output: Output) -> Self {
252 self.collateral_output = Some(output);
253 self
254 }
255
256 pub fn clear_collateral_output(mut self) -> Self {
258 self.collateral_output = None;
259 self
260 }
261
262 pub fn disclosed_signer(mut self, pub_key_hash: Hash<28>) -> Self {
264 let mut disclosed_signers = self.disclosed_signers.unwrap_or_default();
265 disclosed_signers.push(Hash28(*pub_key_hash));
266 self.disclosed_signers = Some(disclosed_signers);
267 self
268 }
269
270 pub fn remove_disclosed_signer(mut self, pub_key_hash: Hash<28>) -> Self {
272 let mut disclosed_signers = self.disclosed_signers.unwrap_or_default();
273 disclosed_signers.retain(|x| *x != Hash28(*pub_key_hash));
274 self.disclosed_signers = Some(disclosed_signers);
275 self
276 }
277
278 pub fn script(mut self, language: ScriptKind, bytes: Vec<u8>) -> Self {
281 let mut scripts = self.scripts.unwrap_or_default();
282
283 let hash = match language {
284 ScriptKind::Native => Hasher::<224>::hash_tagged(bytes.as_ref(), 0),
285 ScriptKind::PlutusV1 => Hasher::<224>::hash_tagged(bytes.as_ref(), 1),
286 ScriptKind::PlutusV2 => Hasher::<224>::hash_tagged(bytes.as_ref(), 2),
287 ScriptKind::PlutusV3 => Hasher::<224>::hash_tagged(bytes.as_ref(), 3),
288 };
289
290 scripts.insert(
291 Hash28(*hash),
292 Script {
293 kind: language,
294 bytes: bytes.into(),
295 },
296 );
297
298 self.scripts = Some(scripts);
299 self
300 }
301
302 pub fn remove_script_by_hash(mut self, script_hash: Hash<28>) -> Self {
304 let mut scripts = self.scripts.unwrap_or_default();
305
306 scripts.remove(&Hash28(*script_hash));
307
308 self.scripts = Some(scripts);
309 self
310 }
311
312 pub fn datum(mut self, datum: Vec<u8>) -> Self {
314 let mut datums = self.datums.unwrap_or_default();
315
316 let hash = Hasher::<256>::hash_cbor(&datum);
317
318 datums.insert(Bytes32(*hash), datum.into());
319 self.datums = Some(datums);
320 self
321 }
322
323 pub fn remove_datum(mut self, datum: Vec<u8>) -> Self {
325 let mut datums = self.datums.unwrap_or_default();
326
327 let hash = Hasher::<256>::hash_cbor(&datum);
328
329 datums.remove(&Bytes32(*hash));
330 self.datums = Some(datums);
331 self
332 }
333
334 pub fn remove_datum_by_hash(mut self, datum_hash: Hash<32>) -> Self {
336 let mut datums = self.datums.unwrap_or_default();
337
338 datums.remove(&Bytes32(*datum_hash));
339 self.datums = Some(datums);
340 self
341 }
342
343 pub fn language_views(mut self, views: pallas_primitives::conway::LanguageViews) -> Self {
345 self.language_views = Some(views);
346 self
347 }
348
349 pub fn add_language(mut self, plutus_version: ScriptKind, cost_model: Vec<i64>) -> Self {
352 let version = match plutus_version {
353 ScriptKind::PlutusV1 => 0,
354 ScriptKind::PlutusV2 => 1,
355 ScriptKind::PlutusV3 => 2,
356 ScriptKind::Native => return self,
357 };
358 let mut map = self
359 .language_views
360 .as_ref()
361 .map(|v| v.0.clone())
362 .unwrap_or_default();
363 map.insert(version, cost_model);
364 self.language_views = Some(pallas_primitives::conway::LanguageViews(map));
365 self
366 }
367
368 pub fn add_spend_redeemer(
371 mut self,
372 input: Input,
373 plutus_data: Vec<u8>,
374 ex_units: Option<ExUnits>,
375 ) -> Self {
376 let mut rdmrs = self.redeemers.map(|x| x.0).unwrap_or_default();
377
378 rdmrs.insert(
379 RedeemerPurpose::Spend(input),
380 (plutus_data.into(), ex_units),
381 );
382
383 self.redeemers = Some(Redeemers(rdmrs));
384
385 self
386 }
387
388 pub fn remove_spend_redeemer(mut self, input: Input) -> Self {
390 let mut rdmrs = self.redeemers.map(|x| x.0).unwrap_or_default();
391
392 rdmrs.remove(&RedeemerPurpose::Spend(input));
393
394 self.redeemers = Some(Redeemers(rdmrs));
395
396 self
397 }
398
399 pub fn add_mint_redeemer(
402 mut self,
403 policy: Hash<28>,
404 plutus_data: Vec<u8>,
405 ex_units: Option<ExUnits>,
406 ) -> Self {
407 let mut rdmrs = self.redeemers.map(|x| x.0).unwrap_or_default();
408
409 rdmrs.insert(
410 RedeemerPurpose::Mint(Hash28(*policy)),
411 (plutus_data.into(), ex_units),
412 );
413
414 self.redeemers = Some(Redeemers(rdmrs));
415
416 self
417 }
418
419 pub fn remove_mint_redeemer(mut self, policy: Hash<28>) -> Self {
421 let mut rdmrs = self.redeemers.map(|x| x.0).unwrap_or_default();
422
423 rdmrs.remove(&RedeemerPurpose::Mint(Hash28(*policy)));
424
425 self.redeemers = Some(Redeemers(rdmrs));
426
427 self
428 }
429
430 pub fn signature_amount_override(mut self, amount: u8) -> Self {
432 self.signature_amount_override = Some(amount);
433 self
434 }
435
436 pub fn clear_signature_amount_override(mut self) -> Self {
438 self.signature_amount_override = None;
439 self
440 }
441
442 pub fn change_address(mut self, address: PallasAddress) -> Self {
444 self.change_address = Some(Address(address));
445 self
446 }
447
448 pub fn clear_change_address(mut self) -> Self {
450 self.change_address = None;
451 self
452 }
453
454 pub fn add_auxiliary_data(mut self, data: Vec<u8>) -> Self {
456 if let Ok(aux) = minicbor::decode::<AuxiliaryData>(data.as_ref()) {
457 self.auxiliary_data = Some(aux);
458 }
459 self
460 }
461
462 pub fn clear_auxiliary_data(mut self) -> Self {
464 self.auxiliary_data = None;
465 self
466 }
467}
468
469#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Hash, Clone)]
472pub struct Input {
473 pub tx_hash: TxHash,
475 pub txo_index: u64,
477}
478
479impl Input {
480 pub fn new(tx_hash: Hash<32>, txo_index: u64) -> Self {
482 Self {
483 tx_hash: Bytes32(*tx_hash),
484 txo_index,
485 }
486 }
487}
488
489#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
492pub struct Output {
493 pub address: Address,
495 pub lovelace: u64,
497 pub assets: Option<OutputAssets>,
499 pub datum: Option<Datum>,
501 pub script: Option<Script>,
503}
504
505impl Output {
506 pub fn new(address: PallasAddress, lovelace: u64) -> Self {
508 Self {
509 address: Address(address),
510 lovelace,
511 assets: None,
512 datum: None,
513 script: None,
514 }
515 }
516
517 pub fn add_asset(
520 mut self,
521 policy: Hash<28>,
522 name: Vec<u8>,
523 amount: u64,
524 ) -> Result<Self, TxBuilderError> {
525 if name.len() > 32 {
526 return Err(TxBuilderError::AssetNameTooLong);
527 }
528
529 let mut assets = self.assets.map(|x| x.0).unwrap_or_default();
530
531 assets
532 .entry(Hash28(*policy))
533 .and_modify(|policy_map| {
534 policy_map
535 .entry(name.clone().into())
536 .and_modify(|asset_map| {
537 *asset_map += amount;
538 })
539 .or_insert(amount);
540 })
541 .or_insert_with(|| {
542 let mut map: HashMap<Bytes, u64> = HashMap::new();
543 map.insert(name.clone().into(), amount);
544 map
545 });
546
547 self.assets = Some(OutputAssets(assets));
548
549 Ok(self)
550 }
551
552 pub fn set_inline_datum(mut self, plutus_data: Vec<u8>) -> Self {
554 self.datum = Some(Datum {
555 kind: DatumKind::Inline,
556 bytes: plutus_data.into(),
557 });
558
559 self
560 }
561
562 pub fn set_datum_hash(mut self, datum_hash: Hash<32>) -> Self {
564 self.datum = Some(Datum {
565 kind: DatumKind::Hash,
566 bytes: datum_hash.to_vec().into(),
567 });
568
569 self
570 }
571
572 pub fn set_inline_script(mut self, language: ScriptKind, bytes: Vec<u8>) -> Self {
574 self.script = Some(Script {
575 kind: language,
576 bytes: bytes.into(),
577 });
578
579 self
580 }
581}
582
583#[derive(PartialEq, Eq, Debug, Clone, Default)]
585pub struct OutputAssets(HashMap<PolicyId, HashMap<AssetName, u64>>);
586
587impl Deref for OutputAssets {
588 type Target = HashMap<PolicyId, HashMap<Bytes, u64>>;
589
590 fn deref(&self) -> &Self::Target {
591 &self.0
592 }
593}
594
595impl OutputAssets {
596 pub fn from_map(map: HashMap<PolicyId, HashMap<Bytes, u64>>) -> Self {
598 Self(map)
599 }
600}
601
602#[derive(PartialEq, Eq, Debug, Clone, Default)]
604pub struct MintAssets(HashMap<PolicyId, HashMap<AssetName, i64>>);
605
606impl Deref for MintAssets {
607 type Target = HashMap<PolicyId, HashMap<Bytes, i64>>;
608
609 fn deref(&self) -> &Self::Target {
610 &self.0
611 }
612}
613
614impl MintAssets {
615 pub fn new() -> Self {
617 MintAssets(HashMap::new())
618 }
619
620 pub fn from_map(map: HashMap<PolicyId, HashMap<Bytes, i64>>) -> Self {
622 Self(map)
623 }
624}
625
626#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
628#[serde(rename_all = "snake_case")]
629pub enum ScriptKind {
630 Native,
632 PlutusV1,
634 PlutusV2,
636 PlutusV3,
638}
639
640#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
642pub struct Script {
643 pub kind: ScriptKind,
645 pub bytes: ScriptBytes,
647}
648
649impl Script {
650 pub fn new(kind: ScriptKind, bytes: Vec<u8>) -> Self {
652 Self {
653 kind,
654 bytes: bytes.into(),
655 }
656 }
657}
658
659#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
661#[serde(rename_all = "snake_case")]
662pub enum DatumKind {
663 Hash,
665 Inline,
667}
668
669#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
671pub struct Datum {
672 pub kind: DatumKind,
674 pub bytes: DatumBytes,
676}
677
678#[derive(PartialEq, Eq, Hash, Debug, Clone)]
680pub enum RedeemerPurpose {
681 Spend(Input),
683 Mint(PolicyId),
685 }
688
689#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
691pub struct ExUnits {
692 pub mem: u64,
694 pub steps: u64,
696}
697
698#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default, Clone)]
700pub struct Redeemers(HashMap<RedeemerPurpose, (Bytes, Option<ExUnits>)>);
701
702impl Deref for Redeemers {
703 type Target = HashMap<RedeemerPurpose, (Bytes, Option<ExUnits>)>;
704
705 fn deref(&self) -> &Self::Target {
706 &self.0
707 }
708}
709
710impl Redeemers {
711 pub fn from_map(map: HashMap<RedeemerPurpose, (Bytes, Option<ExUnits>)>) -> Self {
713 Self(map)
714 }
715}
716
717#[derive(PartialEq, Eq, Debug, Clone)]
719pub struct Address(pub PallasAddress);
720
721impl Deref for Address {
722 type Target = PallasAddress;
723
724 fn deref(&self) -> &Self::Target {
725 &self.0
726 }
727}
728
729impl From<PallasAddress> for Address {
730 fn from(value: PallasAddress) -> Self {
731 Self(value)
732 }
733}
734
735#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
737#[serde(rename_all = "snake_case")]
738pub enum BuilderEra {
739 Babbage,
741 Conway,
743}
744
745pub trait Ed25519Signer {
747 fn public_key(&self) -> ed25519::PublicKey;
749 fn sign<T: AsRef<[u8]>>(&self, msg: T) -> ed25519::Signature;
751}
752
753impl Ed25519Signer for ed25519::SecretKey {
754 fn public_key(&self) -> ed25519::PublicKey {
755 self.public_key()
756 }
757
758 fn sign<T: AsRef<[u8]>>(&self, msg: T) -> ed25519::Signature {
759 self.sign(msg)
760 }
761}
762
763impl Ed25519Signer for ed25519::SecretKeyExtended {
764 fn public_key(&self) -> ed25519::PublicKey {
765 self.public_key()
766 }
767
768 fn sign<T: AsRef<[u8]>>(&self, msg: T) -> ed25519::Signature {
769 self.sign(msg)
770 }
771}
772
773#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
775pub struct BuiltTransaction {
776 pub version: String,
778 pub era: BuilderEra,
780 pub status: TransactionStatus,
782 pub tx_hash: TxHash,
784 pub tx_bytes: Bytes,
786 pub signatures: Option<HashMap<PublicKey, Signature>>,
788}
789
790impl BuiltTransaction {
791 pub fn sign<K: Ed25519Signer>(mut self, private_key: &K) -> Result<Self, TxBuilderError> {
794 let pubkey: [u8; 32] = private_key
795 .public_key()
796 .as_ref()
797 .try_into()
798 .map_err(|_| TxBuilderError::MalformedKey)?;
799
800 let signature: [u8; ed25519::Signature::SIZE] = private_key
801 .sign(self.tx_hash.0)
802 .as_ref()
803 .try_into()
804 .unwrap();
805
806 match self.era {
807 BuilderEra::Conway => {
808 let mut new_sigs = self.signatures.unwrap_or_default();
809
810 new_sigs.insert(Bytes32(pubkey), Bytes64(signature));
811
812 self.signatures = Some(new_sigs);
813
814 let mut tx = conway::Tx::decode_fragment(&self.tx_bytes.0)
816 .map_err(|_| TxBuilderError::CorruptedTxBytes)?;
817
818 let mut vkey_witnesses = tx
819 .transaction_witness_set
820 .vkeywitness
821 .as_ref()
822 .map(|x| x.clone().to_vec())
823 .unwrap_or_default();
824
825 vkey_witnesses.push(conway::VKeyWitness {
826 vkey: Vec::from(pubkey.as_ref()).into(),
827 signature: Vec::from(signature.as_ref()).into(),
828 });
829
830 tx.transaction_witness_set.vkeywitness =
831 Some(NonEmptySet::from_vec(vkey_witnesses).unwrap());
832
833 self.tx_bytes = tx.encode_fragment().unwrap().into();
834 }
835 _ => return Err(TxBuilderError::UnsupportedEra),
836 }
837
838 Ok(self)
839 }
840
841 pub fn add_signature(
843 mut self,
844 pub_key: ed25519::PublicKey,
845 signature: [u8; 64],
846 ) -> Result<Self, TxBuilderError> {
847 match self.era {
848 BuilderEra::Conway => {
849 let mut new_sigs = self.signatures.unwrap_or_default();
850
851 new_sigs.insert(
852 Bytes32(
853 pub_key
854 .as_ref()
855 .try_into()
856 .map_err(|_| TxBuilderError::MalformedKey)?,
857 ),
858 Bytes64(signature),
859 );
860
861 self.signatures = Some(new_sigs);
862
863 let mut tx = conway::Tx::decode_fragment(&self.tx_bytes.0)
865 .map_err(|_| TxBuilderError::CorruptedTxBytes)?;
866
867 let mut vkey_witnesses = tx
868 .transaction_witness_set
869 .vkeywitness
870 .as_ref()
871 .map(|x| x.clone().to_vec())
872 .unwrap_or_default();
873
874 vkey_witnesses.push(conway::VKeyWitness {
875 vkey: Vec::from(pub_key.as_ref()).into(),
876 signature: Vec::from(signature.as_ref()).into(),
877 });
878
879 tx.transaction_witness_set.vkeywitness =
880 Some(NonEmptySet::from_vec(vkey_witnesses).unwrap());
881
882 self.tx_bytes = tx.encode_fragment().unwrap().into();
883 }
884 _ => return Err(TxBuilderError::UnsupportedEra),
885 }
886
887 Ok(self)
888 }
889
890 pub fn remove_signature(mut self, pub_key: ed25519::PublicKey) -> Result<Self, TxBuilderError> {
892 match self.era {
893 BuilderEra::Conway => {
894 let mut new_sigs = self.signatures.unwrap_or_default();
895
896 let pk = Bytes32(
897 pub_key
898 .as_ref()
899 .try_into()
900 .map_err(|_| TxBuilderError::MalformedKey)?,
901 );
902
903 new_sigs.remove(&pk);
904
905 self.signatures = Some(new_sigs);
906
907 let mut tx = conway::Tx::decode_fragment(&self.tx_bytes.0)
909 .map_err(|_| TxBuilderError::CorruptedTxBytes)?;
910
911 let mut vkey_witnesses = tx
912 .transaction_witness_set
913 .vkeywitness
914 .as_ref()
915 .map(|x| x.clone().to_vec())
916 .unwrap_or_default();
917
918 vkey_witnesses.retain(|x| *x.vkey != pk.0.to_vec());
919
920 tx.transaction_witness_set.vkeywitness =
921 Some(NonEmptySet::from_vec(vkey_witnesses).unwrap());
922
923 self.tx_bytes = tx.encode_fragment().unwrap().into();
924 }
925 _ => return Err(TxBuilderError::UnsupportedEra),
926 }
927
928 Ok(self)
929 }
930}