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