1use std::fmt::{self, Display, Formatter, Write};
6
7use enum_dispatch::enum_dispatch;
8use fastcrypto::encoding::{Base64, Encoding};
9use futures::{Stream, StreamExt, stream::FuturesOrdered};
10use iota_json::{IotaJsonValue, primitive_type};
11use iota_metrics::monitored_scope;
12use iota_package_resolver::{CleverError, ErrorConstants, PackageStore, Resolver};
13use iota_types::{
14 IOTA_FRAMEWORK_ADDRESS,
15 authenticator_state::ActiveJwk,
16 base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber, TransactionDigest},
17 crypto::IotaSignature,
18 digests::{ConsensusCommitDigest, ObjectDigest, TransactionEventsDigest},
19 effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents},
20 error::{ExecutionError, IotaError, IotaResult},
21 event::EventID,
22 execution_status::{ExecutionFailureStatus, ExecutionStatus},
23 gas::GasCostSummary,
24 iota_serde::{
25 BigInt, IotaTypeTag as AsIotaTypeTag, Readable, SequenceNumber as AsSequenceNumber,
26 },
27 layout_resolver::{LayoutResolver, get_layout_from_struct_tag},
28 messages_checkpoint::CheckpointSequenceNumber,
29 messages_consensus::ConsensusDeterminedVersionAssignments,
30 object::{Owner, bounded_visitor::BoundedVisitor},
31 parse_iota_type_tag,
32 quorum_driver_types::ExecuteTransactionRequestType,
33 signature::GenericSignature,
34 storage::{DeleteKind, WriteKind},
35 transaction::{
36 Argument, CallArg, ChangeEpoch, ChangeEpochV2, ChangeEpochV3, ChangeEpochV4, Command,
37 EndOfEpochTransactionKind, GenesisObject, InputObjectKind, ObjectArg, ProgrammableMoveCall,
38 ProgrammableTransaction, SenderSignedData, TransactionData, TransactionDataAPI,
39 TransactionKind,
40 },
41};
42use move_binary_format::CompiledModule;
43use move_bytecode_utils::module_cache::GetModule;
44use move_core_types::{
45 annotated_value::MoveTypeLayout,
46 identifier::{IdentStr, Identifier},
47 language_storage::{ModuleId, StructTag, TypeTag},
48};
49use schemars::JsonSchema;
50use serde::{Deserialize, Serialize};
51use serde_with::serde_as;
52use strum::{Display, EnumString};
53use tabled::{
54 builder::Builder as TableBuilder,
55 settings::{Panel as TablePanel, Style as TableStyle, style::HorizontalLine},
56};
57
58use crate::{
59 Filter, IotaEvent, IotaMoveValue, IotaObjectRef, Page, balance_changes::BalanceChange,
60 iota_transaction::GenericSignature::Signature, object_changes::ObjectChange,
61};
62
63pub type IotaEpochId = BigInt<u64>;
65
66#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
67#[serde(
68 rename_all = "camelCase",
69 rename = "TransactionBlockResponseQuery",
70 default
71)]
72pub struct IotaTransactionBlockResponseQuery {
73 pub filter: Option<TransactionFilter>,
75 pub options: Option<IotaTransactionBlockResponseOptions>,
78}
79
80impl IotaTransactionBlockResponseQuery {
81 pub fn new(
82 filter: Option<TransactionFilter>,
83 options: Option<IotaTransactionBlockResponseOptions>,
84 ) -> Self {
85 Self { filter, options }
86 }
87
88 pub fn new_with_filter(filter: TransactionFilter) -> Self {
89 Self {
90 filter: Some(filter),
91 options: None,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
97#[serde(
98 rename_all = "camelCase",
99 rename = "TransactionBlockResponseQuery",
100 default
101)]
102pub struct IotaTransactionBlockResponseQueryV2 {
103 pub filter: Option<TransactionFilterV2>,
105 pub options: Option<IotaTransactionBlockResponseOptions>,
108}
109
110impl IotaTransactionBlockResponseQueryV2 {
111 pub fn new(
112 filter: Option<TransactionFilterV2>,
113 options: Option<IotaTransactionBlockResponseOptions>,
114 ) -> Self {
115 Self { filter, options }
116 }
117
118 pub fn new_with_filter(filter: TransactionFilterV2) -> Self {
119 Self {
120 filter: Some(filter),
121 options: None,
122 }
123 }
124}
125
126pub type TransactionBlocksPage = Page<IotaTransactionBlockResponse, TransactionDigest>;
127
128#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Default)]
129#[serde(
130 rename_all = "camelCase",
131 rename = "TransactionBlockResponseOptions",
132 default
133)]
134pub struct IotaTransactionBlockResponseOptions {
135 pub show_input: bool,
137 pub show_raw_input: bool,
139 pub show_effects: bool,
141 pub show_events: bool,
143 pub show_object_changes: bool,
145 pub show_balance_changes: bool,
147 pub show_raw_effects: bool,
149}
150
151impl IotaTransactionBlockResponseOptions {
152 pub fn new() -> Self {
153 Self::default()
154 }
155
156 pub fn full_content() -> Self {
157 Self {
158 show_effects: true,
159 show_input: true,
160 show_raw_input: true,
161 show_events: true,
162 show_object_changes: true,
163 show_balance_changes: true,
164 show_raw_effects: false,
167 }
168 }
169
170 pub fn with_input(mut self) -> Self {
171 self.show_input = true;
172 self
173 }
174
175 pub fn with_raw_input(mut self) -> Self {
176 self.show_raw_input = true;
177 self
178 }
179
180 pub fn with_effects(mut self) -> Self {
181 self.show_effects = true;
182 self
183 }
184
185 pub fn with_events(mut self) -> Self {
186 self.show_events = true;
187 self
188 }
189
190 pub fn with_balance_changes(mut self) -> Self {
191 self.show_balance_changes = true;
192 self
193 }
194
195 pub fn with_object_changes(mut self) -> Self {
196 self.show_object_changes = true;
197 self
198 }
199
200 pub fn with_raw_effects(mut self) -> Self {
201 self.show_raw_effects = true;
202 self
203 }
204
205 pub fn default_execution_request_type(&self) -> ExecuteTransactionRequestType {
208 if self.require_effects() {
211 ExecuteTransactionRequestType::WaitForLocalExecution
212 } else {
213 ExecuteTransactionRequestType::WaitForEffectsCert
214 }
215 }
216
217 pub fn require_input(&self) -> bool {
218 self.show_input || self.show_raw_input || self.show_object_changes
219 }
220
221 pub fn require_effects(&self) -> bool {
222 self.show_effects
223 || self.show_events
224 || self.show_balance_changes
225 || self.show_object_changes
226 || self.show_raw_effects
227 }
228
229 pub fn only_digest(&self) -> bool {
230 self == &Self::default()
231 }
232}
233
234#[serde_as]
235#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
236#[serde(rename_all = "camelCase", rename = "TransactionBlockResponse")]
237pub struct IotaTransactionBlockResponse {
238 pub digest: TransactionDigest,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub transaction: Option<IotaTransactionBlock>,
242 #[serde_as(as = "Base64")]
245 #[schemars(with = "Base64")]
246 #[serde(skip_serializing_if = "Vec::is_empty", default)]
247 pub raw_transaction: Vec<u8>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub effects: Option<IotaTransactionBlockEffects>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub events: Option<IotaTransactionBlockEvents>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub object_changes: Option<Vec<ObjectChange>>,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub balance_changes: Option<Vec<BalanceChange>>,
256 #[serde(default, skip_serializing_if = "Option::is_none")]
257 #[schemars(with = "Option<BigInt<u64>>")]
258 #[serde_as(as = "Option<BigInt<u64>>")]
259 pub timestamp_ms: Option<u64>,
260 #[serde(default, skip_serializing_if = "Option::is_none")]
261 pub confirmed_local_execution: Option<bool>,
262 #[schemars(with = "Option<BigInt<u64>>")]
266 #[serde_as(as = "Option<BigInt<u64>>")]
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub checkpoint: Option<CheckpointSequenceNumber>,
269 #[serde(skip_serializing_if = "Vec::is_empty", default)]
270 pub errors: Vec<String>,
271 #[serde(skip_serializing_if = "Vec::is_empty", default)]
272 pub raw_effects: Vec<u8>,
273}
274
275impl IotaTransactionBlockResponse {
276 pub fn new(digest: TransactionDigest) -> Self {
277 Self {
278 digest,
279 ..Default::default()
280 }
281 }
282
283 pub fn status_ok(&self) -> Option<bool> {
284 self.effects.as_ref().map(|e| e.status().is_ok())
285 }
286
287 pub fn mutated_objects(&self) -> impl Iterator<Item = ObjectRef> + '_ {
289 self.object_changes.iter().flat_map(|obj_changes| {
290 obj_changes
291 .iter()
292 .filter(|change| matches!(change, ObjectChange::Mutated { .. }))
293 .map(|change| change.object_ref())
294 })
295 }
296}
297
298impl PartialEq for IotaTransactionBlockResponse {
300 fn eq(&self, other: &Self) -> bool {
301 self.transaction == other.transaction
302 && self.effects == other.effects
303 && self.timestamp_ms == other.timestamp_ms
304 && self.confirmed_local_execution == other.confirmed_local_execution
305 && self.checkpoint == other.checkpoint
306 }
307}
308
309impl Display for IotaTransactionBlockResponse {
310 fn fmt(&self, writer: &mut Formatter<'_>) -> fmt::Result {
311 writeln!(writer, "Transaction Digest: {}", &self.digest)?;
312
313 if let Some(t) = &self.transaction {
314 writeln!(writer, "{t}")?;
315 }
316
317 if let Some(e) = &self.effects {
318 writeln!(writer, "{e}")?;
319 }
320
321 if let Some(e) = &self.events {
322 writeln!(writer, "{e}")?;
323 }
324
325 if let Some(object_changes) = &self.object_changes {
326 let mut builder = TableBuilder::default();
327 let (
328 mut created,
329 mut deleted,
330 mut mutated,
331 mut published,
332 mut transferred,
333 mut wrapped,
334 mut unwrapped,
335 ) = (vec![], vec![], vec![], vec![], vec![], vec![], vec![]);
336
337 for obj in object_changes {
338 match obj {
339 ObjectChange::Created { .. } => created.push(obj),
340 ObjectChange::Deleted { .. } => deleted.push(obj),
341 ObjectChange::Mutated { .. } => mutated.push(obj),
342 ObjectChange::Published { .. } => published.push(obj),
343 ObjectChange::Transferred { .. } => transferred.push(obj),
344 ObjectChange::Wrapped { .. } => wrapped.push(obj),
345 ObjectChange::Unwrapped { .. } => unwrapped.push(obj),
346 };
347 }
348
349 write_obj_changes(created, "Created", &mut builder)?;
350 write_obj_changes(deleted, "Deleted", &mut builder)?;
351 write_obj_changes(mutated, "Mutated", &mut builder)?;
352 write_obj_changes(published, "Published", &mut builder)?;
353 write_obj_changes(transferred, "Transferred", &mut builder)?;
354 write_obj_changes(wrapped, "Wrapped", &mut builder)?;
355 write_obj_changes(unwrapped, "Unwrapped", &mut builder)?;
356
357 let mut table = builder.build();
358 table.with(TablePanel::header("Object Changes"));
359 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
360 1,
361 TableStyle::modern().get_horizontal(),
362 )]));
363 writeln!(writer, "{table}")?;
364 }
365
366 if let Some(balance_changes) = &self.balance_changes {
367 if !balance_changes.is_empty() {
371 let mut builder = TableBuilder::default();
372 for balance in balance_changes {
373 builder.push_record(vec![format!("{balance}")]);
374 }
375 let mut table = builder.build();
376 table.with(TablePanel::header("Balance Changes"));
377 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
378 1,
379 TableStyle::modern().get_horizontal(),
380 )]));
381 writeln!(writer, "{table}")?;
382 } else {
383 writeln!(writer, "╭────────────────────╮")?;
384 writeln!(writer, "│ No balance changes │")?;
385 writeln!(writer, "╰────────────────────╯")?;
386 }
387 }
388 Ok(())
389 }
390}
391
392fn write_obj_changes<T: Display>(
393 values: Vec<T>,
394 output_string: &str,
395 builder: &mut TableBuilder,
396) -> std::fmt::Result {
397 if !values.is_empty() {
398 builder.push_record(vec![format!("{output_string} Objects: ")]);
399 for obj in values {
400 builder.push_record(vec![format!("{obj}")]);
401 }
402 }
403 Ok(())
404}
405
406pub fn get_new_package_obj_from_response(
407 response: &IotaTransactionBlockResponse,
408) -> Option<ObjectRef> {
409 response.object_changes.as_ref().and_then(|changes| {
410 changes
411 .iter()
412 .find(|change| matches!(change, ObjectChange::Published { .. }))
413 .map(|change| change.object_ref())
414 })
415}
416
417pub fn get_new_package_upgrade_cap_from_response(
418 response: &IotaTransactionBlockResponse,
419) -> Option<ObjectRef> {
420 response.object_changes.as_ref().and_then(|changes| {
421 changes
422 .iter()
423 .find(|change| {
424 matches!(change, ObjectChange::Created {
425 owner: Owner::AddressOwner(_),
426 object_type: StructTag {
427 address: IOTA_FRAMEWORK_ADDRESS,
428 module,
429 name,
430 ..
431 },
432 ..
433 } if module.as_str() == "package" && name.as_str() == "UpgradeCap")
434 })
435 .map(|change| change.object_ref())
436 })
437}
438
439#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
440#[serde(rename = "TransactionBlockKind", tag = "kind")]
441pub enum IotaTransactionBlockKind {
442 Genesis(IotaGenesisTransaction),
445 ConsensusCommitPrologueV1(IotaConsensusCommitPrologueV1),
448 ProgrammableTransaction(IotaProgrammableTransactionBlock),
451 AuthenticatorStateUpdateV1(IotaAuthenticatorStateUpdateV1),
453 RandomnessStateUpdate(IotaRandomnessStateUpdate),
455 EndOfEpochTransaction(IotaEndOfEpochTransaction),
457 }
459
460impl Display for IotaTransactionBlockKind {
461 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
462 let mut writer = String::new();
463 match &self {
464 Self::Genesis(_) => {
465 writeln!(writer, "Transaction Kind: Genesis Transaction")?;
466 }
467 Self::ConsensusCommitPrologueV1(p) => {
468 writeln!(writer, "Transaction Kind: Consensus Commit Prologue V1")?;
469 writeln!(
470 writer,
471 "Epoch: {}, Round: {}, SubDagIndex: {:?}, Timestamp: {}, ConsensusCommitDigest: {}",
472 p.epoch,
473 p.round,
474 p.sub_dag_index,
475 p.commit_timestamp_ms,
476 p.consensus_commit_digest
477 )?;
478 }
479 Self::ProgrammableTransaction(p) => {
480 write!(writer, "Transaction Kind: Programmable")?;
481 write!(writer, "{}", crate::displays::Pretty(p))?;
482 }
483 Self::AuthenticatorStateUpdateV1(_) => {
484 writeln!(writer, "Transaction Kind: Authenticator State Update")?;
485 }
486 Self::RandomnessStateUpdate(_) => {
487 writeln!(writer, "Transaction Kind: Randomness State Update")?;
488 }
489 Self::EndOfEpochTransaction(_) => {
490 writeln!(writer, "Transaction Kind: End of Epoch Transaction")?;
491 }
492 }
493 write!(f, "{writer}")
494 }
495}
496
497impl IotaTransactionBlockKind {
498 fn try_from_inner(
499 tx: TransactionKind,
500 tx_digest: TransactionDigest,
501 ) -> Result<Self, anyhow::Error> {
502 Ok(match tx {
503 TransactionKind::Genesis(g) => Self::Genesis(IotaGenesisTransaction {
504 objects: g.objects.iter().map(GenesisObject::id).collect(),
505 events: g
506 .events
507 .into_iter()
508 .enumerate()
509 .map(|(seq, _event)| EventID::from((tx_digest, seq as u64)))
510 .collect(),
511 }),
512 TransactionKind::ConsensusCommitPrologueV1(p) => {
513 Self::ConsensusCommitPrologueV1(IotaConsensusCommitPrologueV1 {
514 epoch: p.epoch,
515 round: p.round,
516 sub_dag_index: p.sub_dag_index,
517 commit_timestamp_ms: p.commit_timestamp_ms,
518 consensus_commit_digest: p.consensus_commit_digest,
519 consensus_determined_version_assignments: p
520 .consensus_determined_version_assignments,
521 })
522 }
523 TransactionKind::ProgrammableTransaction(_) => {
524 unreachable!()
526 }
527 TransactionKind::AuthenticatorStateUpdateV1(update) => {
528 Self::AuthenticatorStateUpdateV1(IotaAuthenticatorStateUpdateV1 {
529 epoch: update.epoch,
530 round: update.round,
531 new_active_jwks: update
532 .new_active_jwks
533 .into_iter()
534 .map(IotaActiveJwk::from)
535 .collect(),
536 })
537 }
538 TransactionKind::RandomnessStateUpdate(update) => {
539 Self::RandomnessStateUpdate(IotaRandomnessStateUpdate {
540 epoch: update.epoch,
541 randomness_round: update.randomness_round.0,
542 random_bytes: update.random_bytes,
543 })
544 }
545 TransactionKind::EndOfEpochTransaction(end_of_epoch_tx) => {
546 Self::EndOfEpochTransaction(IotaEndOfEpochTransaction {
547 transactions: end_of_epoch_tx
548 .into_iter()
549 .map(|tx| match tx {
550 EndOfEpochTransactionKind::ChangeEpoch(e) => {
551 IotaEndOfEpochTransactionKind::ChangeEpoch(e.into())
552 }
553 EndOfEpochTransactionKind::ChangeEpochV2(e) => {
554 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
555 }
556 EndOfEpochTransactionKind::ChangeEpochV3(e) => {
557 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
558 }
559 EndOfEpochTransactionKind::ChangeEpochV4(e) => {
560 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
561 }
562 EndOfEpochTransactionKind::AuthenticatorStateCreate => {
563 IotaEndOfEpochTransactionKind::AuthenticatorStateCreate
564 }
565 EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
566 IotaEndOfEpochTransactionKind::AuthenticatorStateExpire(
567 IotaAuthenticatorStateExpire {
568 min_epoch: expire.min_epoch,
569 },
570 )
571 }
572 })
573 .collect(),
574 })
575 }
576 })
577 }
578
579 fn try_from_with_module_cache(
580 tx: TransactionKind,
581 module_cache: &impl GetModule,
582 tx_digest: TransactionDigest,
583 ) -> Result<Self, anyhow::Error> {
584 match tx {
585 TransactionKind::ProgrammableTransaction(p) => Ok(Self::ProgrammableTransaction(
586 IotaProgrammableTransactionBlock::try_from_with_module_cache(p, module_cache)?,
587 )),
588 tx => Self::try_from_inner(tx, tx_digest),
589 }
590 }
591
592 async fn try_from_with_package_resolver(
593 tx: TransactionKind,
594 package_resolver: &Resolver<impl PackageStore>,
595 tx_digest: TransactionDigest,
596 ) -> Result<Self, anyhow::Error> {
597 match tx {
598 TransactionKind::ProgrammableTransaction(p) => Ok(Self::ProgrammableTransaction(
599 IotaProgrammableTransactionBlock::try_from_with_package_resolver(
600 p,
601 package_resolver,
602 )
603 .await?,
604 )),
605 tx => Self::try_from_inner(tx, tx_digest),
606 }
607 }
608
609 pub fn transaction_count(&self) -> usize {
610 match self {
611 Self::ProgrammableTransaction(p) => p.commands.len(),
612 _ => 1,
613 }
614 }
615
616 pub fn name(&self) -> &'static str {
617 match self {
618 Self::Genesis(_) => "Genesis",
619 Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
620 Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
621 Self::AuthenticatorStateUpdateV1(_) => "AuthenticatorStateUpdateV1",
622 Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
623 Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
624 }
625 }
626}
627
628#[serde_as]
629#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
630pub struct IotaChangeEpoch {
631 #[schemars(with = "BigInt<u64>")]
632 #[serde_as(as = "BigInt<u64>")]
633 pub epoch: EpochId,
634 #[schemars(with = "BigInt<u64>")]
635 #[serde_as(as = "BigInt<u64>")]
636 pub storage_charge: u64,
637 #[schemars(with = "BigInt<u64>")]
638 #[serde_as(as = "BigInt<u64>")]
639 pub computation_charge: u64,
640 #[schemars(with = "BigInt<u64>")]
641 #[serde_as(as = "BigInt<u64>")]
642 pub storage_rebate: u64,
643 #[schemars(with = "BigInt<u64>")]
644 #[serde_as(as = "BigInt<u64>")]
645 pub epoch_start_timestamp_ms: u64,
646}
647
648impl From<ChangeEpoch> for IotaChangeEpoch {
649 fn from(e: ChangeEpoch) -> Self {
650 Self {
651 epoch: e.epoch,
652 storage_charge: e.storage_charge,
653 computation_charge: e.computation_charge,
654 storage_rebate: e.storage_rebate,
655 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
656 }
657 }
658}
659
660#[serde_as]
661#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
662pub struct IotaChangeEpochV2 {
663 #[schemars(with = "BigInt<u64>")]
664 #[serde_as(as = "BigInt<u64>")]
665 pub epoch: EpochId,
666 #[schemars(with = "BigInt<u64>")]
667 #[serde_as(as = "BigInt<u64>")]
668 pub storage_charge: u64,
669 #[schemars(with = "BigInt<u64>")]
670 #[serde_as(as = "BigInt<u64>")]
671 pub computation_charge: u64,
672 #[schemars(with = "BigInt<u64>")]
673 #[serde_as(as = "BigInt<u64>")]
674 pub computation_charge_burned: u64,
675 #[schemars(with = "BigInt<u64>")]
676 #[serde_as(as = "BigInt<u64>")]
677 pub storage_rebate: u64,
678 #[schemars(with = "BigInt<u64>")]
679 #[serde_as(as = "BigInt<u64>")]
680 pub epoch_start_timestamp_ms: u64,
681 #[schemars(with = "Option<Vec<BigInt<u64>>>")]
682 #[serde_as(as = "Option<Vec<BigInt<u64>>>")]
683 #[serde(skip_serializing_if = "Option::is_none", default)]
684 pub eligible_active_validators: Option<Vec<u64>>,
685 #[schemars(with = "Option<Vec<BigInt<u64>>>")]
686 #[serde_as(as = "Option<Vec<BigInt<u64>>>")]
687 #[serde(skip_serializing_if = "Option::is_none", default)]
688 pub scores: Option<Vec<u64>>,
689}
690
691impl From<ChangeEpochV2> for IotaChangeEpochV2 {
692 fn from(e: ChangeEpochV2) -> Self {
693 Self {
694 epoch: e.epoch,
695 storage_charge: e.storage_charge,
696 computation_charge: e.computation_charge,
697 computation_charge_burned: e.computation_charge_burned,
698 storage_rebate: e.storage_rebate,
699 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
700 eligible_active_validators: None,
701 scores: None,
702 }
703 }
704}
705
706impl From<ChangeEpochV3> for IotaChangeEpochV2 {
707 fn from(e: ChangeEpochV3) -> Self {
708 Self {
709 epoch: e.epoch,
710 storage_charge: e.storage_charge,
711 computation_charge: e.computation_charge,
712 computation_charge_burned: e.computation_charge_burned,
713 storage_rebate: e.storage_rebate,
714 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
715 eligible_active_validators: Some(e.eligible_active_validators),
716 scores: None,
717 }
718 }
719}
720
721impl From<ChangeEpochV4> for IotaChangeEpochV2 {
722 fn from(e: ChangeEpochV4) -> Self {
723 Self {
724 epoch: e.epoch,
725 storage_charge: e.storage_charge,
726 computation_charge: e.computation_charge,
727 computation_charge_burned: e.computation_charge_burned,
728 storage_rebate: e.storage_rebate,
729 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
730 eligible_active_validators: Some(e.eligible_active_validators),
731 scores: Some(e.scores),
732 }
733 }
734}
735
736#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
737#[enum_dispatch(IotaTransactionBlockEffectsAPI)]
738#[serde(
739 rename = "TransactionBlockEffects",
740 rename_all = "camelCase",
741 tag = "messageVersion"
742)]
743pub enum IotaTransactionBlockEffects {
744 V1(IotaTransactionBlockEffectsV1),
745}
746
747#[enum_dispatch]
748pub trait IotaTransactionBlockEffectsAPI {
749 fn status(&self) -> &IotaExecutionStatus;
750 fn into_status(self) -> IotaExecutionStatus;
751 fn shared_objects(&self) -> &[IotaObjectRef];
752 fn created(&self) -> &[OwnedObjectRef];
753 fn mutated(&self) -> &[OwnedObjectRef];
754 fn unwrapped(&self) -> &[OwnedObjectRef];
755 fn deleted(&self) -> &[IotaObjectRef];
756 fn unwrapped_then_deleted(&self) -> &[IotaObjectRef];
757 fn wrapped(&self) -> &[IotaObjectRef];
758 fn gas_object(&self) -> &OwnedObjectRef;
759 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
760 fn dependencies(&self) -> &[TransactionDigest];
761 fn executed_epoch(&self) -> EpochId;
762 fn transaction_digest(&self) -> &TransactionDigest;
763 fn gas_cost_summary(&self) -> &GasCostSummary;
764
765 fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef>;
767 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>;
768 fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>;
769 fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)>;
770}
771
772#[serde_as]
773#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
774#[serde(
775 rename = "TransactionBlockEffectsModifiedAtVersions",
776 rename_all = "camelCase"
777)]
778pub struct IotaTransactionBlockEffectsModifiedAtVersions {
779 object_id: ObjectID,
780 #[schemars(with = "AsSequenceNumber")]
781 #[serde_as(as = "AsSequenceNumber")]
782 sequence_number: SequenceNumber,
783}
784
785#[serde_as]
787#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
788#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")]
789pub struct IotaTransactionBlockEffectsV1 {
790 pub status: IotaExecutionStatus,
792 #[schemars(with = "BigInt<u64>")]
794 #[serde_as(as = "BigInt<u64>")]
795 pub executed_epoch: EpochId,
796 pub gas_used: GasCostSummary,
797 #[serde(default, skip_serializing_if = "Vec::is_empty")]
800 pub modified_at_versions: Vec<IotaTransactionBlockEffectsModifiedAtVersions>,
801 #[serde(default, skip_serializing_if = "Vec::is_empty")]
804 pub shared_objects: Vec<IotaObjectRef>,
805 pub transaction_digest: TransactionDigest,
807 #[serde(default, skip_serializing_if = "Vec::is_empty")]
809 pub created: Vec<OwnedObjectRef>,
810 #[serde(default, skip_serializing_if = "Vec::is_empty")]
812 pub mutated: Vec<OwnedObjectRef>,
813 #[serde(default, skip_serializing_if = "Vec::is_empty")]
817 pub unwrapped: Vec<OwnedObjectRef>,
818 #[serde(default, skip_serializing_if = "Vec::is_empty")]
820 pub deleted: Vec<IotaObjectRef>,
821 #[serde(default, skip_serializing_if = "Vec::is_empty")]
824 pub unwrapped_then_deleted: Vec<IotaObjectRef>,
825 #[serde(default, skip_serializing_if = "Vec::is_empty")]
827 pub wrapped: Vec<IotaObjectRef>,
828 pub gas_object: OwnedObjectRef,
831 #[serde(skip_serializing_if = "Option::is_none")]
834 pub events_digest: Option<TransactionEventsDigest>,
835 #[serde(default, skip_serializing_if = "Vec::is_empty")]
837 pub dependencies: Vec<TransactionDigest>,
838}
839
840impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 {
841 fn status(&self) -> &IotaExecutionStatus {
842 &self.status
843 }
844 fn into_status(self) -> IotaExecutionStatus {
845 self.status
846 }
847 fn shared_objects(&self) -> &[IotaObjectRef] {
848 &self.shared_objects
849 }
850 fn created(&self) -> &[OwnedObjectRef] {
851 &self.created
852 }
853 fn mutated(&self) -> &[OwnedObjectRef] {
854 &self.mutated
855 }
856 fn unwrapped(&self) -> &[OwnedObjectRef] {
857 &self.unwrapped
858 }
859 fn deleted(&self) -> &[IotaObjectRef] {
860 &self.deleted
861 }
862 fn unwrapped_then_deleted(&self) -> &[IotaObjectRef] {
863 &self.unwrapped_then_deleted
864 }
865 fn wrapped(&self) -> &[IotaObjectRef] {
866 &self.wrapped
867 }
868 fn gas_object(&self) -> &OwnedObjectRef {
869 &self.gas_object
870 }
871 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
872 self.events_digest.as_ref()
873 }
874 fn dependencies(&self) -> &[TransactionDigest] {
875 &self.dependencies
876 }
877
878 fn executed_epoch(&self) -> EpochId {
879 self.executed_epoch
880 }
881
882 fn transaction_digest(&self) -> &TransactionDigest {
883 &self.transaction_digest
884 }
885
886 fn gas_cost_summary(&self) -> &GasCostSummary {
887 &self.gas_used
888 }
889
890 fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef> {
891 self.mutated
892 .iter()
893 .filter(|o| *o != &self.gas_object)
894 .cloned()
895 .collect()
896 }
897
898 fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> {
899 self.modified_at_versions
900 .iter()
901 .map(|v| (v.object_id, v.sequence_number))
902 .collect::<Vec<_>>()
903 }
904
905 fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> {
906 self.mutated
907 .iter()
908 .map(|owner_ref| (owner_ref, WriteKind::Mutate))
909 .chain(
910 self.created
911 .iter()
912 .map(|owner_ref| (owner_ref, WriteKind::Create)),
913 )
914 .chain(
915 self.unwrapped
916 .iter()
917 .map(|owner_ref| (owner_ref, WriteKind::Unwrap)),
918 )
919 .collect()
920 }
921
922 fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)> {
923 self.deleted
924 .iter()
925 .map(|r| (r, DeleteKind::Normal))
926 .chain(
927 self.unwrapped_then_deleted
928 .iter()
929 .map(|r| (r, DeleteKind::UnwrapThenDelete)),
930 )
931 .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap)))
932 .collect()
933 }
934}
935
936impl IotaTransactionBlockEffects {
937 pub fn new_for_testing(
938 transaction_digest: TransactionDigest,
939 status: IotaExecutionStatus,
940 ) -> Self {
941 Self::V1(IotaTransactionBlockEffectsV1 {
942 transaction_digest,
943 status,
944 gas_object: OwnedObjectRef {
945 owner: Owner::AddressOwner(IotaAddress::random_for_testing_only()),
946 reference: iota_types::base_types::random_object_ref().into(),
947 },
948 executed_epoch: 0,
949 modified_at_versions: vec![],
950 gas_used: GasCostSummary::default(),
951 shared_objects: vec![],
952 created: vec![],
953 mutated: vec![],
954 unwrapped: vec![],
955 deleted: vec![],
956 unwrapped_then_deleted: vec![],
957 wrapped: vec![],
958 events_digest: None,
959 dependencies: vec![],
960 })
961 }
962
963 pub async fn from_native_with_clever_error<S: PackageStore>(
969 native: TransactionEffects,
970 resolver: &Resolver<S>,
971 ) -> Self {
972 let clever_status =
973 IotaExecutionStatus::from_native_with_clever_error(native.status().clone(), resolver)
974 .await;
975 match native {
976 TransactionEffects::V1(inner) => {
977 let mut inner = IotaTransactionBlockEffectsV1::from(inner);
978 inner.status = clever_status;
979 inner.into()
980 }
981 }
982 }
983}
984
985impl TryFrom<TransactionEffects> for IotaTransactionBlockEffects {
986 type Error = IotaError;
987
988 fn try_from(native: TransactionEffects) -> Result<Self, Self::Error> {
989 Ok(IotaTransactionBlockEffects::V1(native.into()))
990 }
991}
992
993impl<T: TransactionEffectsAPI> From<T> for IotaTransactionBlockEffectsV1 {
994 fn from(native: T) -> Self {
995 Self {
996 status: native.status().clone().into(),
997 executed_epoch: native.executed_epoch(),
998 modified_at_versions: native
999 .modified_at_versions()
1000 .into_iter()
1001 .map(
1002 |(object_id, sequence_number)| IotaTransactionBlockEffectsModifiedAtVersions {
1003 object_id,
1004 sequence_number,
1005 },
1006 )
1007 .collect(),
1008 gas_used: native.gas_cost_summary().clone(),
1009 shared_objects: to_iota_object_ref(
1010 native
1011 .input_shared_objects()
1012 .into_iter()
1013 .map(|kind| kind.object_ref())
1014 .collect(),
1015 ),
1016 transaction_digest: *native.transaction_digest(),
1017 created: to_owned_ref(native.created()),
1018 mutated: to_owned_ref(native.mutated().to_vec()),
1019 unwrapped: to_owned_ref(native.unwrapped().to_vec()),
1020 deleted: to_iota_object_ref(native.deleted().to_vec()),
1021 unwrapped_then_deleted: to_iota_object_ref(native.unwrapped_then_deleted().to_vec()),
1022 wrapped: to_iota_object_ref(native.wrapped().to_vec()),
1023 gas_object: OwnedObjectRef {
1024 owner: native.gas_object().1,
1025 reference: native.gas_object().0.into(),
1026 },
1027 events_digest: native.events_digest().copied(),
1028 dependencies: native.dependencies().to_vec(),
1029 }
1030 }
1031}
1032
1033fn owned_objref_string(obj: &OwnedObjectRef) -> String {
1034 format!(
1035 " ┌──\n │ ID: {} \n │ Owner: {} \n │ Version: {} \n │ Digest: {}\n └──",
1036 obj.reference.object_id,
1037 obj.owner,
1038 u64::from(obj.reference.version),
1039 obj.reference.digest
1040 )
1041}
1042
1043fn objref_string(obj: &IotaObjectRef) -> String {
1044 format!(
1045 " ┌──\n │ ID: {} \n │ Version: {} \n │ Digest: {}\n └──",
1046 obj.object_id,
1047 u64::from(obj.version),
1048 obj.digest
1049 )
1050}
1051
1052impl Display for IotaTransactionBlockEffects {
1053 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1054 let mut builder = TableBuilder::default();
1055
1056 builder.push_record(vec![format!("Digest: {}", self.transaction_digest())]);
1057 builder.push_record(vec![format!("Status: {:?}", self.status())]);
1058 builder.push_record(vec![format!("Executed Epoch: {}", self.executed_epoch())]);
1059
1060 if !self.created().is_empty() {
1061 builder.push_record(vec![format!("\nCreated Objects: ")]);
1062
1063 for oref in self.created() {
1064 builder.push_record(vec![owned_objref_string(oref)]);
1065 }
1066 }
1067
1068 if !self.mutated().is_empty() {
1069 builder.push_record(vec![format!("Mutated Objects: ")]);
1070 for oref in self.mutated() {
1071 builder.push_record(vec![owned_objref_string(oref)]);
1072 }
1073 }
1074
1075 if !self.shared_objects().is_empty() {
1076 builder.push_record(vec![format!("Shared Objects: ")]);
1077 for oref in self.shared_objects() {
1078 builder.push_record(vec![objref_string(oref)]);
1079 }
1080 }
1081
1082 if !self.deleted().is_empty() {
1083 builder.push_record(vec![format!("Deleted Objects: ")]);
1084
1085 for oref in self.deleted() {
1086 builder.push_record(vec![objref_string(oref)]);
1087 }
1088 }
1089
1090 if !self.wrapped().is_empty() {
1091 builder.push_record(vec![format!("Wrapped Objects: ")]);
1092
1093 for oref in self.wrapped() {
1094 builder.push_record(vec![objref_string(oref)]);
1095 }
1096 }
1097
1098 if !self.unwrapped().is_empty() {
1099 builder.push_record(vec![format!("Unwrapped Objects: ")]);
1100 for oref in self.unwrapped() {
1101 builder.push_record(vec![owned_objref_string(oref)]);
1102 }
1103 }
1104
1105 builder.push_record(vec![format!(
1106 "Gas Object: \n{}",
1107 owned_objref_string(self.gas_object())
1108 )]);
1109
1110 let gas_cost_summary = self.gas_cost_summary();
1111 builder.push_record(vec![format!(
1112 "Gas Cost Summary:\n \
1113 Storage Cost: {} NANOS\n \
1114 Computation Cost: {} NANOS\n \
1115 Computation Cost Burned: {} NANOS\n \
1116 Storage Rebate: {} NANOS\n \
1117 Non-refundable Storage Fee: {} NANOS",
1118 gas_cost_summary.storage_cost,
1119 gas_cost_summary.computation_cost,
1120 gas_cost_summary.computation_cost_burned,
1121 gas_cost_summary.storage_rebate,
1122 gas_cost_summary.non_refundable_storage_fee,
1123 )]);
1124
1125 let dependencies = self.dependencies();
1126 if !dependencies.is_empty() {
1127 builder.push_record(vec![format!("\nTransaction Dependencies:")]);
1128 for dependency in dependencies {
1129 builder.push_record(vec![format!(" {dependency}")]);
1130 }
1131 }
1132
1133 let mut table = builder.build();
1134 table.with(TablePanel::header("Transaction Effects"));
1135 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1136 1,
1137 TableStyle::modern().get_horizontal(),
1138 )]));
1139 write!(f, "{table}")
1140 }
1141}
1142
1143#[serde_as]
1144#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1145#[serde(rename_all = "camelCase")]
1146pub struct DryRunTransactionBlockResponse {
1147 pub effects: IotaTransactionBlockEffects,
1148 pub events: IotaTransactionBlockEvents,
1149 pub object_changes: Vec<ObjectChange>,
1150 pub balance_changes: Vec<BalanceChange>,
1151 pub input: IotaTransactionBlockData,
1152 #[serde(default, skip_serializing_if = "Option::is_none")]
1154 #[schemars(with = "Option<BigInt<u64>>")]
1155 #[serde_as(as = "Option<BigInt<u64>>")]
1156 pub suggested_gas_price: Option<u64>,
1157 pub execution_error_source: Option<String>,
1158}
1159
1160#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1161#[serde(rename = "TransactionBlockEvents", transparent)]
1162pub struct IotaTransactionBlockEvents {
1163 pub data: Vec<IotaEvent>,
1164}
1165
1166impl IotaTransactionBlockEvents {
1167 pub fn try_from(
1168 events: TransactionEvents,
1169 tx_digest: TransactionDigest,
1170 timestamp_ms: Option<u64>,
1171 resolver: &mut dyn LayoutResolver,
1172 ) -> IotaResult<Self> {
1173 Ok(Self {
1174 data: events
1175 .data
1176 .into_iter()
1177 .enumerate()
1178 .map(|(seq, event)| {
1179 let layout = resolver.get_annotated_layout(&event.type_)?;
1180 IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1181 })
1182 .collect::<Result<_, _>>()?,
1183 })
1184 }
1185
1186 pub fn try_from_using_module_resolver(
1189 events: TransactionEvents,
1190 tx_digest: TransactionDigest,
1191 timestamp_ms: Option<u64>,
1192 resolver: &impl GetModule,
1193 ) -> IotaResult<Self> {
1194 Ok(Self {
1195 data: events
1196 .data
1197 .into_iter()
1198 .enumerate()
1199 .map(|(seq, event)| {
1200 let layout = get_layout_from_struct_tag(event.type_.clone(), resolver)?;
1201 IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1202 })
1203 .collect::<Result<_, _>>()?,
1204 })
1205 }
1206}
1207
1208impl Display for IotaTransactionBlockEvents {
1209 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1210 if self.data.is_empty() {
1211 writeln!(f, "╭─────────────────────────────╮")?;
1212 writeln!(f, "│ No transaction block events │")?;
1213 writeln!(f, "╰─────────────────────────────╯")
1214 } else {
1215 let mut builder = TableBuilder::default();
1216
1217 for event in &self.data {
1218 builder.push_record(vec![format!("{event}")]);
1219 }
1220
1221 let mut table = builder.build();
1222 table.with(TablePanel::header("Transaction Block Events"));
1223 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1224 1,
1225 TableStyle::modern().get_horizontal(),
1226 )]));
1227 write!(f, "{table}")
1228 }
1229 }
1230}
1231
1232#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
1236#[serde(rename = "DevInspectArgs", rename_all = "camelCase")]
1237pub struct DevInspectArgs {
1238 pub gas_sponsor: Option<IotaAddress>,
1241 pub gas_budget: Option<BigInt<u64>>,
1243 pub gas_objects: Option<Vec<ObjectRef>>,
1245 pub skip_checks: Option<bool>,
1247 pub show_raw_txn_data_and_effects: Option<bool>,
1249}
1250
1251#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1253#[serde(rename = "DevInspectResults", rename_all = "camelCase")]
1254pub struct DevInspectResults {
1255 pub effects: IotaTransactionBlockEffects,
1260 pub events: IotaTransactionBlockEvents,
1263 #[serde(skip_serializing_if = "Option::is_none")]
1266 pub results: Option<Vec<IotaExecutionResult>>,
1267 #[serde(skip_serializing_if = "Option::is_none")]
1269 pub error: Option<String>,
1270 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1272 pub raw_txn_data: Vec<u8>,
1273 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1275 pub raw_effects: Vec<u8>,
1276}
1277
1278#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1279#[serde(rename = "IotaExecutionResult", rename_all = "camelCase")]
1280pub struct IotaExecutionResult {
1281 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1284 pub mutable_reference_outputs: Vec<(IotaArgument, Vec<u8>, IotaTypeTag)>,
1285 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1287 pub return_values: Vec<(Vec<u8>, IotaTypeTag)>,
1288}
1289
1290impl IotaExecutionResult {
1291 fn into_stream_return_value_layouts<S: PackageStore>(
1292 self,
1293 package_resolver: &Resolver<S>,
1294 ) -> impl Stream<Item = anyhow::Result<(Vec<u8>, MoveTypeLayout)>> + use<'_, S> {
1295 self.return_values
1296 .into_iter()
1297 .map(|(bytes, iota_type_tag)| async {
1298 let type_tag = TypeTag::try_from(iota_type_tag)?;
1299 let move_type_layout = package_resolver.type_layout(type_tag).await?;
1300 Ok((bytes, move_type_layout))
1301 })
1302 .collect::<FuturesOrdered<_>>()
1303 }
1304}
1305
1306#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1307pub enum IotaMoveViewCallResults {
1308 #[serde(rename = "executionError")]
1310 Error(String),
1311 #[serde(rename = "functionReturnValues")]
1313 Results(Vec<IotaMoveValue>),
1314}
1315
1316impl IotaMoveViewCallResults {
1317 pub async fn from_dev_inspect_results<S: PackageStore>(
1320 package_store: S,
1321 dev_inspect_results: DevInspectResults,
1322 ) -> anyhow::Result<Self> {
1323 if let Some(error) = dev_inspect_results.error {
1324 return Ok(Self::Error(error));
1325 }
1326 let Some(mut tx_execution_results) = dev_inspect_results.results else {
1327 return Ok(Self::Error("function call returned no values".into()));
1328 };
1329 let Some(execution_results) = tx_execution_results.pop() else {
1330 return Ok(Self::Error(
1331 "no results from move view function call".into(),
1332 ));
1333 };
1334 if !tx_execution_results.is_empty() {
1335 return Ok(Self::Error("multiple transactions executed".into()));
1336 }
1337 let mut move_call_results = Vec::with_capacity(execution_results.return_values.len());
1338 let package_resolver = Resolver::new(package_store);
1339 let mut execution_results =
1340 execution_results.into_stream_return_value_layouts(&package_resolver);
1341 while let Some(result) = execution_results.next().await {
1342 let (bytes, move_type_layout) = result?;
1343 let move_value = BoundedVisitor::deserialize_value(&bytes, &move_type_layout)?;
1344 move_call_results.push(IotaMoveValue::from(move_value));
1345 }
1346 Ok(Self::Results(move_call_results))
1347 }
1348
1349 pub fn into_return_values(self) -> Vec<IotaMoveValue> {
1350 match self {
1351 IotaMoveViewCallResults::Error(_) => Default::default(),
1352 IotaMoveViewCallResults::Results(values) => values,
1353 }
1354 }
1355
1356 pub fn error(&self) -> Option<&str> {
1357 match self {
1358 IotaMoveViewCallResults::Error(e) => Some(e.as_str()),
1359 IotaMoveViewCallResults::Results(_) => None,
1360 }
1361 }
1362}
1363
1364type ExecutionResult = (
1365 Vec<(Argument, Vec<u8>, TypeTag)>,
1367 Vec<(Vec<u8>, TypeTag)>,
1369);
1370
1371impl DevInspectResults {
1372 pub fn new(
1373 effects: TransactionEffects,
1374 events: TransactionEvents,
1375 return_values: Result<Vec<ExecutionResult>, ExecutionError>,
1376 raw_txn_data: Vec<u8>,
1377 raw_effects: Vec<u8>,
1378 resolver: &mut dyn LayoutResolver,
1379 ) -> IotaResult<Self> {
1380 let tx_digest = *effects.transaction_digest();
1381 let mut error = None;
1382 let mut results = None;
1383 match return_values {
1384 Err(e) => error = Some(e.to_string()),
1385 Ok(srvs) => {
1386 results = Some(
1387 srvs.into_iter()
1388 .map(|srv| {
1389 let (mutable_reference_outputs, return_values) = srv;
1390 let mutable_reference_outputs = mutable_reference_outputs
1391 .into_iter()
1392 .map(|(a, bytes, tag)| (a.into(), bytes, IotaTypeTag::from(tag)))
1393 .collect();
1394 let return_values = return_values
1395 .into_iter()
1396 .map(|(bytes, tag)| (bytes, IotaTypeTag::from(tag)))
1397 .collect();
1398 IotaExecutionResult {
1399 mutable_reference_outputs,
1400 return_values,
1401 }
1402 })
1403 .collect(),
1404 )
1405 }
1406 };
1407 Ok(Self {
1408 effects: effects.try_into()?,
1409 events: IotaTransactionBlockEvents::try_from(events, tx_digest, None, resolver)?,
1410 results,
1411 error,
1412 raw_txn_data,
1413 raw_effects,
1414 })
1415 }
1416}
1417
1418#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1419pub enum IotaTransactionBlockBuilderMode {
1420 Commit,
1422 DevInspect,
1425}
1426
1427#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1428#[serde(rename = "ExecutionStatus", rename_all = "camelCase", tag = "status")]
1429pub enum IotaExecutionStatus {
1430 Success,
1432 Failure { error: String },
1434}
1435
1436impl IotaExecutionStatus {
1437 pub async fn from_native_with_clever_error<S: PackageStore>(
1443 native: ExecutionStatus,
1444 resolver: &Resolver<S>,
1445 ) -> Self {
1446 match native {
1447 ExecutionStatus::Failure {
1448 error,
1449 command: Some(mut command_index),
1450 } => {
1451 let error = 'error: {
1452 let ExecutionFailureStatus::MoveAbort(loc, code) = &error else {
1453 break 'error error.to_string();
1454 };
1455 let fname_string = if let Some(fname) = &loc.function_name {
1456 format!("::{fname}'")
1457 } else {
1458 "'".to_string()
1459 };
1460
1461 let Some(CleverError {
1462 module_id,
1463 source_line_number,
1464 error_info,
1465 }) = resolver
1466 .resolve_clever_error(loc.module.clone(), *code)
1467 .await
1468 else {
1469 break 'error format!(
1470 "from '{}{fname_string} (instruction {}), abort code: {code}",
1471 loc.module.to_canonical_display(true),
1472 loc.instruction,
1473 );
1474 };
1475
1476 match error_info {
1477 ErrorConstants::Rendered {
1478 identifier,
1479 constant,
1480 } => {
1481 format!(
1482 "from '{}{fname_string} (line {source_line_number}), abort '{identifier}': {constant}",
1483 module_id.to_canonical_display(true)
1484 )
1485 }
1486 ErrorConstants::Raw { identifier, bytes } => {
1487 let const_str = Base64::encode(bytes);
1488 format!(
1489 "from '{}{fname_string} (line {source_line_number}), abort '{identifier}': {const_str}",
1490 module_id.to_canonical_display(true)
1491 )
1492 }
1493 ErrorConstants::None => {
1494 format!(
1495 "from '{}{fname_string} (line {source_line_number})",
1496 module_id.to_canonical_display(true)
1497 )
1498 }
1499 }
1500 };
1501 command_index += 1;
1503 let suffix = match command_index % 10 {
1504 1 => "st",
1505 2 => "nd",
1506 3 => "rd",
1507 _ => "th",
1508 };
1509 IotaExecutionStatus::Failure {
1510 error: format!("Error in {command_index}{suffix} command, {error}"),
1511 }
1512 }
1513 _ => native.into(),
1514 }
1515 }
1516}
1517
1518impl Display for IotaExecutionStatus {
1519 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1520 match self {
1521 Self::Success => write!(f, "success"),
1522 Self::Failure { error } => write!(f, "failure due to {error}"),
1523 }
1524 }
1525}
1526
1527impl IotaExecutionStatus {
1528 pub fn is_ok(&self) -> bool {
1529 matches!(self, IotaExecutionStatus::Success)
1530 }
1531 pub fn is_err(&self) -> bool {
1532 matches!(self, IotaExecutionStatus::Failure { .. })
1533 }
1534}
1535
1536impl From<ExecutionStatus> for IotaExecutionStatus {
1537 fn from(status: ExecutionStatus) -> Self {
1538 match status {
1539 ExecutionStatus::Success => Self::Success,
1540 ExecutionStatus::Failure {
1541 error,
1542 command: None,
1543 } => Self::Failure {
1544 error: error.to_string(),
1545 },
1546 ExecutionStatus::Failure {
1547 error,
1548 command: Some(idx),
1549 } => Self::Failure {
1550 error: format!("{error} in command {idx}"),
1551 },
1552 }
1553 }
1554}
1555
1556fn to_iota_object_ref(refs: Vec<ObjectRef>) -> Vec<IotaObjectRef> {
1557 refs.into_iter().map(IotaObjectRef::from).collect()
1558}
1559
1560fn to_owned_ref(owned_refs: Vec<(ObjectRef, Owner)>) -> Vec<OwnedObjectRef> {
1561 owned_refs
1562 .into_iter()
1563 .map(|(oref, owner)| OwnedObjectRef {
1564 owner,
1565 reference: oref.into(),
1566 })
1567 .collect()
1568}
1569
1570#[serde_as]
1571#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1572#[serde(rename = "GasData", rename_all = "camelCase")]
1573pub struct IotaGasData {
1574 pub payment: Vec<IotaObjectRef>,
1575 pub owner: IotaAddress,
1576 #[schemars(with = "BigInt<u64>")]
1577 #[serde_as(as = "BigInt<u64>")]
1578 pub price: u64,
1579 #[schemars(with = "BigInt<u64>")]
1580 #[serde_as(as = "BigInt<u64>")]
1581 pub budget: u64,
1582}
1583
1584impl Display for IotaGasData {
1585 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1586 writeln!(f, "Gas Owner: {}", self.owner)?;
1587 writeln!(f, "Gas Budget: {} NANOS", self.budget)?;
1588 writeln!(f, "Gas Price: {} NANOS", self.price)?;
1589 writeln!(f, "Gas Payment:")?;
1590 for payment in &self.payment {
1591 write!(f, "{} ", objref_string(payment))?;
1592 }
1593 writeln!(f)
1594 }
1595}
1596
1597#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1598#[enum_dispatch(IotaTransactionBlockDataAPI)]
1599#[serde(
1600 rename = "TransactionBlockData",
1601 rename_all = "camelCase",
1602 tag = "messageVersion"
1603)]
1604pub enum IotaTransactionBlockData {
1605 V1(IotaTransactionBlockDataV1),
1606}
1607
1608#[enum_dispatch]
1609pub trait IotaTransactionBlockDataAPI {
1610 fn transaction(&self) -> &IotaTransactionBlockKind;
1611 fn sender(&self) -> &IotaAddress;
1612 fn gas_data(&self) -> &IotaGasData;
1613}
1614
1615#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1616#[serde(rename = "TransactionBlockDataV1", rename_all = "camelCase")]
1617pub struct IotaTransactionBlockDataV1 {
1618 pub transaction: IotaTransactionBlockKind,
1619 pub sender: IotaAddress,
1620 pub gas_data: IotaGasData,
1621}
1622
1623impl IotaTransactionBlockDataAPI for IotaTransactionBlockDataV1 {
1624 fn transaction(&self) -> &IotaTransactionBlockKind {
1625 &self.transaction
1626 }
1627 fn sender(&self) -> &IotaAddress {
1628 &self.sender
1629 }
1630 fn gas_data(&self) -> &IotaGasData {
1631 &self.gas_data
1632 }
1633}
1634
1635impl IotaTransactionBlockData {
1636 pub fn move_calls(&self) -> Vec<&IotaProgrammableMoveCall> {
1637 match self {
1638 Self::V1(data) => match &data.transaction {
1639 IotaTransactionBlockKind::ProgrammableTransaction(pt) => pt
1640 .commands
1641 .iter()
1642 .filter_map(|command| match command {
1643 IotaCommand::MoveCall(c) => Some(&**c),
1644 _ => None,
1645 })
1646 .collect(),
1647 _ => vec![],
1648 },
1649 }
1650 }
1651
1652 fn try_from_inner(
1653 data: TransactionData,
1654 transaction: IotaTransactionBlockKind,
1655 ) -> Result<Self, anyhow::Error> {
1656 let message_version = data.message_version();
1657 let sender = data.sender();
1658 let gas_data = IotaGasData {
1659 payment: data
1660 .gas()
1661 .iter()
1662 .map(|obj_ref| IotaObjectRef::from(*obj_ref))
1663 .collect(),
1664 owner: data.gas_owner(),
1665 price: data.gas_price(),
1666 budget: data.gas_budget(),
1667 };
1668
1669 match message_version {
1670 1 => Ok(IotaTransactionBlockData::V1(IotaTransactionBlockDataV1 {
1671 transaction,
1672 sender,
1673 gas_data,
1674 })),
1675 _ => Err(anyhow::anyhow!(
1676 "Support for TransactionData version {} not implemented",
1677 message_version
1678 )),
1679 }
1680 }
1681
1682 pub fn try_from_with_module_cache(
1683 data: TransactionData,
1684 module_cache: &impl GetModule,
1685 tx_digest: TransactionDigest,
1686 ) -> Result<Self, anyhow::Error> {
1687 let transaction = IotaTransactionBlockKind::try_from_with_module_cache(
1688 data.kind().clone(),
1689 module_cache,
1690 tx_digest,
1691 )?;
1692 Self::try_from_inner(data, transaction)
1693 }
1694
1695 pub async fn try_from_with_package_resolver(
1696 data: TransactionData,
1697 package_resolver: &Resolver<impl PackageStore>,
1698 tx_digest: TransactionDigest,
1699 ) -> Result<Self, anyhow::Error> {
1700 let transaction = IotaTransactionBlockKind::try_from_with_package_resolver(
1701 data.kind().clone(),
1702 package_resolver,
1703 tx_digest,
1704 )
1705 .await?;
1706 Self::try_from_inner(data, transaction)
1707 }
1708}
1709
1710impl Display for IotaTransactionBlockData {
1711 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1712 match self {
1713 Self::V1(data) => {
1714 writeln!(f, "Sender: {}", data.sender)?;
1715 writeln!(f, "{}", self.gas_data())?;
1716 writeln!(f, "{}", data.transaction)
1717 }
1718 }
1719 }
1720}
1721
1722#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1723#[serde(rename = "TransactionBlock", rename_all = "camelCase")]
1724pub struct IotaTransactionBlock {
1725 pub data: IotaTransactionBlockData,
1726 pub tx_signatures: Vec<GenericSignature>,
1727}
1728
1729impl IotaTransactionBlock {
1730 pub fn try_from(
1731 data: SenderSignedData,
1732 module_cache: &impl GetModule,
1733 tx_digest: TransactionDigest,
1734 ) -> Result<Self, anyhow::Error> {
1735 Ok(Self {
1736 data: IotaTransactionBlockData::try_from_with_module_cache(
1737 data.intent_message().value.clone(),
1738 module_cache,
1739 tx_digest,
1740 )?,
1741 tx_signatures: data.tx_signatures().to_vec(),
1742 })
1743 }
1744
1745 pub async fn try_from_with_package_resolver(
1749 data: SenderSignedData,
1750 package_resolver: &Resolver<impl PackageStore>,
1751 tx_digest: TransactionDigest,
1752 ) -> Result<Self, anyhow::Error> {
1753 Ok(Self {
1754 data: IotaTransactionBlockData::try_from_with_package_resolver(
1755 data.intent_message().value.clone(),
1756 package_resolver,
1757 tx_digest,
1758 )
1759 .await?,
1760 tx_signatures: data.tx_signatures().to_vec(),
1761 })
1762 }
1763}
1764
1765impl Display for IotaTransactionBlock {
1766 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1767 let mut builder = TableBuilder::default();
1768
1769 builder.push_record(vec![format!("{}", self.data)]);
1770 builder.push_record(vec![format!("Signatures:")]);
1771 for tx_sig in &self.tx_signatures {
1772 builder.push_record(vec![format!(
1773 " {}\n",
1774 match tx_sig {
1775 Signature(sig) => Base64::from_bytes(sig.signature_bytes()).encoded(),
1776 _ => Base64::from_bytes(tx_sig.as_ref()).encoded(),
1780 }
1781 )]);
1782 }
1783
1784 let mut table = builder.build();
1785 table.with(TablePanel::header("Transaction Data"));
1786 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1787 1,
1788 TableStyle::modern().get_horizontal(),
1789 )]));
1790 write!(f, "{table}")
1791 }
1792}
1793
1794#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1795pub struct IotaGenesisTransaction {
1796 pub objects: Vec<ObjectID>,
1797 pub events: Vec<EventID>,
1798}
1799
1800#[serde_as]
1801#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1802pub struct IotaConsensusCommitPrologueV1 {
1803 #[schemars(with = "BigInt<u64>")]
1804 #[serde_as(as = "BigInt<u64>")]
1805 pub epoch: u64,
1806 #[schemars(with = "BigInt<u64>")]
1807 #[serde_as(as = "BigInt<u64>")]
1808 pub round: u64,
1809 #[schemars(with = "Option<BigInt<u64>>")]
1810 #[serde_as(as = "Option<BigInt<u64>>")]
1811 pub sub_dag_index: Option<u64>,
1812 #[schemars(with = "BigInt<u64>")]
1813 #[serde_as(as = "BigInt<u64>")]
1814 pub commit_timestamp_ms: u64,
1815 pub consensus_commit_digest: ConsensusCommitDigest,
1816 pub consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments,
1817}
1818
1819#[serde_as]
1820#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1821pub struct IotaAuthenticatorStateUpdateV1 {
1822 #[schemars(with = "BigInt<u64>")]
1823 #[serde_as(as = "BigInt<u64>")]
1824 pub epoch: u64,
1825 #[schemars(with = "BigInt<u64>")]
1826 #[serde_as(as = "BigInt<u64>")]
1827 pub round: u64,
1828
1829 pub new_active_jwks: Vec<IotaActiveJwk>,
1830}
1831
1832#[serde_as]
1833#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1834pub struct IotaRandomnessStateUpdate {
1835 #[schemars(with = "BigInt<u64>")]
1836 #[serde_as(as = "BigInt<u64>")]
1837 pub epoch: u64,
1838
1839 #[schemars(with = "BigInt<u64>")]
1840 #[serde_as(as = "BigInt<u64>")]
1841 pub randomness_round: u64,
1842 pub random_bytes: Vec<u8>,
1843}
1844
1845#[serde_as]
1846#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1847pub struct IotaEndOfEpochTransaction {
1848 pub transactions: Vec<IotaEndOfEpochTransactionKind>,
1849}
1850
1851#[serde_as]
1852#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1853pub enum IotaEndOfEpochTransactionKind {
1854 ChangeEpoch(IotaChangeEpoch),
1855 ChangeEpochV2(IotaChangeEpochV2),
1856 AuthenticatorStateCreate,
1857 AuthenticatorStateExpire(IotaAuthenticatorStateExpire),
1858}
1859
1860#[serde_as]
1861#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1862pub struct IotaAuthenticatorStateExpire {
1863 #[schemars(with = "BigInt<u64>")]
1864 #[serde_as(as = "BigInt<u64>")]
1865 pub min_epoch: u64,
1866}
1867
1868#[serde_as]
1869#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1870pub struct IotaActiveJwk {
1871 pub jwk_id: IotaJwkId,
1872 pub jwk: IotaJWK,
1873
1874 #[schemars(with = "BigInt<u64>")]
1875 #[serde_as(as = "BigInt<u64>")]
1876 pub epoch: u64,
1877}
1878
1879impl From<ActiveJwk> for IotaActiveJwk {
1880 fn from(active_jwk: ActiveJwk) -> Self {
1881 Self {
1882 jwk_id: IotaJwkId {
1883 iss: active_jwk.jwk_id.iss.clone(),
1884 kid: active_jwk.jwk_id.kid.clone(),
1885 },
1886 jwk: IotaJWK {
1887 kty: active_jwk.jwk.kty.clone(),
1888 e: active_jwk.jwk.e.clone(),
1889 n: active_jwk.jwk.n.clone(),
1890 alg: active_jwk.jwk.alg.clone(),
1891 },
1892 epoch: active_jwk.epoch,
1893 }
1894 }
1895}
1896
1897#[serde_as]
1898#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1899pub struct IotaJwkId {
1900 pub iss: String,
1901 pub kid: String,
1902}
1903
1904#[serde_as]
1905#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1906pub struct IotaJWK {
1907 pub kty: String,
1908 pub e: String,
1909 pub n: String,
1910 pub alg: String,
1911}
1912
1913#[serde_as]
1914#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
1915#[serde(rename = "InputObjectKind")]
1916pub enum IotaInputObjectKind {
1917 MovePackage(ObjectID),
1919 ImmOrOwnedMoveObject(IotaObjectRef),
1921 SharedMoveObject {
1923 id: ObjectID,
1924 #[schemars(with = "AsSequenceNumber")]
1925 #[serde_as(as = "AsSequenceNumber")]
1926 initial_shared_version: SequenceNumber,
1927 #[serde(default = "default_shared_object_mutability")]
1928 mutable: bool,
1929 },
1930}
1931
1932#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1935pub struct IotaProgrammableTransactionBlock {
1936 pub inputs: Vec<IotaCallArg>,
1938 #[serde(rename = "transactions")]
1939 pub commands: Vec<IotaCommand>,
1943}
1944
1945impl Display for IotaProgrammableTransactionBlock {
1946 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1947 let Self { inputs, commands } = self;
1948 writeln!(f, "Inputs: {inputs:?}")?;
1949 writeln!(f, "Commands: [")?;
1950 for c in commands {
1951 writeln!(f, " {c},")?;
1952 }
1953 writeln!(f, "]")
1954 }
1955}
1956
1957impl IotaProgrammableTransactionBlock {
1958 fn try_from_with_module_cache(
1959 value: ProgrammableTransaction,
1960 module_cache: &impl GetModule,
1961 ) -> Result<Self, anyhow::Error> {
1962 let ProgrammableTransaction { inputs, commands } = value;
1963 let input_types = Self::resolve_input_type(&inputs, &commands, module_cache);
1964 Ok(IotaProgrammableTransactionBlock {
1965 inputs: inputs
1966 .into_iter()
1967 .zip(input_types)
1968 .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
1969 .collect::<Result<_, _>>()?,
1970 commands: commands.into_iter().map(IotaCommand::from).collect(),
1971 })
1972 }
1973
1974 async fn try_from_with_package_resolver(
1975 value: ProgrammableTransaction,
1976 package_resolver: &Resolver<impl PackageStore>,
1977 ) -> Result<Self, anyhow::Error> {
1978 let input_types = package_resolver
1981 .pure_input_layouts(&value)
1982 .await
1983 .unwrap_or_else(|e| {
1984 tracing::warn!("pure_input_layouts failed: {:?}", e);
1985 vec![None; value.inputs.len()]
1986 });
1987
1988 let ProgrammableTransaction { inputs, commands } = value;
1989 Ok(IotaProgrammableTransactionBlock {
1990 inputs: inputs
1991 .into_iter()
1992 .zip(input_types)
1993 .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
1994 .collect::<Result<_, _>>()?,
1995 commands: commands.into_iter().map(IotaCommand::from).collect(),
1996 })
1997 }
1998
1999 fn resolve_input_type(
2000 inputs: &[CallArg],
2001 commands: &[Command],
2002 module_cache: &impl GetModule,
2003 ) -> Vec<Option<MoveTypeLayout>> {
2004 let mut result_types = vec![None; inputs.len()];
2005 for command in commands.iter() {
2006 match command {
2007 Command::MoveCall(c) => {
2008 let Ok(module) = Identifier::new(c.module.clone()) else {
2009 return result_types;
2010 };
2011
2012 let Ok(function) = Identifier::new(c.function.clone()) else {
2013 return result_types;
2014 };
2015
2016 let id = ModuleId::new(c.package.into(), module);
2017 let Some(types) =
2018 get_signature_types(id, function.as_ident_str(), module_cache)
2019 else {
2020 return result_types;
2021 };
2022 for (arg, type_) in c.arguments.iter().zip(types) {
2023 if let (&Argument::Input(i), Some(type_)) = (arg, type_) {
2024 if let Some(x) = result_types.get_mut(i as usize) {
2025 x.replace(type_);
2026 }
2027 }
2028 }
2029 }
2030 Command::SplitCoins(_, amounts) => {
2031 for arg in amounts {
2032 if let &Argument::Input(i) = arg {
2033 if let Some(x) = result_types.get_mut(i as usize) {
2034 x.replace(MoveTypeLayout::U64);
2035 }
2036 }
2037 }
2038 }
2039 Command::TransferObjects(_, Argument::Input(i)) => {
2040 if let Some(x) = result_types.get_mut((*i) as usize) {
2041 x.replace(MoveTypeLayout::Address);
2042 }
2043 }
2044 _ => {}
2045 }
2046 }
2047 result_types
2048 }
2049}
2050
2051fn get_signature_types(
2052 id: ModuleId,
2053 function: &IdentStr,
2054 module_cache: &impl GetModule,
2055) -> Option<Vec<Option<MoveTypeLayout>>> {
2056 use std::borrow::Borrow;
2057 if let Ok(Some(module)) = module_cache.get_module_by_id(&id) {
2058 let module: &CompiledModule = module.borrow();
2059 let func = module
2060 .function_handles
2061 .iter()
2062 .find(|f| module.identifier_at(f.name) == function)?;
2063 Some(
2064 module
2065 .signature_at(func.parameters)
2066 .0
2067 .iter()
2068 .map(|s| primitive_type(module, &[], s))
2069 .collect(),
2070 )
2071 } else {
2072 None
2073 }
2074}
2075
2076#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2078#[serde(rename = "IotaTransaction")]
2079pub enum IotaCommand {
2080 MoveCall(Box<IotaProgrammableMoveCall>),
2082 TransferObjects(Vec<IotaArgument>, IotaArgument),
2087 SplitCoins(IotaArgument, Vec<IotaArgument>),
2090 MergeCoins(IotaArgument, Vec<IotaArgument>),
2093 Publish(Vec<ObjectID>),
2096 Upgrade(Vec<ObjectID>, ObjectID, IotaArgument),
2098 MakeMoveVec(Option<String>, Vec<IotaArgument>),
2102}
2103
2104impl Display for IotaCommand {
2105 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2106 match self {
2107 Self::MoveCall(p) => {
2108 write!(f, "MoveCall({p})")
2109 }
2110 Self::MakeMoveVec(ty_opt, elems) => {
2111 write!(f, "MakeMoveVec(")?;
2112 if let Some(ty) = ty_opt {
2113 write!(f, "Some{ty}")?;
2114 } else {
2115 write!(f, "None")?;
2116 }
2117 write!(f, ",[")?;
2118 write_sep(f, elems, ",")?;
2119 write!(f, "])")
2120 }
2121 Self::TransferObjects(objs, addr) => {
2122 write!(f, "TransferObjects([")?;
2123 write_sep(f, objs, ",")?;
2124 write!(f, "],{addr})")
2125 }
2126 Self::SplitCoins(coin, amounts) => {
2127 write!(f, "SplitCoins({coin},")?;
2128 write_sep(f, amounts, ",")?;
2129 write!(f, ")")
2130 }
2131 Self::MergeCoins(target, coins) => {
2132 write!(f, "MergeCoins({target},")?;
2133 write_sep(f, coins, ",")?;
2134 write!(f, ")")
2135 }
2136 Self::Publish(deps) => {
2137 write!(f, "Publish(<modules>,")?;
2138 write_sep(f, deps, ",")?;
2139 write!(f, ")")
2140 }
2141 Self::Upgrade(deps, current_package_id, ticket) => {
2142 write!(f, "Upgrade(<modules>, {ticket},")?;
2143 write_sep(f, deps, ",")?;
2144 write!(f, ", {current_package_id}")?;
2145 write!(f, ")")
2146 }
2147 }
2148 }
2149}
2150
2151impl From<Command> for IotaCommand {
2152 fn from(value: Command) -> Self {
2153 match value {
2154 Command::MoveCall(m) => IotaCommand::MoveCall(Box::new((*m).into())),
2155 Command::TransferObjects(args, arg) => IotaCommand::TransferObjects(
2156 args.into_iter().map(IotaArgument::from).collect(),
2157 arg.into(),
2158 ),
2159 Command::SplitCoins(arg, args) => IotaCommand::SplitCoins(
2160 arg.into(),
2161 args.into_iter().map(IotaArgument::from).collect(),
2162 ),
2163 Command::MergeCoins(arg, args) => IotaCommand::MergeCoins(
2164 arg.into(),
2165 args.into_iter().map(IotaArgument::from).collect(),
2166 ),
2167 Command::Publish(_modules, dep_ids) => IotaCommand::Publish(dep_ids),
2168 Command::MakeMoveVec(tag_opt, args) => IotaCommand::MakeMoveVec(
2169 tag_opt.map(|tag| tag.to_string()),
2170 args.into_iter().map(IotaArgument::from).collect(),
2171 ),
2172 Command::Upgrade(_modules, dep_ids, current_package_id, ticket) => {
2173 IotaCommand::Upgrade(dep_ids, current_package_id, IotaArgument::from(ticket))
2174 }
2175 }
2176 }
2177}
2178
2179#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2181pub enum IotaArgument {
2182 GasCoin,
2185 Input(u16),
2188 Result(u16),
2191 NestedResult(u16, u16),
2195}
2196
2197impl Display for IotaArgument {
2198 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2199 match self {
2200 Self::GasCoin => write!(f, "GasCoin"),
2201 Self::Input(i) => write!(f, "Input({i})"),
2202 Self::Result(i) => write!(f, "Result({i})"),
2203 Self::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
2204 }
2205 }
2206}
2207
2208impl From<Argument> for IotaArgument {
2209 fn from(value: Argument) -> Self {
2210 match value {
2211 Argument::GasCoin => Self::GasCoin,
2212 Argument::Input(i) => Self::Input(i),
2213 Argument::Result(i) => Self::Result(i),
2214 Argument::NestedResult(i, j) => Self::NestedResult(i, j),
2215 }
2216 }
2217}
2218
2219#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2220#[serde(untagged)]
2221pub enum PtbInput {
2222 PtbRef(IotaArgument),
2223 CallArg(IotaJsonValue),
2224}
2225
2226#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2229pub struct IotaProgrammableMoveCall {
2230 pub package: ObjectID,
2232 pub module: String,
2234 pub function: String,
2236 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2237 pub type_arguments: Vec<String>,
2239 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2240 pub arguments: Vec<IotaArgument>,
2242}
2243
2244fn write_sep<T: Display>(
2245 f: &mut Formatter<'_>,
2246 items: impl IntoIterator<Item = T>,
2247 sep: &str,
2248) -> std::fmt::Result {
2249 let mut xs = items.into_iter().peekable();
2250 while let Some(x) = xs.next() {
2251 write!(f, "{x}")?;
2252 if xs.peek().is_some() {
2253 write!(f, "{sep}")?;
2254 }
2255 }
2256 Ok(())
2257}
2258
2259impl Display for IotaProgrammableMoveCall {
2260 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2261 let Self {
2262 package,
2263 module,
2264 function,
2265 type_arguments,
2266 arguments,
2267 } = self;
2268 write!(f, "{package}::{module}::{function}")?;
2269 if !type_arguments.is_empty() {
2270 write!(f, "<")?;
2271 write_sep(f, type_arguments, ",")?;
2272 write!(f, ">")?;
2273 }
2274 write!(f, "(")?;
2275 write_sep(f, arguments, ",")?;
2276 write!(f, ")")
2277 }
2278}
2279
2280impl From<ProgrammableMoveCall> for IotaProgrammableMoveCall {
2281 fn from(value: ProgrammableMoveCall) -> Self {
2282 let ProgrammableMoveCall {
2283 package,
2284 module,
2285 function,
2286 type_arguments,
2287 arguments,
2288 } = value;
2289 Self {
2290 package,
2291 module,
2292 function,
2293 type_arguments: type_arguments.into_iter().map(|t| t.to_string()).collect(),
2294 arguments: arguments.into_iter().map(IotaArgument::from).collect(),
2295 }
2296 }
2297}
2298
2299const fn default_shared_object_mutability() -> bool {
2300 true
2301}
2302
2303impl From<InputObjectKind> for IotaInputObjectKind {
2304 fn from(input: InputObjectKind) -> Self {
2305 match input {
2306 InputObjectKind::MovePackage(id) => Self::MovePackage(id),
2307 InputObjectKind::ImmOrOwnedMoveObject(oref) => Self::ImmOrOwnedMoveObject(oref.into()),
2308 InputObjectKind::SharedMoveObject {
2309 id,
2310 initial_shared_version,
2311 mutable,
2312 } => Self::SharedMoveObject {
2313 id,
2314 initial_shared_version,
2315 mutable,
2316 },
2317 }
2318 }
2319}
2320
2321#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
2322#[serde(rename = "TypeTag", rename_all = "camelCase")]
2323pub struct IotaTypeTag(String);
2324
2325impl IotaTypeTag {
2326 pub fn new(tag: String) -> Self {
2327 Self(tag)
2328 }
2329}
2330
2331impl AsRef<str> for IotaTypeTag {
2332 fn as_ref(&self) -> &str {
2333 &self.0
2334 }
2335}
2336
2337impl TryFrom<IotaTypeTag> for TypeTag {
2338 type Error = anyhow::Error;
2339 fn try_from(tag: IotaTypeTag) -> Result<Self, Self::Error> {
2340 parse_iota_type_tag(&tag.0)
2341 }
2342}
2343
2344impl From<TypeTag> for IotaTypeTag {
2345 fn from(tag: TypeTag) -> Self {
2346 Self(format!("{tag}"))
2347 }
2348}
2349
2350#[derive(Serialize, Deserialize, JsonSchema)]
2351#[serde(rename_all = "camelCase")]
2352pub enum RPCTransactionRequestParams {
2353 TransferObjectRequestParams(TransferObjectParams),
2354 MoveCallRequestParams(MoveCallParams),
2355}
2356
2357#[derive(Serialize, Deserialize, JsonSchema)]
2358#[serde(rename_all = "camelCase")]
2359pub struct TransferObjectParams {
2360 pub recipient: IotaAddress,
2361 pub object_id: ObjectID,
2362}
2363
2364#[derive(Serialize, Deserialize, JsonSchema)]
2365#[serde(rename_all = "camelCase")]
2366pub struct MoveCallParams {
2367 pub package_object_id: ObjectID,
2368 pub module: String,
2369 pub function: String,
2370 #[serde(default)]
2371 pub type_arguments: Vec<IotaTypeTag>,
2372 pub arguments: Vec<PtbInput>,
2373}
2374
2375#[serde_as]
2376#[derive(Serialize, Deserialize, Clone, JsonSchema)]
2377#[serde(rename_all = "camelCase")]
2378pub struct TransactionBlockBytes {
2379 pub tx_bytes: Base64,
2382 pub gas: Vec<IotaObjectRef>,
2384 pub input_objects: Vec<IotaInputObjectKind>,
2386}
2387
2388impl TransactionBlockBytes {
2389 pub fn from_data(data: TransactionData) -> Result<Self, anyhow::Error> {
2390 Ok(Self {
2391 tx_bytes: Base64::from_bytes(bcs::to_bytes(&data)?.as_slice()),
2392 gas: data
2393 .gas()
2394 .iter()
2395 .map(|obj_ref| IotaObjectRef::from(*obj_ref))
2396 .collect(),
2397 input_objects: data
2398 .input_objects()?
2399 .into_iter()
2400 .map(IotaInputObjectKind::from)
2401 .collect(),
2402 })
2403 }
2404
2405 pub fn to_data(self) -> Result<TransactionData, anyhow::Error> {
2406 bcs::from_bytes::<TransactionData>(&self.tx_bytes.to_vec().map_err(|e| anyhow::anyhow!(e))?)
2407 .map_err(|e| anyhow::anyhow!(e))
2408 }
2409}
2410
2411#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
2412#[serde(rename = "OwnedObjectRef")]
2413pub struct OwnedObjectRef {
2414 pub owner: Owner,
2415 pub reference: IotaObjectRef,
2416}
2417
2418impl OwnedObjectRef {
2419 pub fn object_id(&self) -> ObjectID {
2420 self.reference.object_id
2421 }
2422 pub fn version(&self) -> SequenceNumber {
2423 self.reference.version
2424 }
2425}
2426
2427#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2428#[serde(tag = "type", rename_all = "camelCase")]
2429pub enum IotaCallArg {
2430 Object(IotaObjectArg),
2432 Pure(IotaPureValue),
2434}
2435
2436impl IotaCallArg {
2437 pub fn try_from(
2438 value: CallArg,
2439 layout: Option<&MoveTypeLayout>,
2440 ) -> Result<Self, anyhow::Error> {
2441 Ok(match value {
2442 CallArg::Pure(p) => IotaCallArg::Pure(IotaPureValue {
2443 value_type: layout.map(|l| l.into()),
2444 value: IotaJsonValue::from_bcs_bytes(layout, &p)?,
2445 }),
2446 CallArg::Object(ObjectArg::ImmOrOwnedObject((id, version, digest))) => {
2447 IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject {
2448 object_id: id,
2449 version,
2450 digest,
2451 })
2452 }
2453 CallArg::Object(ObjectArg::SharedObject {
2454 id,
2455 initial_shared_version,
2456 mutable,
2457 }) => IotaCallArg::Object(IotaObjectArg::SharedObject {
2458 object_id: id,
2459 initial_shared_version,
2460 mutable,
2461 }),
2462 CallArg::Object(ObjectArg::Receiving((object_id, version, digest))) => {
2463 IotaCallArg::Object(IotaObjectArg::Receiving {
2464 object_id,
2465 version,
2466 digest,
2467 })
2468 }
2469 })
2470 }
2471
2472 pub fn pure(&self) -> Option<&IotaJsonValue> {
2473 match self {
2474 IotaCallArg::Pure(v) => Some(&v.value),
2475 _ => None,
2476 }
2477 }
2478
2479 pub fn object(&self) -> Option<&ObjectID> {
2480 match self {
2481 IotaCallArg::Object(IotaObjectArg::SharedObject { object_id, .. })
2482 | IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject { object_id, .. })
2483 | IotaCallArg::Object(IotaObjectArg::Receiving { object_id, .. }) => Some(object_id),
2484 _ => None,
2485 }
2486 }
2487}
2488
2489#[serde_as]
2490#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2491#[serde(rename_all = "camelCase")]
2492pub struct IotaPureValue {
2493 #[schemars(with = "Option<String>")]
2494 #[serde_as(as = "Option<AsIotaTypeTag>")]
2495 value_type: Option<TypeTag>,
2496 value: IotaJsonValue,
2497}
2498
2499impl IotaPureValue {
2500 pub fn value(&self) -> IotaJsonValue {
2501 self.value.clone()
2502 }
2503
2504 pub fn value_type(&self) -> Option<TypeTag> {
2505 self.value_type.clone()
2506 }
2507}
2508
2509#[serde_as]
2510#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2511#[serde(tag = "objectType", rename_all = "camelCase")]
2512pub enum IotaObjectArg {
2513 #[serde(rename_all = "camelCase")]
2515 ImmOrOwnedObject {
2516 object_id: ObjectID,
2517 #[schemars(with = "AsSequenceNumber")]
2518 #[serde_as(as = "AsSequenceNumber")]
2519 version: SequenceNumber,
2520 digest: ObjectDigest,
2521 },
2522 #[serde(rename_all = "camelCase")]
2526 SharedObject {
2527 object_id: ObjectID,
2528 #[schemars(with = "AsSequenceNumber")]
2529 #[serde_as(as = "AsSequenceNumber")]
2530 initial_shared_version: SequenceNumber,
2531 mutable: bool,
2532 },
2533 #[serde(rename_all = "camelCase")]
2535 Receiving {
2536 object_id: ObjectID,
2537 #[schemars(with = "AsSequenceNumber")]
2538 #[serde_as(as = "AsSequenceNumber")]
2539 version: SequenceNumber,
2540 digest: ObjectDigest,
2541 },
2542}
2543
2544#[derive(Clone)]
2545pub struct EffectsWithInput {
2546 pub effects: IotaTransactionBlockEffects,
2547 pub input: TransactionData,
2548}
2549
2550impl From<EffectsWithInput> for IotaTransactionBlockEffects {
2551 fn from(e: EffectsWithInput) -> Self {
2552 e.effects
2553 }
2554}
2555
2556#[serde_as]
2557#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2558pub enum TransactionFilter {
2559 Checkpoint(
2561 #[schemars(with = "BigInt<u64>")]
2562 #[serde_as(as = "Readable<BigInt<u64>, _>")]
2563 CheckpointSequenceNumber,
2564 ),
2565 MoveFunction {
2567 package: ObjectID,
2568 module: Option<String>,
2569 function: Option<String>,
2570 },
2571 InputObject(ObjectID),
2573 ChangedObject(ObjectID),
2576 FromAddress(IotaAddress),
2578 ToAddress(IotaAddress),
2580 FromAndToAddress { from: IotaAddress, to: IotaAddress },
2582 FromOrToAddress { addr: IotaAddress },
2584 TransactionKind(IotaTransactionKind),
2586 TransactionKindIn(Vec<IotaTransactionKind>),
2588}
2589
2590impl TransactionFilter {
2591 pub fn as_v2(&self) -> TransactionFilterV2 {
2592 match self {
2593 TransactionFilter::InputObject(o) => TransactionFilterV2::InputObject(*o),
2594 TransactionFilter::ChangedObject(o) => TransactionFilterV2::ChangedObject(*o),
2595 TransactionFilter::FromAddress(a) => TransactionFilterV2::FromAddress(*a),
2596 TransactionFilter::ToAddress(a) => TransactionFilterV2::ToAddress(*a),
2597 TransactionFilter::FromAndToAddress { from, to } => {
2598 TransactionFilterV2::FromAndToAddress {
2599 from: *from,
2600 to: *to,
2601 }
2602 }
2603 TransactionFilter::FromOrToAddress { addr } => {
2604 TransactionFilterV2::FromOrToAddress { addr: *addr }
2605 }
2606 TransactionFilter::MoveFunction {
2607 package,
2608 module,
2609 function,
2610 } => TransactionFilterV2::MoveFunction {
2611 package: *package,
2612 module: module.clone(),
2613 function: function.clone(),
2614 },
2615 TransactionFilter::TransactionKind(kind) => TransactionFilterV2::TransactionKind(*kind),
2616 TransactionFilter::TransactionKindIn(kinds) => {
2617 TransactionFilterV2::TransactionKindIn(kinds.clone())
2618 }
2619 TransactionFilter::Checkpoint(checkpoint) => {
2620 TransactionFilterV2::Checkpoint(*checkpoint)
2621 }
2622 }
2623 }
2624}
2625
2626impl Filter<EffectsWithInput> for TransactionFilter {
2627 fn matches(&self, item: &EffectsWithInput) -> bool {
2628 let _scope = monitored_scope("TransactionFilter::matches");
2629 match self {
2630 TransactionFilter::InputObject(o) => {
2631 let Ok(input_objects) = item.input.input_objects() else {
2632 return false;
2633 };
2634 input_objects.iter().any(|object| object.object_id() == *o)
2635 }
2636 TransactionFilter::ChangedObject(o) => item
2637 .effects
2638 .mutated()
2639 .iter()
2640 .any(|oref: &OwnedObjectRef| &oref.reference.object_id == o),
2641 TransactionFilter::FromAddress(a) => &item.input.sender() == a,
2642 TransactionFilter::ToAddress(a) => {
2643 let mutated: &[OwnedObjectRef] = item.effects.mutated();
2644 mutated.iter().chain(item.effects.unwrapped().iter()).any(|oref: &OwnedObjectRef| {
2645 matches!(oref.owner, Owner::AddressOwner(owner) if owner == *a)
2646 })
2647 }
2648 TransactionFilter::FromAndToAddress { from, to } => {
2649 Self::FromAddress(*from).matches(item) && Self::ToAddress(*to).matches(item)
2650 }
2651 TransactionFilter::FromOrToAddress { addr } => {
2652 Self::FromAddress(*addr).matches(item) || Self::ToAddress(*addr).matches(item)
2653 }
2654 TransactionFilter::MoveFunction {
2655 package,
2656 module,
2657 function,
2658 } => item.input.move_calls().into_iter().any(|(p, m, f)| {
2659 p == package
2660 && (module.is_none() || matches!(module, Some(m2) if m2 == &m.to_string()))
2661 && (function.is_none() || matches!(function, Some(f2) if f2 == &f.to_string()))
2662 }),
2663 TransactionFilter::TransactionKind(kind) => {
2664 kind == &IotaTransactionKind::from(item.input.kind())
2665 }
2666 TransactionFilter::TransactionKindIn(kinds) => kinds
2667 .iter()
2668 .any(|kind| kind == &IotaTransactionKind::from(item.input.kind())),
2669 TransactionFilter::Checkpoint(_) => false,
2671 }
2672 }
2673}
2674
2675#[serde_as]
2676#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2677#[non_exhaustive]
2678pub enum TransactionFilterV2 {
2679 Checkpoint(
2681 #[schemars(with = "BigInt<u64>")]
2682 #[serde_as(as = "Readable<BigInt<u64>, _>")]
2683 CheckpointSequenceNumber,
2684 ),
2685 MoveFunction {
2687 package: ObjectID,
2688 module: Option<String>,
2689 function: Option<String>,
2690 },
2691 InputObject(ObjectID),
2693 ChangedObject(ObjectID),
2696 WrappedOrDeletedObject(ObjectID),
2700 FromAddress(IotaAddress),
2702 ToAddress(IotaAddress),
2704 FromAndToAddress { from: IotaAddress, to: IotaAddress },
2706 FromOrToAddress { addr: IotaAddress },
2708 TransactionKind(IotaTransactionKind),
2710 TransactionKindIn(Vec<IotaTransactionKind>),
2712}
2713
2714impl TransactionFilterV2 {
2715 pub fn as_v1(&self) -> Option<TransactionFilter> {
2716 match self {
2717 TransactionFilterV2::InputObject(o) => Some(TransactionFilter::InputObject(*o)),
2718 TransactionFilterV2::ChangedObject(o) => Some(TransactionFilter::ChangedObject(*o)),
2719 TransactionFilterV2::FromAddress(a) => Some(TransactionFilter::FromAddress(*a)),
2720 TransactionFilterV2::ToAddress(a) => Some(TransactionFilter::ToAddress(*a)),
2721 TransactionFilterV2::FromAndToAddress { from, to } => {
2722 Some(TransactionFilter::FromAndToAddress {
2723 from: *from,
2724 to: *to,
2725 })
2726 }
2727 TransactionFilterV2::FromOrToAddress { addr } => {
2728 Some(TransactionFilter::FromOrToAddress { addr: *addr })
2729 }
2730 TransactionFilterV2::MoveFunction {
2731 package,
2732 module,
2733 function,
2734 } => Some(TransactionFilter::MoveFunction {
2735 package: *package,
2736 module: module.clone(),
2737 function: function.clone(),
2738 }),
2739 TransactionFilterV2::TransactionKind(kind) => {
2740 Some(TransactionFilter::TransactionKind(*kind))
2741 }
2742 TransactionFilterV2::TransactionKindIn(kinds) => {
2743 Some(TransactionFilter::TransactionKindIn(kinds.clone()))
2744 }
2745 TransactionFilterV2::Checkpoint(checkpoint) => {
2746 Some(TransactionFilter::Checkpoint(*checkpoint))
2747 }
2748 TransactionFilterV2::WrappedOrDeletedObject(_) => None,
2750 }
2751 }
2752}
2753
2754impl Filter<EffectsWithInput> for TransactionFilterV2 {
2755 fn matches(&self, item: &EffectsWithInput) -> bool {
2756 let _scope = monitored_scope("TransactionFilterV2::matches");
2757 if let Some(v1) = self.as_v1() {
2758 return v1.matches(item);
2759 }
2760 match self {
2762 TransactionFilterV2::WrappedOrDeletedObject(o) => item
2763 .effects
2764 .wrapped()
2765 .iter()
2766 .chain(item.effects.deleted())
2767 .chain(item.effects.unwrapped_then_deleted())
2768 .any(|oref| &oref.object_id == o),
2769
2770 _ => false,
2771 }
2772 }
2773}
2774
2775#[derive(
2778 Debug, Clone, Copy, PartialEq, Eq, EnumString, Display, Serialize, Deserialize, JsonSchema,
2779)]
2780#[non_exhaustive]
2781pub enum IotaTransactionKind {
2782 SystemTransaction = 0,
2785 ProgrammableTransaction = 1,
2786 Genesis = 2,
2787 ConsensusCommitPrologueV1 = 3,
2788 AuthenticatorStateUpdateV1 = 4,
2789 RandomnessStateUpdate = 5,
2790 EndOfEpochTransaction = 6,
2791}
2792
2793impl IotaTransactionKind {
2794 pub fn is_system_transaction(&self) -> bool {
2796 !matches!(self, Self::ProgrammableTransaction)
2797 }
2798}
2799
2800impl From<&TransactionKind> for IotaTransactionKind {
2801 fn from(kind: &TransactionKind) -> Self {
2802 match kind {
2803 TransactionKind::Genesis(_) => Self::Genesis,
2804 TransactionKind::ConsensusCommitPrologueV1(_) => Self::ConsensusCommitPrologueV1,
2805 TransactionKind::AuthenticatorStateUpdateV1(_) => Self::AuthenticatorStateUpdateV1,
2806 TransactionKind::RandomnessStateUpdate(_) => Self::RandomnessStateUpdate,
2807 TransactionKind::EndOfEpochTransaction(_) => Self::EndOfEpochTransaction,
2808 TransactionKind::ProgrammableTransaction(_) => Self::ProgrammableTransaction,
2809 }
2810 }
2811}