casper_client/cli/
transaction_v1_builder.rs

1pub mod error;
2use super::arg_handling;
3use crate::{
4    cli::{FieldsContainer, FieldsContainerError},
5    types::InitiatorAddrAndSecretKey,
6};
7use alloc::collections::BTreeMap;
8use alloc::collections::BTreeSet;
9use alloc::vec::Vec;
10use casper_types::{
11    bytesrepr::{Bytes, ToBytes},
12    system::auction::{DelegatorKind, Reservation},
13    AddressableEntityHash, CLValueError, Digest, EntityVersion, EntityVersionKey, InitiatorAddr,
14    PackageHash, PricingMode, PublicKey, RuntimeArgs, SecretKey, TimeDiff, Timestamp,
15    TransactionArgs, TransactionEntryPoint, TransactionInvocationTarget, TransactionRuntimeParams,
16    TransactionScheduling, TransactionTarget, TransactionV1, TransactionV1Payload, TransferTarget,
17    URef, U512,
18};
19use core::marker::PhantomData;
20pub use error::TransactionV1BuilderError;
21
22/// A builder for constructing `TransactionV1` instances with various configuration options.
23///
24/// The `TransactionV1Builder` provides a flexible API for specifying different transaction
25/// parameters like the target, scheduling, entry point, and signing options. Once all the required
26/// fields are set, the transaction can be built by calling [`build`](Self::build).
27///
28/// # Fields
29///
30/// - `args`: Arguments passed to the transaction's runtime, initialized to
31///   [`RuntimeArgs::new`](RuntimeArgs::new).
32/// - `target`: Specifies the target of the transaction, which can be native or other custom
33///   targets. Defaults to [`TransactionTarget::Native`](TransactionTarget::Native).
34/// - `scheduling`: Determines the scheduling mechanism of the transaction, e.g., standard or
35///   immediate, and is initialized to
36///   [`TransactionScheduling::Standard`](TransactionScheduling::Standard).
37/// - `entry_point`: Defines the transaction's entry point, such as transfer or another defined
38///   action. Defaults to [`TransactionEntryPoint::Transfer`](TransactionEntryPoint::Transfer).
39/// - `chain_name`: The name of the blockchain where the transaction will be executed. Initially set
40///   to `None` and must be provided before building the transaction.
41///
42/// ## Time-Related Fields
43/// - `timestamp`: The timestamp at which the transaction is created. It is either set to the
44///   current time using [`Timestamp::now`](Timestamp::now) or [`Timestamp::zero`](Timestamp::zero)
45///   without the `std-fs-io` feature.
46/// - `ttl`: Time-to-live for the transaction, specified as a [`TimeDiff`], representing how long
47///   the transaction is valid for execution. Defaults to [`Self::DEFAULT_TTL`].
48///
49/// ## Pricing and Initiator Fields
50/// - `pricing_mode`: Specifies the pricing mode to use for transaction execution (e.g., fixed or
51///   dynamic). Defaults to [`Self::DEFAULT_PRICING_MODE`].
52/// - `initiator_addr`: The address of the initiator who creates and signs the transaction.
53///   Initially set to `None` and must be set before building.
54///
55/// ## Signing Fields
56/// - `secret_key`: The secret key used to sign the transaction. This field is conditional based on
57/// the compilation environment:
58///     - In normal mode, it holds a reference to the secret key (`Option<&'a SecretKey>`).
59///     - In testing mode or with the `std` feature enabled, it holds an owned secret key
60///  (`Option<SecretKey>`).
61///
62/// ## Phantom Data
63/// - `_phantom_data`: Ensures the correct lifetime `'a` is respected for the builder, helping with
64///   proper borrowing and memory safety.
65#[derive(Debug)]
66pub struct TransactionV1Builder<'a> {
67    /// Arguments passed to the transaction's runtime.
68    args: TransactionArgs,
69    /// The target of the transaction (e.g., native).
70    target: TransactionTarget,
71    /// Defines how the transaction is scheduled (e.g., standard, immediate).
72    scheduling: TransactionScheduling,
73    /// Specifies the entry point of the transaction (e.g., transfer).
74    entry_point: TransactionEntryPoint,
75    /// The name of the blockchain where the transaction will be executed.
76    chain_name: Option<String>,
77    /// The timestamp of the transaction.
78    timestamp: Timestamp,
79    /// The time-to-live for the transaction, representing how long it's valid for execution.
80    ttl: TimeDiff,
81    /// The pricing mode used for the transaction's execution cost.
82    pricing_mode: PricingMode,
83    /// The address of the transaction initiator.
84    initiator_addr: Option<InitiatorAddr>,
85    /// The secret key used for signing the transaction (in normal mode).
86    #[cfg(not(test))]
87    secret_key: Option<&'a SecretKey>,
88    /// The secret key used for signing the transaction (in testing).
89    #[cfg(test)]
90    secret_key: Option<SecretKey>,
91    /// Phantom data to ensure the correct lifetime for references.
92    _phantom_data: PhantomData<&'a ()>,
93}
94
95impl<'a> TransactionV1Builder<'a> {
96    /// The default time-to-live for transactions, i.e. 30 minutes.
97    pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000);
98    /// The default pricing mode for v1 transactions, ie FIXED cost.
99    pub const DEFAULT_PRICING_MODE: PricingMode = PricingMode::Fixed {
100        gas_price_tolerance: 5,
101        additional_computation_factor: 0,
102    };
103    /// The default scheduling for transactions, i.e. `Standard`.
104    pub const DEFAULT_SCHEDULING: TransactionScheduling = TransactionScheduling::Standard;
105
106    /// Creates a new `TransactionV1Builder` instance with default settings.
107    ///
108    /// # Important
109    ///
110    /// Before calling [`build`](Self::build), you must ensure that either:
111    /// - A chain name is provided by calling [`with_chain_name`](Self::with_chain_name),
112    /// - An initiator address is set by calling [`with_initiator_addr`](Self::with_initiator_addr),
113    /// - or a secret key is set by calling [`with_secret_key`](Self::with_secret_key).
114    ///
115    /// # Default Values
116    /// This function sets the following default values upon creation:
117    ///
118    /// - `chain_name`: Initialized to `None`.
119    /// - `timestamp`: Set to the current time using [`Timestamp::now`](Timestamp::now), or
120    ///   [`Timestamp::zero`](Timestamp::zero) if the `std-fs-io` feature is disabled.
121    /// - `ttl`: Defaults to [`Self::DEFAULT_TTL`].
122    /// - `pricing_mode`: Defaults to [`Self::DEFAULT_PRICING_MODE`].
123    /// - `initiator_addr`: Initialized to `None`.
124    /// - `secret_key`: Initialized to `None`.
125    ///
126    /// Additionally, the following internal fields are configured:
127    ///
128    /// - `args`: Initialized to an empty [`RuntimeArgs::new`](RuntimeArgs::new).
129    /// - `entry_point`: Set to
130    ///   [`TransactionEntryPoint::Transfer`](TransactionEntryPoint::Transfer).
131    /// - `target`: Defaults to [`TransactionTarget::Native`](TransactionTarget::Native).
132    /// - `scheduling`: Defaults to
133    ///   [`TransactionScheduling::Standard`](TransactionScheduling::Standard).
134    ///
135    /// # Testing and Additional Configuration
136    ///
137    /// # Returns
138    ///
139    /// A new `TransactionV1Builder` instance.
140    pub(crate) fn new() -> Self {
141        #[cfg(any(feature = "std-fs-io", test))]
142        let timestamp = Timestamp::now();
143        #[cfg(not(any(feature = "std-fs-io", test)))]
144        let timestamp = Timestamp::zero();
145
146        TransactionV1Builder {
147            args: TransactionArgs::Named(RuntimeArgs::new()),
148            entry_point: TransactionEntryPoint::Transfer,
149            target: TransactionTarget::Native,
150            scheduling: TransactionScheduling::Standard,
151            chain_name: None,
152            timestamp,
153            ttl: Self::DEFAULT_TTL,
154            pricing_mode: Self::DEFAULT_PRICING_MODE,
155            initiator_addr: None,
156            secret_key: None,
157            _phantom_data: PhantomData,
158        }
159    }
160
161    /// Returns a new `TransactionV1Builder` suitable for building a native transfer transaction.
162    pub fn new_transfer<A: Into<U512>, T: Into<TransferTarget>>(
163        amount: A,
164        maybe_source: Option<URef>,
165        target: T,
166        maybe_id: Option<u64>,
167    ) -> Result<Self, CLValueError> {
168        let args = arg_handling::new_transfer_args(amount, maybe_source, target, maybe_id)?;
169        let mut builder = TransactionV1Builder::new();
170        builder.args = TransactionArgs::Named(args);
171        builder.target = TransactionTarget::Native;
172        builder.entry_point = TransactionEntryPoint::Transfer;
173        builder.scheduling = Self::DEFAULT_SCHEDULING;
174        Ok(builder)
175    }
176
177    /// Returns a new `TransactionV1Builder` suitable for building a native add_bid transaction.
178    pub fn new_add_bid<A: Into<U512>>(
179        public_key: PublicKey,
180        delegation_rate: u8,
181        amount: A,
182        minimum_delegation_amount: Option<u64>,
183        maximum_delegation_amount: Option<u64>,
184        reserved_slots: Option<u32>,
185    ) -> Result<Self, CLValueError> {
186        let args = arg_handling::new_add_bid_args(
187            public_key,
188            delegation_rate,
189            amount,
190            minimum_delegation_amount,
191            maximum_delegation_amount,
192            reserved_slots,
193        )?;
194        let mut builder = TransactionV1Builder::new();
195        builder.args = TransactionArgs::Named(args);
196        builder.target = TransactionTarget::Native;
197        builder.entry_point = TransactionEntryPoint::AddBid;
198        builder.scheduling = Self::DEFAULT_SCHEDULING;
199        Ok(builder)
200    }
201
202    /// Returns a new `TransactionV1Builder` suitable for building a native withdraw_bid
203    /// transaction.
204    pub fn new_withdraw_bid<A: Into<U512>>(
205        public_key: PublicKey,
206        amount: A,
207    ) -> Result<Self, CLValueError> {
208        let args = arg_handling::new_withdraw_bid_args(public_key, amount)?;
209        let mut builder = TransactionV1Builder::new();
210        builder.args = TransactionArgs::Named(args);
211        builder.target = TransactionTarget::Native;
212        builder.entry_point = TransactionEntryPoint::WithdrawBid;
213        builder.scheduling = Self::DEFAULT_SCHEDULING;
214        Ok(builder)
215    }
216
217    /// Returns a new `TransactionV1Builder` suitable for building a native delegate transaction.
218    pub fn new_delegate<A: Into<U512>>(
219        delegator: PublicKey,
220        validator: PublicKey,
221        amount: A,
222    ) -> Result<Self, CLValueError> {
223        let args = arg_handling::new_delegate_args(delegator, validator, amount)?;
224        let mut builder = TransactionV1Builder::new();
225        builder.args = TransactionArgs::Named(args);
226        builder.target = TransactionTarget::Native;
227        builder.entry_point = TransactionEntryPoint::Delegate;
228        builder.scheduling = Self::DEFAULT_SCHEDULING;
229        Ok(builder)
230    }
231
232    /// Returns a new `TransactionV1Builder` suitable for building a native undelegate transaction.
233    pub fn new_undelegate<A: Into<U512>>(
234        delegator: PublicKey,
235        validator: PublicKey,
236        amount: A,
237    ) -> Result<Self, CLValueError> {
238        let args = arg_handling::new_undelegate_args(delegator, validator, amount)?;
239        let mut builder = TransactionV1Builder::new();
240        builder.args = TransactionArgs::Named(args);
241        builder.target = TransactionTarget::Native;
242        builder.entry_point = TransactionEntryPoint::Undelegate;
243        builder.scheduling = Self::DEFAULT_SCHEDULING;
244        Ok(builder)
245    }
246
247    /// Returns a new `TransactionV1Builder` suitable for building a native redelegate transaction.
248    pub fn new_redelegate<A: Into<U512>>(
249        delegator: PublicKey,
250        validator: PublicKey,
251        amount: A,
252        new_validator: PublicKey,
253    ) -> Result<Self, CLValueError> {
254        let args = arg_handling::new_redelegate_args(delegator, validator, amount, new_validator)?;
255        let mut builder = TransactionV1Builder::new();
256        builder.args = TransactionArgs::Named(args);
257        builder.target = TransactionTarget::Native;
258        builder.entry_point = TransactionEntryPoint::Redelegate;
259        builder.scheduling = Self::DEFAULT_SCHEDULING;
260        Ok(builder)
261    }
262
263    /// Returns a new `TransactionV1Builder` suitable for building a native activate_bid
264    /// transaction.
265    pub fn new_activate_bid(validator: PublicKey) -> Result<Self, CLValueError> {
266        let args = arg_handling::new_activate_bid_args(validator)?;
267        let mut builder = TransactionV1Builder::new();
268        builder.args = TransactionArgs::Named(args);
269        builder.target = TransactionTarget::Native;
270        builder.entry_point = TransactionEntryPoint::ActivateBid;
271        builder.scheduling = Self::DEFAULT_SCHEDULING;
272        Ok(builder)
273    }
274
275    /// Returns a new `TransactionV1Builder` suitable for building a native change_bid_public_key
276    /// transaction.
277    pub fn new_change_bid_public_key(
278        public_key: PublicKey,
279        new_public_key: PublicKey,
280    ) -> Result<Self, CLValueError> {
281        let args = arg_handling::new_change_bid_public_key_args(public_key, new_public_key)?;
282        let mut builder = TransactionV1Builder::new();
283        builder.args = TransactionArgs::Named(args);
284        builder.target = TransactionTarget::Native;
285        builder.entry_point = TransactionEntryPoint::ChangeBidPublicKey;
286        builder.scheduling = Self::DEFAULT_SCHEDULING;
287        Ok(builder)
288    }
289
290    /// Returns a new `TransactionV1Builder` suitable for building a native add_reservations
291    /// transaction.
292    pub fn new_add_reservations(reservations: Vec<Reservation>) -> Result<Self, CLValueError> {
293        let args = arg_handling::new_add_reservations_args(reservations)?;
294        let mut builder = TransactionV1Builder::new();
295        builder.args = TransactionArgs::Named(args);
296        builder.target = TransactionTarget::Native;
297        builder.entry_point = TransactionEntryPoint::AddReservations;
298        builder.scheduling = Self::DEFAULT_SCHEDULING;
299        Ok(builder)
300    }
301
302    /// Returns a new `TransactionV1Builder` suitable for building a native cancel_reservations
303    /// transaction.
304    pub fn new_cancel_reservations(
305        validator: PublicKey,
306        delegators: Vec<DelegatorKind>,
307    ) -> Result<Self, CLValueError> {
308        let args = arg_handling::new_cancel_reservations_args(validator, delegators)?;
309        let mut builder = TransactionV1Builder::new();
310        builder.args = TransactionArgs::Named(args);
311        builder.target = TransactionTarget::Native;
312        builder.entry_point = TransactionEntryPoint::CancelReservations;
313        builder.scheduling = Self::DEFAULT_SCHEDULING;
314        Ok(builder)
315    }
316
317    fn new_targeting_stored<E: Into<String>>(
318        id: TransactionInvocationTarget,
319        entry_point: E,
320        runtime: TransactionRuntimeParams,
321    ) -> Self {
322        let target = TransactionTarget::Stored { id, runtime };
323        let mut builder = TransactionV1Builder::new();
324        builder.args = TransactionArgs::Named(RuntimeArgs::new());
325        builder.target = target;
326        builder.entry_point = TransactionEntryPoint::Custom(entry_point.into());
327        builder.scheduling = Self::DEFAULT_SCHEDULING;
328        builder
329    }
330
331    /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a stored
332    /// entity.
333    pub fn new_targeting_invocable_entity<E: Into<String>>(
334        hash: AddressableEntityHash,
335        entry_point: E,
336        runtime: TransactionRuntimeParams,
337    ) -> Self {
338        let id = TransactionInvocationTarget::new_invocable_entity(hash);
339        Self::new_targeting_stored(id, entry_point, runtime)
340    }
341
342    /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a stored
343    /// entity via its alias.
344    pub fn new_targeting_invocable_entity_via_alias<A: Into<String>, E: Into<String>>(
345        alias: A,
346        entry_point: E,
347        runtime: TransactionRuntimeParams,
348    ) -> Self {
349        let id = TransactionInvocationTarget::new_invocable_entity_alias(alias.into());
350        Self::new_targeting_stored(id, entry_point, runtime)
351    }
352
353    /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a
354    /// package.
355    pub fn new_targeting_package<E: Into<String>>(
356        hash: PackageHash,
357        version: Option<EntityVersion>,
358        entry_point: E,
359        runtime: TransactionRuntimeParams,
360    ) -> Self {
361        #[allow(deprecated)]
362        let id = TransactionInvocationTarget::new_package(hash, version);
363        Self::new_targeting_stored(id, entry_point, runtime)
364    }
365
366    /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a
367    /// package.
368    pub fn new_targeting_package_with_version_key<E: Into<String>>(
369        hash: PackageHash,
370        version: Option<EntityVersionKey>,
371        entry_point: E,
372        runtime: TransactionRuntimeParams,
373    ) -> Self {
374        let id = if let Some(version) = version {
375            TransactionInvocationTarget::new_package_with_major(hash, Some(version.entity_version()), Some(version.protocol_version_major()))
376        } else {
377            TransactionInvocationTarget::new_package_with_major(hash, None, None)
378        };
379        Self::new_targeting_stored(id, entry_point, runtime)
380    }
381
382    /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a
383    /// package via its alias.
384    pub fn new_targeting_package_via_alias<A: Into<String>, E: Into<String>>(
385        alias: A,
386        version: Option<EntityVersion>,
387        entry_point: E,
388        runtime: TransactionRuntimeParams,
389    ) -> Self {
390        #[allow(deprecated)]
391        let id = TransactionInvocationTarget::new_package_alias(alias.into(), version);
392        Self::new_targeting_stored(id, entry_point, runtime)
393    }
394
395    /// Returns a new `TransactionV1Builder` suitable for building a transaction targeting a
396    /// package via its alias.
397    pub fn new_targeting_package_via_alias_with_version_key<A: Into<String>, E: Into<String>>(
398        alias: A,
399        version: Option<EntityVersionKey>,
400        entry_point: E,
401        runtime: TransactionRuntimeParams,
402    ) -> Self {
403        let id = if let Some(version) = version {
404            TransactionInvocationTarget::new_package_alias_with_major(
405                alias.into(),
406                Some(version.entity_version()),
407                Some(version.protocol_version_major()),
408            )
409        } else {
410            TransactionInvocationTarget::new_package_alias_with_major(alias.into(), None, None)
411        };
412        Self::new_targeting_stored(id, entry_point, runtime)
413    }
414
415    /// Returns a new `TransactionV1Builder` suitable for building a transaction for running session
416    /// logic, i.e. compiled Wasm.
417    pub fn new_session(
418        is_install_upgrade: bool,
419        module_bytes: Bytes,
420        runtime: TransactionRuntimeParams,
421    ) -> Self {
422        let target = TransactionTarget::Session {
423            is_install_upgrade,
424            module_bytes,
425            runtime,
426        };
427        let mut builder = TransactionV1Builder::new();
428        builder.args = TransactionArgs::Named(RuntimeArgs::new());
429        builder.target = target;
430        builder.entry_point = TransactionEntryPoint::Call;
431        builder.scheduling = Self::DEFAULT_SCHEDULING;
432        builder
433    }
434
435    /// Sets the `chain_name` in the transaction.
436    ///
437    /// Must be provided or building will fail.
438    pub fn with_chain_name<C: Into<String>>(mut self, chain_name: C) -> Self {
439        self.chain_name = Some(chain_name.into());
440        self
441    }
442
443    /// Sets the `timestamp` in the transaction.
444    ///
445    /// If not provided, the timestamp will be set to the time when the builder was constructed.
446    pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self {
447        self.timestamp = timestamp;
448        self
449    }
450
451    /// Sets the `ttl` (time-to-live) in the transaction.
452    ///
453    /// If not provided, the ttl will be set to [`Self::DEFAULT_TTL`].
454    pub fn with_ttl(mut self, ttl: TimeDiff) -> Self {
455        self.ttl = ttl;
456        self
457    }
458
459    /// Sets the `pricing_mode` in the transaction.
460    ///
461    /// If not provided, the pricing mode will be set to [`Self::DEFAULT_PRICING_MODE`].
462    pub fn with_pricing_mode(mut self, pricing_mode: PricingMode) -> Self {
463        self.pricing_mode = pricing_mode;
464        self
465    }
466
467    /// Sets the `initiator_addr` in the transaction.
468    ///
469    /// If not provided, the public key derived from the secret key used in the builder will be
470    /// used as the `InitiatorAddr::PublicKey` in the transaction.
471    pub fn with_initiator_addr<I: Into<InitiatorAddr>>(mut self, initiator_addr: I) -> Self {
472        self.initiator_addr = Some(initiator_addr.into());
473        self
474    }
475
476    /// Sets the secret key used to sign the transaction on calling [`build`](Self::build).
477    ///
478    /// If not provided, the transaction can still be built, but will be unsigned and will be
479    /// invalid until subsequently signed.
480    pub fn with_secret_key(mut self, secret_key: &'a SecretKey) -> Self {
481        #[cfg(not(test))]
482        {
483            self.secret_key = Some(secret_key);
484        }
485        #[cfg(test)]
486        {
487            self.secret_key = Some(
488                SecretKey::from_der(secret_key.to_der().expect("should der-encode"))
489                    .expect("should der-decode"),
490            );
491        }
492        self
493    }
494
495    /// Sets the runtime args in the transaction.
496    ///
497    /// NOTE: this overwrites any existing runtime args.  To append to existing args, use
498    /// [`TransactionV1Builder::with_runtime_arg`].
499    pub fn with_runtime_args(mut self, args: RuntimeArgs) -> Self {
500        self.args = TransactionArgs::Named(args);
501        self
502    }
503
504    /// Sets the runtime args in the transaction.
505    pub fn with_chunked_args(mut self, args: Bytes) -> Self {
506        self.args = TransactionArgs::Bytesrepr(args);
507        self
508    }
509
510    /// Sets the entry point for the transaction.
511    pub fn with_entry_point(mut self, entry_point: TransactionEntryPoint) -> Self {
512        self.entry_point = entry_point;
513        self
514    }
515
516    /// Returns the new transaction, or an error if non-defaulted fields were not set.
517    ///
518    /// For more info, see [the `TransactionBuilder` documentation](TransactionV1Builder).
519    pub fn build(self) -> Result<TransactionV1, TransactionV1BuilderError> {
520        self.do_build()
521    }
522
523    fn build_transaction_inner(
524        chain_name: String,
525        timestamp: Timestamp,
526        ttl: TimeDiff,
527        pricing_mode: PricingMode,
528        fields: BTreeMap<u16, Bytes>,
529        initiator_addr_and_secret_key: InitiatorAddrAndSecretKey,
530    ) -> TransactionV1 {
531        let initiator_addr = initiator_addr_and_secret_key.initiator_addr();
532        let transaction_v1_payload = TransactionV1Payload::new(
533            chain_name,
534            timestamp,
535            ttl,
536            pricing_mode,
537            initiator_addr,
538            fields,
539        );
540        let hash = Digest::hash(
541            transaction_v1_payload
542                .to_bytes()
543                .unwrap_or_else(|error| panic!("should serialize body: {}", error)),
544        );
545        let mut transaction =
546            TransactionV1::new(hash.into(), transaction_v1_payload, BTreeSet::new());
547
548        if let Some(secret_key) = initiator_addr_and_secret_key.secret_key() {
549            transaction.sign(secret_key);
550        }
551        transaction
552    }
553
554    fn do_build(self) -> Result<TransactionV1, TransactionV1BuilderError> {
555        let initiator_addr_and_secret_key = match (self.initiator_addr, &self.secret_key) {
556            (Some(initiator_addr), Some(secret_key)) => InitiatorAddrAndSecretKey::Both {
557                initiator_addr,
558                secret_key,
559            },
560            (Some(initiator_addr), None) => {
561                InitiatorAddrAndSecretKey::InitiatorAddr(initiator_addr)
562            }
563            (None, Some(secret_key)) => InitiatorAddrAndSecretKey::SecretKey(secret_key),
564            (None, None) => return Err(TransactionV1BuilderError::MissingInitiatorAddr),
565        };
566
567        let chain_name = self
568            .chain_name
569            .ok_or(TransactionV1BuilderError::MissingChainName)?;
570
571        let container =
572            FieldsContainer::new(self.args, self.target, self.entry_point, self.scheduling)
573                .to_map()
574                .map_err(|err| match err {
575                    FieldsContainerError::CouldNotSerializeField { field_index } => {
576                        TransactionV1BuilderError::CouldNotSerializeField { field_index }
577                    }
578                })?;
579
580        let transaction = Self::build_transaction_inner(
581            chain_name,
582            self.timestamp,
583            self.ttl,
584            self.pricing_mode,
585            container,
586            initiator_addr_and_secret_key,
587        );
588
589        Ok(transaction)
590    }
591}