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#[derive(Debug)]
67pub struct TransactionV1Builder<'a> {
68 args: TransactionArgs,
70 target: TransactionTarget,
72 scheduling: TransactionScheduling,
74 entry_point: TransactionEntryPoint,
76 chain_name: Option<String>,
78 timestamp: Timestamp,
80 ttl: TimeDiff,
82 pricing_mode: PricingMode,
84 initiator_addr: Option<InitiatorAddr>,
86 #[cfg(not(test))]
88 secret_key: Option<&'a SecretKey>,
89 #[cfg(test)]
91 secret_key: Option<SecretKey>,
92 _phantom_data: PhantomData<&'a ()>,
94}
95
96impl<'a> TransactionV1Builder<'a> {
97 pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000);
99 pub const DEFAULT_PRICING_MODE: PricingMode = PricingMode::Fixed {
101 gas_price_tolerance: 5,
102 additional_computation_factor: 0,
103 };
104 pub const DEFAULT_SCHEDULING: TransactionScheduling = TransactionScheduling::Standard;
106
107 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self {
446 self.timestamp = timestamp;
447 self
448 }
449
450 pub fn with_ttl(mut self, ttl: TimeDiff) -> Self {
454 self.ttl = ttl;
455 self
456 }
457
458 pub fn with_pricing_mode(mut self, pricing_mode: PricingMode) -> Self {
462 self.pricing_mode = pricing_mode;
463 self
464 }
465
466 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 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 pub fn with_runtime_args(mut self, args: RuntimeArgs) -> Self {
499 self.args = TransactionArgs::Named(args);
500 self
501 }
502
503 pub fn with_chunked_args(mut self, args: Bytes) -> Self {
505 self.args = TransactionArgs::Bytesrepr(args);
506 self
507 }
508
509 pub fn with_entry_point(mut self, entry_point: TransactionEntryPoint) -> Self {
511 self.entry_point = entry_point;
512 self
513 }
514
515 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}