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