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