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_sdk_types::{
14 Argument, CancelledTransaction, ChangeEpoch, ChangeEpochV2, ChangeEpochV3, ChangeEpochV4,
15 Command, ConsensusDeterminedVersionAssignments, ExecutionError as ExecutionFailureStatus,
16 ExecutionStatus, GenesisObject, Identifier, MoveCall, ObjectId, Owner, TransactionKind,
17 TransferObjects, TypeTag, VersionAssignment, gas::GasCostSummary,
18};
19use iota_types::{
20 base_types::{EpochId, IotaAddress, ObjectRef, SequenceNumber, TransactionDigest},
21 crypto::IotaSignature,
22 digests::{ConsensusCommitDigest, ObjectDigest, TransactionEventsDigest},
23 effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents},
24 error::{ExecutionError, IotaError, IotaResult},
25 event::EventID,
26 iota_sdk_types_conversions::type_tag_core_to_sdk,
27 iota_serde::BigInt,
28 layout_resolver::{LayoutResolver, get_layout_from_struct_tag},
29 messages_checkpoint::CheckpointSequenceNumber,
30 object::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 CallArg, EndOfEpochTransactionKind, InputObjectKind, ProgrammableTransaction,
37 SenderSignedData, SharedObjectRef, TransactionData, TransactionDataAPI,
38 },
39};
40use move_binary_format::CompiledModule;
41use move_bytecode_utils::module_cache::GetModule;
42use move_core_types::{
43 account_address::AccountAddress, annotated_value::MoveTypeLayout, language_storage::ModuleId,
44};
45use schemars::JsonSchema;
46use serde::{Deserialize, Serialize};
47use serde_with::{DisplayFromStr, serde_as};
48use strum::{Display, EnumString};
49use tabled::{
50 builder::Builder as TableBuilder,
51 settings::{Panel as TablePanel, Style as TableStyle, style::HorizontalLine},
52};
53
54use crate::{
55 Filter, IotaEvent, IotaEventID, IotaMoveValue, ObjectRefSchema, Page,
56 balance_changes::BalanceChange,
57 iota_gas_cost_summary::IotaGasCostSummary,
58 iota_owner::OwnerSchema,
59 iota_primitives::{
60 Base58 as Base58Schema, Base64 as Base64Schema, GenericSignature as GenericSignatureSchema,
61 IotaAddress as IotaAddressSchema, ObjectId as ObjectIdSchema,
62 SequenceNumberString as SequenceNumberStringSchema,
63 SequenceNumberU64 as SequenceNumberU64Schema, TypeTag as TypeTagSchema,
64 },
65 object_changes::ObjectChange,
66};
67
68pub type IotaEpochId = BigInt<u64>;
70
71#[derive(Debug, Serialize, Deserialize, JsonSchema)]
72pub enum ExecuteTransactionRequestType {
73 WaitForEffectsCert,
74 WaitForLocalExecution,
75}
76
77impl From<NativeExecuteTransactionRequestType> for ExecuteTransactionRequestType {
78 fn from(request_type: NativeExecuteTransactionRequestType) -> Self {
79 match request_type {
80 NativeExecuteTransactionRequestType::WaitForEffectsCert => Self::WaitForEffectsCert,
81 NativeExecuteTransactionRequestType::WaitForLocalExecution => {
82 Self::WaitForLocalExecution
83 }
84 }
85 }
86}
87
88impl From<ExecuteTransactionRequestType> for NativeExecuteTransactionRequestType {
89 fn from(request_type: ExecuteTransactionRequestType) -> Self {
90 match request_type {
91 ExecuteTransactionRequestType::WaitForEffectsCert => Self::WaitForEffectsCert,
92 ExecuteTransactionRequestType::WaitForLocalExecution => Self::WaitForLocalExecution,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
98#[serde(
99 rename_all = "camelCase",
100 rename = "TransactionBlockResponseQuery",
101 default
102)]
103pub struct IotaTransactionBlockResponseQuery {
104 pub filter: Option<TransactionFilter>,
106 pub options: Option<IotaTransactionBlockResponseOptions>,
109}
110
111impl IotaTransactionBlockResponseQuery {
112 pub fn new(
113 filter: Option<TransactionFilter>,
114 options: Option<IotaTransactionBlockResponseOptions>,
115 ) -> Self {
116 Self { filter, options }
117 }
118
119 pub fn new_with_filter(filter: TransactionFilter) -> Self {
120 Self {
121 filter: Some(filter),
122 options: None,
123 }
124 }
125}
126
127#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
128#[serde(
129 rename_all = "camelCase",
130 rename = "TransactionBlockResponseQuery",
131 default
132)]
133pub struct IotaTransactionBlockResponseQueryV2 {
134 pub filter: Option<TransactionFilterV2>,
136 pub options: Option<IotaTransactionBlockResponseOptions>,
139}
140
141impl IotaTransactionBlockResponseQueryV2 {
142 pub fn new(
143 filter: Option<TransactionFilterV2>,
144 options: Option<IotaTransactionBlockResponseOptions>,
145 ) -> Self {
146 Self { filter, options }
147 }
148
149 pub fn new_with_filter(filter: TransactionFilterV2) -> Self {
150 Self {
151 filter: Some(filter),
152 options: None,
153 }
154 }
155}
156
157pub type TransactionBlocksPage = Page<IotaTransactionBlockResponse, TransactionDigest>;
158
159#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Eq, PartialEq, Default)]
160#[serde(
161 rename_all = "camelCase",
162 rename = "TransactionBlockResponseOptions",
163 default
164)]
165pub struct IotaTransactionBlockResponseOptions {
166 pub show_input: bool,
168 pub show_raw_input: bool,
170 pub show_effects: bool,
172 pub show_events: bool,
174 pub show_object_changes: bool,
176 pub show_balance_changes: bool,
178 pub show_raw_effects: bool,
180}
181
182impl IotaTransactionBlockResponseOptions {
183 pub fn new() -> Self {
184 Self::default()
185 }
186
187 pub fn full_content() -> Self {
188 Self {
189 show_effects: true,
190 show_input: true,
191 show_raw_input: true,
192 show_events: true,
193 show_object_changes: true,
194 show_balance_changes: true,
195 show_raw_effects: false,
198 }
199 }
200
201 pub fn with_input(mut self) -> Self {
202 self.show_input = true;
203 self
204 }
205
206 pub fn with_raw_input(mut self) -> Self {
207 self.show_raw_input = true;
208 self
209 }
210
211 pub fn with_effects(mut self) -> Self {
212 self.show_effects = true;
213 self
214 }
215
216 pub fn with_events(mut self) -> Self {
217 self.show_events = true;
218 self
219 }
220
221 pub fn with_balance_changes(mut self) -> Self {
222 self.show_balance_changes = true;
223 self
224 }
225
226 pub fn with_object_changes(mut self) -> Self {
227 self.show_object_changes = true;
228 self
229 }
230
231 pub fn with_raw_effects(mut self) -> Self {
232 self.show_raw_effects = true;
233 self
234 }
235
236 pub fn default_execution_request_type(&self) -> NativeExecuteTransactionRequestType {
239 if self.require_effects() {
242 NativeExecuteTransactionRequestType::WaitForLocalExecution
243 } else {
244 NativeExecuteTransactionRequestType::WaitForEffectsCert
245 }
246 }
247
248 pub fn require_input(&self) -> bool {
249 self.show_input || self.show_raw_input || self.show_object_changes
250 }
251
252 pub fn require_effects(&self) -> bool {
253 self.show_effects
254 || self.show_events
255 || self.show_balance_changes
256 || self.show_object_changes
257 || self.show_raw_effects
258 }
259
260 pub fn only_digest(&self) -> bool {
261 self == &Self::default()
262 }
263}
264
265#[serde_as]
266#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
267#[serde(rename_all = "camelCase", rename = "TransactionBlockResponse")]
268pub struct IotaTransactionBlockResponse {
269 #[serde_as(as = "Base58Schema")]
270 #[schemars(with = "Base58Schema")]
271 pub digest: TransactionDigest,
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub transaction: Option<IotaTransactionBlock>,
275 #[serde_as(as = "Base64")]
278 #[schemars(with = "Base64Schema")]
279 #[serde(skip_serializing_if = "Vec::is_empty", default)]
280 pub raw_transaction: Vec<u8>,
281 #[serde(skip_serializing_if = "Option::is_none")]
282 pub effects: Option<IotaTransactionBlockEffects>,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub events: Option<IotaTransactionBlockEvents>,
285 #[serde(skip_serializing_if = "Option::is_none")]
286 pub object_changes: Option<Vec<ObjectChange>>,
287 #[serde(skip_serializing_if = "Option::is_none")]
288 pub balance_changes: Option<Vec<BalanceChange>>,
289 #[serde(default, skip_serializing_if = "Option::is_none")]
290 #[schemars(with = "Option<String>")]
291 #[serde_as(as = "Option<DisplayFromStr>")]
292 pub timestamp_ms: Option<u64>,
293 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub confirmed_local_execution: Option<bool>,
295 #[schemars(with = "Option<String>")]
299 #[serde_as(as = "Option<DisplayFromStr>")]
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub checkpoint: Option<CheckpointSequenceNumber>,
302 #[serde(skip_serializing_if = "Vec::is_empty", default)]
303 pub errors: Vec<String>,
304 #[serde(skip_serializing_if = "Vec::is_empty", default)]
305 pub raw_effects: Vec<u8>,
306}
307
308impl IotaTransactionBlockResponse {
309 pub fn new(digest: TransactionDigest) -> Self {
310 Self {
311 digest,
312 ..Default::default()
313 }
314 }
315
316 pub fn status_ok(&self) -> Option<bool> {
317 self.effects.as_ref().map(|e| e.status().is_ok())
318 }
319
320 pub fn mutated_objects(&self) -> impl Iterator<Item = ObjectRef> + '_ {
322 self.object_changes.iter().flat_map(|obj_changes| {
323 obj_changes
324 .iter()
325 .filter(|change| matches!(change, ObjectChange::Mutated { .. }))
326 .map(|change| change.object_ref())
327 })
328 }
329}
330
331impl PartialEq for IotaTransactionBlockResponse {
333 fn eq(&self, other: &Self) -> bool {
334 self.transaction == other.transaction
335 && self.effects == other.effects
336 && self.timestamp_ms == other.timestamp_ms
337 && self.confirmed_local_execution == other.confirmed_local_execution
338 && self.checkpoint == other.checkpoint
339 }
340}
341
342impl Display for IotaTransactionBlockResponse {
343 fn fmt(&self, writer: &mut Formatter<'_>) -> fmt::Result {
344 writeln!(writer, "Transaction Digest: {}", &self.digest)?;
345
346 if let Some(t) = &self.transaction {
347 writeln!(writer, "{t}")?;
348 }
349
350 if let Some(e) = &self.effects {
351 writeln!(writer, "{e}")?;
352 }
353
354 if let Some(e) = &self.events {
355 writeln!(writer, "{e}")?;
356 }
357
358 if let Some(object_changes) = &self.object_changes {
359 let mut builder = TableBuilder::default();
360 let (
361 mut created,
362 mut deleted,
363 mut mutated,
364 mut published,
365 mut transferred,
366 mut wrapped,
367 mut unwrapped,
368 ) = (vec![], vec![], vec![], vec![], vec![], vec![], vec![]);
369
370 for obj in object_changes {
371 match obj {
372 ObjectChange::Created { .. } => created.push(obj),
373 ObjectChange::Deleted { .. } => deleted.push(obj),
374 ObjectChange::Mutated { .. } => mutated.push(obj),
375 ObjectChange::Published { .. } => published.push(obj),
376 ObjectChange::Transferred { .. } => transferred.push(obj),
377 ObjectChange::Wrapped { .. } => wrapped.push(obj),
378 ObjectChange::Unwrapped { .. } => unwrapped.push(obj),
379 };
380 }
381
382 write_obj_changes(created, "Created", &mut builder)?;
383 write_obj_changes(deleted, "Deleted", &mut builder)?;
384 write_obj_changes(mutated, "Mutated", &mut builder)?;
385 write_obj_changes(published, "Published", &mut builder)?;
386 write_obj_changes(transferred, "Transferred", &mut builder)?;
387 write_obj_changes(wrapped, "Wrapped", &mut builder)?;
388 write_obj_changes(unwrapped, "Unwrapped", &mut builder)?;
389
390 let mut table = builder.build();
391 table.with(TablePanel::header("Object Changes"));
392 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
393 1,
394 TableStyle::modern().get_horizontal(),
395 )]));
396 writeln!(writer, "{table}")?;
397 }
398
399 if let Some(balance_changes) = &self.balance_changes {
400 if !balance_changes.is_empty() {
404 let mut builder = TableBuilder::default();
405 for balance in balance_changes {
406 builder.push_record(vec![format!("{balance}")]);
407 }
408 let mut table = builder.build();
409 table.with(TablePanel::header("Balance Changes"));
410 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
411 1,
412 TableStyle::modern().get_horizontal(),
413 )]));
414 writeln!(writer, "{table}")?;
415 } else {
416 writeln!(writer, "╭────────────────────╮")?;
417 writeln!(writer, "│ No balance changes │")?;
418 writeln!(writer, "╰────────────────────╯")?;
419 }
420 }
421 Ok(())
422 }
423}
424
425fn write_obj_changes<T: Display>(
426 values: Vec<T>,
427 output_string: &str,
428 builder: &mut TableBuilder,
429) -> std::fmt::Result {
430 if !values.is_empty() {
431 builder.push_record(vec![format!("{output_string} Objects: ")]);
432 for obj in values {
433 builder.push_record(vec![format!("{obj}")]);
434 }
435 }
436 Ok(())
437}
438
439pub fn get_new_package_obj_from_response(
440 response: &IotaTransactionBlockResponse,
441) -> Option<ObjectRef> {
442 response.object_changes.as_ref().and_then(|changes| {
443 changes
444 .iter()
445 .find(|change| matches!(change, ObjectChange::Published { .. }))
446 .map(|change| change.object_ref())
447 })
448}
449
450pub fn get_new_package_upgrade_cap_from_response(
451 response: &IotaTransactionBlockResponse,
452) -> Option<ObjectRef> {
453 response.object_changes.as_ref().and_then(|changes| {
454 changes
455 .iter()
456 .find(|change| {
457 matches!(change, ObjectChange::Created {
458 owner: Owner::Address(_),
459 object_type,
460 ..
461 } if object_type.is_upgrade_cap())
462 })
463 .map(|change| change.object_ref())
464 })
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
468#[serde(rename = "TransactionBlockKind", tag = "kind")]
469pub enum IotaTransactionBlockKind {
470 Genesis(IotaGenesisTransaction),
473 ConsensusCommitPrologueV1(IotaConsensusCommitPrologueV1),
476 ProgrammableTransaction(IotaProgrammableTransactionBlock),
479 RandomnessStateUpdate(IotaRandomnessStateUpdate),
481 EndOfEpochTransaction(IotaEndOfEpochTransaction),
483 }
485
486impl Display for IotaTransactionBlockKind {
487 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
488 let mut writer = String::new();
489 match &self {
490 Self::Genesis(_) => {
491 writeln!(writer, "Transaction Kind: Genesis Transaction")?;
492 }
493 Self::ConsensusCommitPrologueV1(p) => {
494 writeln!(writer, "Transaction Kind: Consensus Commit Prologue V1")?;
495 writeln!(
496 writer,
497 "Epoch: {}, Round: {}, SubDagIndex: {:?}, Timestamp: {}, ConsensusCommitDigest: {}",
498 p.epoch,
499 p.round,
500 p.sub_dag_index,
501 p.commit_timestamp_ms,
502 p.consensus_commit_digest
503 )?;
504 }
505 Self::ProgrammableTransaction(p) => {
506 write!(writer, "Transaction Kind: Programmable")?;
507 write!(writer, "{}", crate::displays::Pretty(p))?;
508 }
509 Self::RandomnessStateUpdate(_) => {
510 writeln!(writer, "Transaction Kind: Randomness State Update")?;
511 }
512 Self::EndOfEpochTransaction(_) => {
513 writeln!(writer, "Transaction Kind: End of Epoch Transaction")?;
514 }
515 }
516 write!(f, "{writer}")
517 }
518}
519
520impl IotaTransactionBlockKind {
521 fn try_from_inner(
522 tx: TransactionKind,
523 tx_digest: TransactionDigest,
524 ) -> Result<Self, anyhow::Error> {
525 match tx {
526 TransactionKind::Genesis(g) => Ok(Self::Genesis(IotaGenesisTransaction {
527 objects: g.objects.iter().map(GenesisObject::id).collect(),
528 events: g
529 .events
530 .into_iter()
531 .enumerate()
532 .map(|(seq, _event)| EventID::from((tx_digest, seq as u64)))
533 .collect(),
534 })),
535 TransactionKind::ConsensusCommitPrologueV1(p) => Ok(Self::ConsensusCommitPrologueV1(
536 IotaConsensusCommitPrologueV1 {
537 epoch: p.epoch,
538 round: p.round,
539 sub_dag_index: p.sub_dag_index,
540 commit_timestamp_ms: p.commit_timestamp_ms,
541 consensus_commit_digest: p.consensus_commit_digest,
542 consensus_determined_version_assignments: p
543 .consensus_determined_version_assignments
544 .into(),
545 },
546 )),
547 TransactionKind::Programmable(_) => {
548 Err(anyhow::anyhow!(
550 "ProgrammableTransaction must be handled by the caller, not try_from_inner"
551 ))
552 }
553 #[allow(deprecated)]
554 TransactionKind::AuthenticatorStateUpdateV1Deprecated => {
555 Err(anyhow::anyhow!(
559 "AuthenticatorStateUpdateV1 transactions are deprecated and were never created on IOTA"
560 ))
561 }
562 TransactionKind::RandomnessStateUpdate(update) => {
563 Ok(Self::RandomnessStateUpdate(IotaRandomnessStateUpdate {
564 epoch: update.epoch,
565 randomness_round: update.randomness_round.value(),
566 random_bytes: update.random_bytes,
567 }))
568 }
569 TransactionKind::EndOfEpoch(end_of_epoch_tx) => {
570 Ok(Self::EndOfEpochTransaction(IotaEndOfEpochTransaction {
571 transactions: end_of_epoch_tx
572 .into_iter()
573 .map(|tx| match tx {
574 EndOfEpochTransactionKind::ChangeEpoch(e) => {
575 IotaEndOfEpochTransactionKind::ChangeEpoch(e.into())
576 }
577 EndOfEpochTransactionKind::ChangeEpochV2(e) => {
578 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
579 }
580 EndOfEpochTransactionKind::ChangeEpochV3(e) => {
581 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
582 }
583 EndOfEpochTransactionKind::ChangeEpochV4(e) => {
584 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
585 }
586 _ => unimplemented!(
587 "a new EndOfEpochTransactionKind enum variant was added and needs to be handled"
588 ),
589 })
590 .collect(),
591 }))
592 }
593 _ => unimplemented!(
594 "a new TransactionKind enum variant was added and needs to be handled"
595 )
596 }
597 }
598
599 fn try_from_with_module_cache(
600 tx: TransactionKind,
601 module_cache: &impl GetModule,
602 tx_digest: TransactionDigest,
603 ) -> Result<Self, anyhow::Error> {
604 match tx {
605 TransactionKind::Programmable(p) => Ok(Self::ProgrammableTransaction(
606 IotaProgrammableTransactionBlock::try_from_with_module_cache(p, module_cache)?,
607 )),
608 tx => Self::try_from_inner(tx, tx_digest),
609 }
610 }
611
612 async fn try_from_with_package_resolver(
613 tx: TransactionKind,
614 package_resolver: &Resolver<impl PackageStore>,
615 tx_digest: TransactionDigest,
616 ) -> Result<Self, anyhow::Error> {
617 match tx {
618 TransactionKind::Programmable(p) => Ok(Self::ProgrammableTransaction(
619 IotaProgrammableTransactionBlock::try_from_with_package_resolver(
620 p,
621 package_resolver,
622 )
623 .await?,
624 )),
625 tx => Self::try_from_inner(tx, tx_digest),
626 }
627 }
628
629 pub fn transaction_count(&self) -> usize {
630 match self {
631 Self::ProgrammableTransaction(p) => p.commands.len(),
632 _ => 1,
633 }
634 }
635
636 pub fn name(&self) -> &'static str {
637 match self {
638 Self::Genesis(_) => "Genesis",
639 Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
640 Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
641 Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
642 Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
643 }
644 }
645}
646
647#[serde_as]
648#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
649pub struct IotaChangeEpoch {
650 #[schemars(with = "String")]
651 #[serde_as(as = "DisplayFromStr")]
652 pub epoch: EpochId,
653 #[schemars(with = "String")]
654 #[serde_as(as = "DisplayFromStr")]
655 pub storage_charge: u64,
656 #[schemars(with = "String")]
657 #[serde_as(as = "DisplayFromStr")]
658 pub computation_charge: u64,
659 #[schemars(with = "String")]
660 #[serde_as(as = "DisplayFromStr")]
661 pub storage_rebate: u64,
662 #[schemars(with = "String")]
663 #[serde_as(as = "DisplayFromStr")]
664 pub epoch_start_timestamp_ms: u64,
665}
666
667impl From<ChangeEpoch> for IotaChangeEpoch {
668 fn from(e: ChangeEpoch) -> Self {
669 Self {
670 epoch: e.epoch,
671 storage_charge: e.storage_charge,
672 computation_charge: e.computation_charge,
673 storage_rebate: e.storage_rebate,
674 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
675 }
676 }
677}
678
679#[serde_as]
680#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
681pub struct IotaChangeEpochV2 {
682 #[schemars(with = "String")]
683 #[serde_as(as = "DisplayFromStr")]
684 pub epoch: EpochId,
685 #[schemars(with = "String")]
686 #[serde_as(as = "DisplayFromStr")]
687 pub storage_charge: u64,
688 #[schemars(with = "String")]
689 #[serde_as(as = "DisplayFromStr")]
690 pub computation_charge: u64,
691 #[schemars(with = "String")]
692 #[serde_as(as = "DisplayFromStr")]
693 pub computation_charge_burned: u64,
694 #[schemars(with = "String")]
695 #[serde_as(as = "DisplayFromStr")]
696 pub storage_rebate: u64,
697 #[schemars(with = "String")]
698 #[serde_as(as = "DisplayFromStr")]
699 pub epoch_start_timestamp_ms: u64,
700 #[schemars(with = "Option<Vec<String>>")]
701 #[serde_as(as = "Option<Vec<DisplayFromStr>>")]
702 #[serde(skip_serializing_if = "Option::is_none", default)]
703 pub eligible_active_validators: Option<Vec<u64>>,
704 #[schemars(with = "Option<Vec<String>>")]
705 #[serde_as(as = "Option<Vec<DisplayFromStr>>")]
706 #[serde(skip_serializing_if = "Option::is_none", default)]
707 pub scores: Option<Vec<u64>>,
708}
709
710impl From<ChangeEpochV2> for IotaChangeEpochV2 {
711 fn from(e: ChangeEpochV2) -> Self {
712 Self {
713 epoch: e.epoch,
714 storage_charge: e.storage_charge,
715 computation_charge: e.computation_charge,
716 computation_charge_burned: e.computation_charge_burned,
717 storage_rebate: e.storage_rebate,
718 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
719 eligible_active_validators: None,
720 scores: None,
721 }
722 }
723}
724
725impl From<ChangeEpochV3> for IotaChangeEpochV2 {
726 fn from(e: ChangeEpochV3) -> Self {
727 Self {
728 epoch: e.epoch,
729 storage_charge: e.storage_charge,
730 computation_charge: e.computation_charge,
731 computation_charge_burned: e.computation_charge_burned,
732 storage_rebate: e.storage_rebate,
733 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
734 eligible_active_validators: Some(e.eligible_active_validators),
735 scores: None,
736 }
737 }
738}
739
740impl From<ChangeEpochV4> for IotaChangeEpochV2 {
741 fn from(e: ChangeEpochV4) -> Self {
742 Self {
743 epoch: e.epoch,
744 storage_charge: e.storage_charge,
745 computation_charge: e.computation_charge,
746 computation_charge_burned: e.computation_charge_burned,
747 storage_rebate: e.storage_rebate,
748 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
749 eligible_active_validators: Some(e.eligible_active_validators),
750 scores: Some(e.scores),
751 }
752 }
753}
754
755#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
756#[enum_dispatch(IotaTransactionBlockEffectsAPI)]
757#[serde(
758 rename = "TransactionBlockEffects",
759 rename_all = "camelCase",
760 tag = "messageVersion"
761)]
762pub enum IotaTransactionBlockEffects {
763 V1(IotaTransactionBlockEffectsV1),
764}
765
766#[enum_dispatch]
767pub trait IotaTransactionBlockEffectsAPI {
768 fn status(&self) -> &IotaExecutionStatus;
769 fn into_status(self) -> IotaExecutionStatus;
770 fn shared_objects(&self) -> &[ObjectRef];
771 fn created(&self) -> &[OwnedObjectRef];
772 fn mutated(&self) -> &[OwnedObjectRef];
773 fn unwrapped(&self) -> &[OwnedObjectRef];
774 fn deleted(&self) -> &[ObjectRef];
775 fn unwrapped_then_deleted(&self) -> &[ObjectRef];
776 fn wrapped(&self) -> &[ObjectRef];
777 fn gas_object(&self) -> &OwnedObjectRef;
778 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
779 fn dependencies(&self) -> &[TransactionDigest];
780 fn executed_epoch(&self) -> EpochId;
781 fn transaction_digest(&self) -> &TransactionDigest;
782 fn gas_cost_summary(&self) -> &GasCostSummary;
783
784 fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef>;
786 fn modified_at_versions(&self) -> Vec<(ObjectId, SequenceNumber)>;
787 fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>;
788 fn all_deleted_objects(&self) -> Vec<(&ObjectRef, DeleteKind)>;
789}
790
791#[serde_as]
792#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
793#[serde(
794 rename = "TransactionBlockEffectsModifiedAtVersions",
795 rename_all = "camelCase"
796)]
797pub struct IotaTransactionBlockEffectsModifiedAtVersions {
798 #[serde_as(as = "ObjectIdSchema")]
799 #[schemars(with = "ObjectIdSchema")]
800 object_id: ObjectId,
801 #[schemars(with = "SequenceNumberStringSchema")]
802 #[serde_as(as = "SequenceNumberStringSchema")]
803 sequence_number: SequenceNumber,
804}
805
806#[serde_as]
808#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
809#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")]
810pub struct IotaTransactionBlockEffectsV1 {
811 pub status: IotaExecutionStatus,
813 #[schemars(with = "String")]
815 #[serde_as(as = "DisplayFromStr")]
816 pub executed_epoch: EpochId,
817 #[schemars(with = "IotaGasCostSummary")]
818 #[serde_as(as = "IotaGasCostSummary")]
819 pub gas_used: GasCostSummary,
820 #[serde(default, skip_serializing_if = "Vec::is_empty")]
823 pub modified_at_versions: Vec<IotaTransactionBlockEffectsModifiedAtVersions>,
824 #[serde(default, skip_serializing_if = "Vec::is_empty")]
827 #[schemars(with = "Vec<ObjectRefSchema>")]
828 #[serde_as(as = "Vec<ObjectRefSchema>")]
829 pub shared_objects: Vec<ObjectRef>,
830 #[serde_as(as = "Base58Schema")]
832 #[schemars(with = "Base58Schema")]
833 pub transaction_digest: TransactionDigest,
834 #[serde(default, skip_serializing_if = "Vec::is_empty")]
836 pub created: Vec<OwnedObjectRef>,
837 #[serde(default, skip_serializing_if = "Vec::is_empty")]
839 pub mutated: Vec<OwnedObjectRef>,
840 #[serde(default, skip_serializing_if = "Vec::is_empty")]
844 pub unwrapped: Vec<OwnedObjectRef>,
845 #[serde(default, skip_serializing_if = "Vec::is_empty")]
847 #[schemars(with = "Vec<ObjectRefSchema>")]
848 #[serde_as(as = "Vec<ObjectRefSchema>")]
849 pub deleted: Vec<ObjectRef>,
850 #[serde(default, skip_serializing_if = "Vec::is_empty")]
853 #[schemars(with = "Vec<ObjectRefSchema>")]
854 #[serde_as(as = "Vec<ObjectRefSchema>")]
855 pub unwrapped_then_deleted: Vec<ObjectRef>,
856 #[serde(default, skip_serializing_if = "Vec::is_empty")]
858 #[schemars(with = "Vec<ObjectRefSchema>")]
859 #[serde_as(as = "Vec<ObjectRefSchema>")]
860 pub wrapped: Vec<ObjectRef>,
861 pub gas_object: OwnedObjectRef,
864 #[serde(skip_serializing_if = "Option::is_none")]
867 #[serde_as(as = "Option<Base58Schema>")]
868 #[schemars(with = "Option<Base58Schema>")]
869 pub events_digest: Option<TransactionEventsDigest>,
870 #[serde(default, skip_serializing_if = "Vec::is_empty")]
872 #[serde_as(as = "Vec<Base58Schema>")]
873 #[schemars(with = "Vec<Base58Schema>")]
874 pub dependencies: Vec<TransactionDigest>,
875}
876
877impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 {
878 fn status(&self) -> &IotaExecutionStatus {
879 &self.status
880 }
881 fn into_status(self) -> IotaExecutionStatus {
882 self.status
883 }
884 fn shared_objects(&self) -> &[ObjectRef] {
885 &self.shared_objects
886 }
887 fn created(&self) -> &[OwnedObjectRef] {
888 &self.created
889 }
890 fn mutated(&self) -> &[OwnedObjectRef] {
891 &self.mutated
892 }
893 fn unwrapped(&self) -> &[OwnedObjectRef] {
894 &self.unwrapped
895 }
896 fn deleted(&self) -> &[ObjectRef] {
897 &self.deleted
898 }
899 fn unwrapped_then_deleted(&self) -> &[ObjectRef] {
900 &self.unwrapped_then_deleted
901 }
902 fn wrapped(&self) -> &[ObjectRef] {
903 &self.wrapped
904 }
905 fn gas_object(&self) -> &OwnedObjectRef {
906 &self.gas_object
907 }
908 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
909 self.events_digest.as_ref()
910 }
911 fn dependencies(&self) -> &[TransactionDigest] {
912 &self.dependencies
913 }
914
915 fn executed_epoch(&self) -> EpochId {
916 self.executed_epoch
917 }
918
919 fn transaction_digest(&self) -> &TransactionDigest {
920 &self.transaction_digest
921 }
922
923 fn gas_cost_summary(&self) -> &GasCostSummary {
924 &self.gas_used
925 }
926
927 fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef> {
928 self.mutated
929 .iter()
930 .filter(|o| *o != &self.gas_object)
931 .cloned()
932 .collect()
933 }
934
935 fn modified_at_versions(&self) -> Vec<(ObjectId, SequenceNumber)> {
936 self.modified_at_versions
937 .iter()
938 .map(|v| (v.object_id, v.sequence_number))
939 .collect::<Vec<_>>()
940 }
941
942 fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> {
943 self.mutated
944 .iter()
945 .map(|owner_ref| (owner_ref, WriteKind::Mutate))
946 .chain(
947 self.created
948 .iter()
949 .map(|owner_ref| (owner_ref, WriteKind::Create)),
950 )
951 .chain(
952 self.unwrapped
953 .iter()
954 .map(|owner_ref| (owner_ref, WriteKind::Unwrap)),
955 )
956 .collect()
957 }
958
959 fn all_deleted_objects(&self) -> Vec<(&ObjectRef, DeleteKind)> {
960 self.deleted
961 .iter()
962 .map(|r| (r, DeleteKind::Normal))
963 .chain(
964 self.unwrapped_then_deleted
965 .iter()
966 .map(|r| (r, DeleteKind::UnwrapThenDelete)),
967 )
968 .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap)))
969 .collect()
970 }
971}
972
973impl IotaTransactionBlockEffects {
974 pub fn new_for_testing(
975 transaction_digest: TransactionDigest,
976 status: IotaExecutionStatus,
977 ) -> Self {
978 Self::V1(IotaTransactionBlockEffectsV1 {
979 transaction_digest,
980 status,
981 gas_object: OwnedObjectRef {
982 owner: Owner::Address(IotaAddress::random()),
983 reference: iota_types::base_types::random_object_ref(),
984 },
985 executed_epoch: 0,
986 modified_at_versions: vec![],
987 gas_used: GasCostSummary::default(),
988 shared_objects: vec![],
989 created: vec![],
990 mutated: vec![],
991 unwrapped: vec![],
992 deleted: vec![],
993 unwrapped_then_deleted: vec![],
994 wrapped: vec![],
995 events_digest: None,
996 dependencies: vec![],
997 })
998 }
999
1000 pub async fn from_native_with_clever_error<S: PackageStore>(
1006 native: TransactionEffects,
1007 resolver: &Resolver<S>,
1008 ) -> Self {
1009 let clever_status =
1010 IotaExecutionStatus::from_native_with_clever_error(native.status().clone(), resolver)
1011 .await;
1012 match native {
1013 TransactionEffects::V1(inner) => {
1014 let mut inner = IotaTransactionBlockEffectsV1::from(*inner);
1015 inner.status = clever_status;
1016 inner.into()
1017 }
1018 _ => unimplemented!(
1019 "a new TransactionEffects enum variant was added and needs to be handled"
1020 ),
1021 }
1022 }
1023}
1024
1025impl TryFrom<TransactionEffects> for IotaTransactionBlockEffects {
1026 type Error = IotaError;
1027
1028 fn try_from(native: TransactionEffects) -> Result<Self, Self::Error> {
1029 Ok(IotaTransactionBlockEffects::V1(native.into()))
1030 }
1031}
1032
1033impl<T: TransactionEffectsAPI> From<T> for IotaTransactionBlockEffectsV1 {
1034 fn from(native: T) -> Self {
1035 Self {
1036 status: native.status().clone().into(),
1037 executed_epoch: native.epoch(),
1038 modified_at_versions: native
1039 .modified_at_versions()
1040 .into_iter()
1041 .map(
1042 |(object_id, sequence_number)| IotaTransactionBlockEffectsModifiedAtVersions {
1043 object_id,
1044 sequence_number,
1045 },
1046 )
1047 .collect(),
1048 gas_used: native.gas_cost_summary().clone(),
1049 shared_objects: native
1050 .input_shared_objects()
1051 .into_iter()
1052 .map(|kind| kind.object_ref())
1053 .collect(),
1054 transaction_digest: *native.transaction_digest(),
1055 created: to_owned_ref(native.created()),
1056 mutated: to_owned_ref(native.mutated().to_vec()),
1057 unwrapped: to_owned_ref(native.unwrapped().to_vec()),
1058 deleted: native.deleted().to_vec(),
1059 unwrapped_then_deleted: native.unwrapped_then_deleted().to_vec(),
1060 wrapped: native.wrapped().to_vec(),
1061 gas_object: OwnedObjectRef {
1062 owner: native.gas_object().1,
1063 reference: native.gas_object().0,
1064 },
1065 events_digest: native.events_digest().copied(),
1066 dependencies: native.dependencies().to_vec(),
1067 }
1068 }
1069}
1070
1071fn owned_objref_string(obj: &OwnedObjectRef) -> String {
1072 format!(
1073 " ┌──\n │ ID: {} \n │ Owner: {} \n │ Version: {} \n │ Digest: {}\n └──",
1074 obj.reference.object_id, obj.owner, obj.reference.version, obj.reference.digest
1075 )
1076}
1077
1078fn objref_string(obj: &ObjectRef) -> String {
1079 format!(
1080 " ┌──\n │ ID: {} \n │ Version: {} \n │ Digest: {}\n └──",
1081 obj.object_id, obj.version, obj.digest
1082 )
1083}
1084
1085impl Display for IotaTransactionBlockEffects {
1086 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1087 let mut builder = TableBuilder::default();
1088
1089 builder.push_record(vec![format!("Digest: {}", self.transaction_digest())]);
1090 builder.push_record(vec![format!("Status: {:?}", self.status())]);
1091 builder.push_record(vec![format!("Executed Epoch: {}", self.executed_epoch())]);
1092
1093 if !self.created().is_empty() {
1094 builder.push_record(vec![format!("\nCreated Objects: ")]);
1095
1096 for oref in self.created() {
1097 builder.push_record(vec![owned_objref_string(oref)]);
1098 }
1099 }
1100
1101 if !self.mutated().is_empty() {
1102 builder.push_record(vec![format!("Mutated Objects: ")]);
1103 for oref in self.mutated() {
1104 builder.push_record(vec![owned_objref_string(oref)]);
1105 }
1106 }
1107
1108 if !self.shared_objects().is_empty() {
1109 builder.push_record(vec![format!("Shared Objects: ")]);
1110 for oref in self.shared_objects() {
1111 builder.push_record(vec![objref_string(oref)]);
1112 }
1113 }
1114
1115 if !self.deleted().is_empty() {
1116 builder.push_record(vec![format!("Deleted Objects: ")]);
1117
1118 for oref in self.deleted() {
1119 builder.push_record(vec![objref_string(oref)]);
1120 }
1121 }
1122
1123 if !self.wrapped().is_empty() {
1124 builder.push_record(vec![format!("Wrapped Objects: ")]);
1125
1126 for oref in self.wrapped() {
1127 builder.push_record(vec![objref_string(oref)]);
1128 }
1129 }
1130
1131 if !self.unwrapped().is_empty() {
1132 builder.push_record(vec![format!("Unwrapped Objects: ")]);
1133 for oref in self.unwrapped() {
1134 builder.push_record(vec![owned_objref_string(oref)]);
1135 }
1136 }
1137
1138 builder.push_record(vec![format!(
1139 "Gas Object: \n{}",
1140 owned_objref_string(self.gas_object())
1141 )]);
1142
1143 let gas_cost_summary = self.gas_cost_summary();
1144 builder.push_record(vec![format!(
1145 "Gas Cost Summary:\n \
1146 Storage Cost: {} NANOS\n \
1147 Computation Cost: {} NANOS\n \
1148 Computation Cost Burned: {} NANOS\n \
1149 Storage Rebate: {} NANOS\n \
1150 Non-refundable Storage Fee: {} NANOS",
1151 gas_cost_summary.storage_cost,
1152 gas_cost_summary.computation_cost,
1153 gas_cost_summary.computation_cost_burned,
1154 gas_cost_summary.storage_rebate,
1155 gas_cost_summary.non_refundable_storage_fee,
1156 )]);
1157
1158 let dependencies = self.dependencies();
1159 if !dependencies.is_empty() {
1160 builder.push_record(vec![format!("\nTransaction Dependencies:")]);
1161 for dependency in dependencies {
1162 builder.push_record(vec![format!(" {dependency}")]);
1163 }
1164 }
1165
1166 let mut table = builder.build();
1167 table.with(TablePanel::header("Transaction Effects"));
1168 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1169 1,
1170 TableStyle::modern().get_horizontal(),
1171 )]));
1172 write!(f, "{table}")
1173 }
1174}
1175
1176#[serde_as]
1177#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1178#[serde(rename_all = "camelCase")]
1179pub struct DryRunTransactionBlockResponse {
1180 pub effects: IotaTransactionBlockEffects,
1181 pub events: IotaTransactionBlockEvents,
1182 pub object_changes: Vec<ObjectChange>,
1183 pub balance_changes: Vec<BalanceChange>,
1184 pub input: IotaTransactionBlockData,
1185 #[serde(default, skip_serializing_if = "Option::is_none")]
1187 #[schemars(with = "Option<String>")]
1188 #[serde_as(as = "Option<DisplayFromStr>")]
1189 pub suggested_gas_price: Option<u64>,
1190 pub execution_error_source: Option<String>,
1191}
1192
1193#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1194#[serde(rename = "TransactionBlockEvents", transparent)]
1195pub struct IotaTransactionBlockEvents {
1196 pub data: Vec<IotaEvent>,
1197}
1198
1199impl IotaTransactionBlockEvents {
1200 pub fn try_from(
1201 mut events: TransactionEvents,
1202 tx_digest: TransactionDigest,
1203 timestamp_ms: Option<u64>,
1204 resolver: &mut dyn LayoutResolver,
1205 ) -> IotaResult<Self> {
1206 Ok(Self {
1207 data: events
1208 .drain(..)
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 mut 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 .drain(..)
1229 .enumerate()
1230 .map(|(seq, event)| {
1231 let layout = get_layout_from_struct_tag(event.type_.clone(), resolver)?;
1232 IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1233 })
1234 .collect::<Result<_, _>>()?,
1235 })
1236 }
1237}
1238
1239impl Display for IotaTransactionBlockEvents {
1240 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1241 if self.data.is_empty() {
1242 writeln!(f, "╭─────────────────────────────╮")?;
1243 writeln!(f, "│ No transaction block events │")?;
1244 writeln!(f, "╰─────────────────────────────╯")
1245 } else {
1246 let mut builder = TableBuilder::default();
1247
1248 for event in &self.data {
1249 builder.push_record(vec![format!("{event}")]);
1250 }
1251
1252 let mut table = builder.build();
1253 table.with(TablePanel::header("Transaction Block Events"));
1254 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1255 1,
1256 TableStyle::modern().get_horizontal(),
1257 )]));
1258 write!(f, "{table}")
1259 }
1260 }
1261}
1262
1263#[serde_as]
1267#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
1268#[serde(rename = "DevInspectArgs", rename_all = "camelCase")]
1269pub struct DevInspectArgs {
1270 #[serde_as(as = "Option<IotaAddressSchema>")]
1273 #[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 #[serde_as(as = "IotaAddressSchema")]
1623 #[schemars(with = "IotaAddressSchema")]
1624 pub owner: IotaAddress,
1625 #[schemars(with = "String")]
1626 #[serde_as(as = "DisplayFromStr")]
1627 pub price: u64,
1628 #[schemars(with = "String")]
1629 #[serde_as(as = "DisplayFromStr")]
1630 pub budget: u64,
1631}
1632
1633impl Display for IotaGasData {
1634 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1635 writeln!(f, "Gas Owner: {}", self.owner)?;
1636 writeln!(f, "Gas Budget: {} NANOS", self.budget)?;
1637 writeln!(f, "Gas Price: {} NANOS", self.price)?;
1638 writeln!(f, "Gas Payment:")?;
1639 for payment in &self.payment {
1640 write!(f, "{} ", objref_string(payment))?;
1641 }
1642 writeln!(f)
1643 }
1644}
1645
1646#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1647#[enum_dispatch(IotaTransactionBlockDataAPI)]
1648#[serde(
1649 rename = "TransactionBlockData",
1650 rename_all = "camelCase",
1651 tag = "messageVersion"
1652)]
1653pub enum IotaTransactionBlockData {
1654 V1(IotaTransactionBlockDataV1),
1655}
1656
1657#[enum_dispatch]
1658pub trait IotaTransactionBlockDataAPI {
1659 fn transaction(&self) -> &IotaTransactionBlockKind;
1660 fn sender(&self) -> &IotaAddress;
1661 fn gas_data(&self) -> &IotaGasData;
1662}
1663
1664#[serde_as]
1665#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1666#[serde(rename = "TransactionBlockDataV1", rename_all = "camelCase")]
1667pub struct IotaTransactionBlockDataV1 {
1668 pub transaction: IotaTransactionBlockKind,
1669 #[serde_as(as = "IotaAddressSchema")]
1670 #[schemars(with = "IotaAddressSchema")]
1671 pub sender: IotaAddress,
1672 pub gas_data: IotaGasData,
1673}
1674
1675impl IotaTransactionBlockDataAPI for IotaTransactionBlockDataV1 {
1676 fn transaction(&self) -> &IotaTransactionBlockKind {
1677 &self.transaction
1678 }
1679 fn sender(&self) -> &IotaAddress {
1680 &self.sender
1681 }
1682 fn gas_data(&self) -> &IotaGasData {
1683 &self.gas_data
1684 }
1685}
1686
1687impl IotaTransactionBlockData {
1688 pub fn move_calls(&self) -> Vec<&IotaProgrammableMoveCall> {
1689 match self {
1690 Self::V1(data) => match &data.transaction {
1691 IotaTransactionBlockKind::ProgrammableTransaction(pt) => pt
1692 .commands
1693 .iter()
1694 .filter_map(|command| match command {
1695 IotaCommand::MoveCall(c) => Some(&**c),
1696 _ => None,
1697 })
1698 .collect(),
1699 _ => vec![],
1700 },
1701 }
1702 }
1703
1704 fn try_from_inner(
1705 data: TransactionData,
1706 transaction: IotaTransactionBlockKind,
1707 ) -> Result<Self, anyhow::Error> {
1708 let message_version = data.message_version();
1709 let sender = data.sender();
1710 let gas_data = IotaGasData {
1711 payment: data.gas().to_vec(),
1712 owner: data.gas_owner(),
1713 price: data.gas_price(),
1714 budget: data.gas_budget(),
1715 };
1716
1717 match message_version {
1718 1 => Ok(IotaTransactionBlockData::V1(IotaTransactionBlockDataV1 {
1719 transaction,
1720 sender,
1721 gas_data,
1722 })),
1723 _ => Err(anyhow::anyhow!(
1724 "Support for TransactionData version {} not implemented",
1725 message_version
1726 )),
1727 }
1728 }
1729
1730 pub fn try_from_with_module_cache(
1731 data: TransactionData,
1732 module_cache: &impl GetModule,
1733 tx_digest: TransactionDigest,
1734 ) -> Result<Self, anyhow::Error> {
1735 let transaction = IotaTransactionBlockKind::try_from_with_module_cache(
1736 data.kind().clone(),
1737 module_cache,
1738 tx_digest,
1739 )?;
1740 Self::try_from_inner(data, transaction)
1741 }
1742
1743 pub async fn try_from_with_package_resolver(
1744 data: TransactionData,
1745 package_resolver: &Resolver<impl PackageStore>,
1746 tx_digest: TransactionDigest,
1747 ) -> Result<Self, anyhow::Error> {
1748 let transaction = IotaTransactionBlockKind::try_from_with_package_resolver(
1749 data.kind().clone(),
1750 package_resolver,
1751 tx_digest,
1752 )
1753 .await?;
1754 Self::try_from_inner(data, transaction)
1755 }
1756}
1757
1758impl Display for IotaTransactionBlockData {
1759 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1760 match self {
1761 Self::V1(data) => {
1762 writeln!(f, "Sender: {}", data.sender)?;
1763 writeln!(f, "{}", self.gas_data())?;
1764 writeln!(f, "{}", data.transaction)
1765 }
1766 }
1767 }
1768}
1769
1770#[serde_as]
1771#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1772#[serde(rename = "TransactionBlock", rename_all = "camelCase")]
1773pub struct IotaTransactionBlock {
1774 pub data: IotaTransactionBlockData,
1775 #[serde_as(as = "Vec<GenericSignatureSchema>")]
1776 #[schemars(with = "Vec<GenericSignatureSchema>")]
1777 pub tx_signatures: Vec<GenericSignature>,
1778}
1779
1780impl IotaTransactionBlock {
1781 pub fn try_from(
1782 data: SenderSignedData,
1783 module_cache: &impl GetModule,
1784 tx_digest: TransactionDigest,
1785 ) -> Result<Self, anyhow::Error> {
1786 Ok(Self {
1787 data: IotaTransactionBlockData::try_from_with_module_cache(
1788 data.intent_message().value.clone(),
1789 module_cache,
1790 tx_digest,
1791 )?,
1792 tx_signatures: data.tx_signatures().to_vec(),
1793 })
1794 }
1795
1796 pub async fn try_from_with_package_resolver(
1800 data: SenderSignedData,
1801 package_resolver: &Resolver<impl PackageStore>,
1802 tx_digest: TransactionDigest,
1803 ) -> Result<Self, anyhow::Error> {
1804 Ok(Self {
1805 data: IotaTransactionBlockData::try_from_with_package_resolver(
1806 data.intent_message().value.clone(),
1807 package_resolver,
1808 tx_digest,
1809 )
1810 .await?,
1811 tx_signatures: data.tx_signatures().to_vec(),
1812 })
1813 }
1814}
1815
1816impl Display for IotaTransactionBlock {
1817 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1818 let mut builder = TableBuilder::default();
1819
1820 builder.push_record(vec![format!("{}", self.data)]);
1821 builder.push_record(vec![format!("Signatures:")]);
1822 for tx_sig in &self.tx_signatures {
1823 builder.push_record(vec![format!(
1824 " {}\n",
1825 match tx_sig {
1826 GenericSignature::Signature(sig) =>
1827 Base64::from_bytes(sig.signature_bytes()).encoded(),
1828 _ => Base64::from_bytes(tx_sig.as_ref()).encoded(),
1832 }
1833 )]);
1834 }
1835
1836 let mut table = builder.build();
1837 table.with(TablePanel::header("Transaction Data"));
1838 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1839 1,
1840 TableStyle::modern().get_horizontal(),
1841 )]));
1842 write!(f, "{table}")
1843 }
1844}
1845
1846#[serde_as]
1847#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1848pub struct IotaGenesisTransaction {
1849 #[serde_as(as = "Vec<ObjectIdSchema>")]
1850 #[schemars(with = "Vec<ObjectIdSchema>")]
1851 pub objects: Vec<ObjectId>,
1852 #[schemars(with = "Vec<IotaEventID>")]
1853 pub events: Vec<EventID>,
1854}
1855
1856#[serde_as]
1857#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1858pub struct IotaConsensusCommitPrologueV1 {
1859 #[schemars(with = "String")]
1860 #[serde_as(as = "DisplayFromStr")]
1861 pub epoch: u64,
1862 #[schemars(with = "String")]
1863 #[serde_as(as = "DisplayFromStr")]
1864 pub round: u64,
1865 #[schemars(with = "Option<String>")]
1866 #[serde_as(as = "Option<DisplayFromStr>")]
1867 pub sub_dag_index: Option<u64>,
1868 #[schemars(with = "String")]
1869 #[serde_as(as = "DisplayFromStr")]
1870 pub commit_timestamp_ms: u64,
1871 #[serde_as(as = "Base58Schema")]
1872 #[schemars(with = "Base58Schema")]
1873 pub consensus_commit_digest: ConsensusCommitDigest,
1874 pub consensus_determined_version_assignments: IotaConsensusDeterminedVersionAssignments,
1875}
1876
1877#[serde_as]
1880#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
1881#[schemars(rename = "ConsensusDeterminedVersionAssignments")]
1882pub enum IotaConsensusDeterminedVersionAssignments {
1883 CancelledTransactions(
1885 #[serde_as(as = "Vec<(Base58Schema, Vec<(ObjectIdSchema, SequenceNumberU64Schema)>)>")]
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(|cancelled| {
1902 (
1903 cancelled.digest,
1904 cancelled
1905 .version_assignments
1906 .into_iter()
1907 .map(|va| (va.object_id, va.version))
1908 .collect(),
1909 )
1910 })
1911 .collect(),
1912 ),
1913 _ => unimplemented!(
1914 "a new ConsensusDeterminedVersionAssignments enum variant was added and needs to be handled"
1915 ),
1916 }
1917 }
1918}
1919
1920impl From<IotaConsensusDeterminedVersionAssignments> for ConsensusDeterminedVersionAssignments {
1921 fn from(
1922 iota_consensus_determined_version_assignments: IotaConsensusDeterminedVersionAssignments,
1923 ) -> Self {
1924 match iota_consensus_determined_version_assignments {
1925 IotaConsensusDeterminedVersionAssignments::CancelledTransactions(assignments) => {
1926 ConsensusDeterminedVersionAssignments::CancelledTransactions {
1927 cancelled_transactions: assignments
1928 .into_iter()
1929 .map(|(digest, version_assignments)| CancelledTransaction {
1930 digest,
1931 version_assignments: version_assignments
1932 .into_iter()
1933 .map(|(object_id, version)| VersionAssignment {
1934 object_id,
1935 version,
1936 })
1937 .collect(),
1938 })
1939 .collect(),
1940 }
1941 }
1942 }
1943 }
1944}
1945
1946#[serde_as]
1947#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1948pub struct IotaRandomnessStateUpdate {
1949 #[schemars(with = "String")]
1950 #[serde_as(as = "DisplayFromStr")]
1951 pub epoch: u64,
1952
1953 #[schemars(with = "String")]
1954 #[serde_as(as = "DisplayFromStr")]
1955 pub randomness_round: u64,
1956 pub random_bytes: Vec<u8>,
1957}
1958
1959#[serde_as]
1960#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1961pub struct IotaEndOfEpochTransaction {
1962 pub transactions: Vec<IotaEndOfEpochTransactionKind>,
1963}
1964
1965#[serde_as]
1966#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1967pub enum IotaEndOfEpochTransactionKind {
1968 ChangeEpoch(IotaChangeEpoch),
1969 ChangeEpochV2(IotaChangeEpochV2),
1970}
1971
1972#[serde_as]
1973#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
1974#[serde(rename = "InputObjectKind")]
1975pub enum IotaInputObjectKind {
1976 MovePackage(
1978 #[serde_as(as = "ObjectIdSchema")]
1979 #[schemars(with = "ObjectIdSchema")]
1980 ObjectId,
1981 ),
1982 ImmOrOwnedMoveObject(
1984 #[schemars(with = "ObjectRefSchema")]
1985 #[serde_as(as = "ObjectRefSchema")]
1986 ObjectRef,
1987 ),
1988 SharedMoveObject {
1990 #[serde_as(as = "ObjectIdSchema")]
1991 #[schemars(with = "ObjectIdSchema")]
1992 id: ObjectId,
1993 #[schemars(with = "SequenceNumberStringSchema")]
1994 #[serde_as(as = "SequenceNumberStringSchema")]
1995 initial_shared_version: SequenceNumber,
1996 #[serde(default = "default_shared_object_mutability")]
1997 mutable: bool,
1998 },
1999}
2000
2001#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2004pub struct IotaProgrammableTransactionBlock {
2005 pub inputs: Vec<IotaCallArg>,
2007 #[serde(rename = "transactions")]
2008 pub commands: Vec<IotaCommand>,
2012}
2013
2014impl Display for IotaProgrammableTransactionBlock {
2015 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2016 let Self { inputs, commands } = self;
2017 writeln!(f, "Inputs: {inputs:?}")?;
2018 writeln!(f, "Commands: [")?;
2019 for c in commands {
2020 writeln!(f, " {c},")?;
2021 }
2022 writeln!(f, "]")
2023 }
2024}
2025
2026impl IotaProgrammableTransactionBlock {
2027 fn try_from_with_module_cache(
2028 value: ProgrammableTransaction,
2029 module_cache: &impl GetModule,
2030 ) -> Result<Self, anyhow::Error> {
2031 let ProgrammableTransaction { inputs, commands } = value;
2032 let input_types = Self::resolve_input_type(&inputs, &commands, module_cache);
2033 Ok(IotaProgrammableTransactionBlock {
2034 inputs: inputs
2035 .into_iter()
2036 .zip(input_types)
2037 .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
2038 .collect::<Result<_, _>>()?,
2039 commands: commands.into_iter().map(IotaCommand::from).collect(),
2040 })
2041 }
2042
2043 async fn try_from_with_package_resolver(
2044 value: ProgrammableTransaction,
2045 package_resolver: &Resolver<impl PackageStore>,
2046 ) -> Result<Self, anyhow::Error> {
2047 let input_types = package_resolver
2050 .pure_input_layouts(&value)
2051 .await
2052 .unwrap_or_else(|e| {
2053 tracing::warn!("pure_input_layouts failed: {:?}", e);
2054 vec![None; value.inputs.len()]
2055 });
2056
2057 let ProgrammableTransaction { inputs, commands } = value;
2058 Ok(IotaProgrammableTransactionBlock {
2059 inputs: inputs
2060 .into_iter()
2061 .zip(input_types)
2062 .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
2063 .collect::<Result<_, _>>()?,
2064 commands: commands.into_iter().map(IotaCommand::from).collect(),
2065 })
2066 }
2067
2068 fn resolve_input_type(
2069 inputs: &[CallArg],
2070 commands: &[Command],
2071 module_cache: &impl GetModule,
2072 ) -> Vec<Option<MoveTypeLayout>> {
2073 let mut result_types = vec![None; inputs.len()];
2074 for command in commands.iter() {
2075 match command {
2076 Command::MoveCall(cmd) => {
2077 let module = unsafe {
2079 move_core_types::identifier::Identifier::new_unchecked(cmd.module.as_str())
2080 };
2081 let id = ModuleId::new(AccountAddress::new(cmd.package.into_bytes()), module);
2082 let Some(types) = get_signature_types(id, &cmd.function, module_cache) else {
2083 return result_types;
2084 };
2085 for (arg, type_) in cmd.arguments.iter().zip(types) {
2086 if let (&Argument::Input(i), Some(type_)) = (arg, type_) {
2087 if let Some(x) = result_types.get_mut(i as usize) {
2088 x.replace(type_);
2089 }
2090 }
2091 }
2092 }
2093 Command::SplitCoins(cmd) => {
2094 for arg in &cmd.amounts {
2095 if let &Argument::Input(i) = arg {
2096 if let Some(x) = result_types.get_mut(i as usize) {
2097 x.replace(MoveTypeLayout::U64);
2098 }
2099 }
2100 }
2101 }
2102 Command::TransferObjects(TransferObjects {
2103 address: Argument::Input(i),
2104 ..
2105 }) => {
2106 if let Some(x) = result_types.get_mut((*i) as usize) {
2107 x.replace(MoveTypeLayout::Address);
2108 }
2109 }
2110 _ => {}
2111 }
2112 }
2113 result_types
2114 }
2115}
2116
2117fn get_signature_types(
2118 id: ModuleId,
2119 function: &Identifier,
2120 module_cache: &impl GetModule,
2121) -> Option<Vec<Option<MoveTypeLayout>>> {
2122 use std::borrow::Borrow;
2123 if let Ok(Some(module)) = module_cache.get_module_by_id(&id) {
2124 let module: &CompiledModule = module.borrow();
2125 let func = module
2126 .function_handles
2127 .iter()
2128 .find(|f| module.identifier_at(f.name).as_str() == function.as_str())?;
2129 Some(
2130 module
2131 .signature_at(func.parameters)
2132 .0
2133 .iter()
2134 .map(|s| primitive_type(module, &[], s))
2135 .collect(),
2136 )
2137 } else {
2138 None
2139 }
2140}
2141
2142#[serde_as]
2144#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2145#[serde(rename = "IotaTransaction")]
2146pub enum IotaCommand {
2147 MoveCall(Box<IotaProgrammableMoveCall>),
2149 TransferObjects(Vec<IotaArgument>, IotaArgument),
2154 SplitCoins(IotaArgument, Vec<IotaArgument>),
2157 MergeCoins(IotaArgument, Vec<IotaArgument>),
2160 Publish(
2163 #[serde_as(as = "Vec<ObjectIdSchema>")]
2164 #[schemars(with = "Vec<ObjectIdSchema>")]
2165 Vec<ObjectId>,
2166 ),
2167 Upgrade(
2169 #[serde_as(as = "Vec<ObjectIdSchema>")]
2170 #[schemars(with = "Vec<ObjectIdSchema>")]
2171 Vec<ObjectId>,
2172 #[serde_as(as = "ObjectIdSchema")]
2173 #[schemars(with = "ObjectIdSchema")]
2174 ObjectId,
2175 IotaArgument,
2176 ),
2177 MakeMoveVec(Option<String>, Vec<IotaArgument>),
2181}
2182
2183impl Display for IotaCommand {
2184 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2185 match self {
2186 Self::MoveCall(p) => {
2187 write!(f, "MoveCall({p})")
2188 }
2189 Self::MakeMoveVec(ty_opt, elems) => {
2190 write!(f, "MakeMoveVec(")?;
2191 if let Some(ty) = ty_opt {
2192 write!(f, "Some{ty}")?;
2193 } else {
2194 write!(f, "None")?;
2195 }
2196 write!(f, ",[")?;
2197 write_sep(f, elems, ",")?;
2198 write!(f, "])")
2199 }
2200 Self::TransferObjects(objs, addr) => {
2201 write!(f, "TransferObjects([")?;
2202 write_sep(f, objs, ",")?;
2203 write!(f, "],{addr})")
2204 }
2205 Self::SplitCoins(coin, amounts) => {
2206 write!(f, "SplitCoins({coin},")?;
2207 write_sep(f, amounts, ",")?;
2208 write!(f, ")")
2209 }
2210 Self::MergeCoins(target, coins) => {
2211 write!(f, "MergeCoins({target},")?;
2212 write_sep(f, coins, ",")?;
2213 write!(f, ")")
2214 }
2215 Self::Publish(deps) => {
2216 write!(f, "Publish(<modules>,")?;
2217 write_sep(f, deps, ",")?;
2218 write!(f, ")")
2219 }
2220 Self::Upgrade(deps, current_package_id, ticket) => {
2221 write!(f, "Upgrade(<modules>, {ticket},")?;
2222 write_sep(f, deps, ",")?;
2223 write!(f, ", {current_package_id}")?;
2224 write!(f, ")")
2225 }
2226 }
2227 }
2228}
2229
2230impl From<Command> for IotaCommand {
2231 fn from(value: Command) -> Self {
2232 match value {
2233 Command::MoveCall(cmd) => IotaCommand::MoveCall(Box::new((cmd).into())),
2234 Command::TransferObjects(cmd) => IotaCommand::TransferObjects(
2235 cmd.objects.into_iter().map(IotaArgument::from).collect(),
2236 cmd.address.into(),
2237 ),
2238 Command::SplitCoins(cmd) => IotaCommand::SplitCoins(
2239 cmd.coin.into(),
2240 cmd.amounts.into_iter().map(IotaArgument::from).collect(),
2241 ),
2242 Command::MergeCoins(cmd) => IotaCommand::MergeCoins(
2243 cmd.coin.into(),
2244 cmd.coins_to_merge
2245 .into_iter()
2246 .map(IotaArgument::from)
2247 .collect(),
2248 ),
2249 Command::Publish(cmd) => IotaCommand::Publish(cmd.dependencies),
2250 Command::MakeMoveVector(cmd) => IotaCommand::MakeMoveVec(
2251 cmd.type_.map(|tag| tag.to_string()),
2252 cmd.elements.into_iter().map(IotaArgument::from).collect(),
2253 ),
2254 Command::Upgrade(cmd) => IotaCommand::Upgrade(
2255 cmd.dependencies,
2256 cmd.package,
2257 IotaArgument::from(cmd.ticket),
2258 ),
2259 _ => unimplemented!("a new Command enum variant was added and needs to be handled"),
2260 }
2261 }
2262}
2263
2264#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2266pub enum IotaArgument {
2267 GasCoin,
2270 Input(u16),
2273 Result(u16),
2276 NestedResult(u16, u16),
2280}
2281
2282impl Display for IotaArgument {
2283 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2284 match self {
2285 Self::GasCoin => write!(f, "GasCoin"),
2286 Self::Input(i) => write!(f, "Input({i})"),
2287 Self::Result(i) => write!(f, "Result({i})"),
2288 Self::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
2289 }
2290 }
2291}
2292
2293impl From<Argument> for IotaArgument {
2294 fn from(value: Argument) -> Self {
2295 match value {
2296 Argument::Gas => Self::GasCoin,
2297 Argument::Input(i) => Self::Input(i),
2298 Argument::Result(i) => Self::Result(i),
2299 Argument::NestedResult(i, j) => Self::NestedResult(i, j),
2300 _ => unimplemented!("a new Argument enum variant was added and needs to be handled"),
2301 }
2302 }
2303}
2304
2305#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2306#[serde(untagged)]
2307pub enum PtbInput {
2308 PtbRef(IotaArgument),
2309 CallArg(IotaJsonValue),
2310}
2311
2312#[serde_as]
2315#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2316pub struct IotaProgrammableMoveCall {
2317 #[serde_as(as = "ObjectIdSchema")]
2319 #[schemars(with = "ObjectIdSchema")]
2320 pub package: ObjectId,
2321 pub module: String,
2323 pub function: String,
2325 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2326 pub type_arguments: Vec<String>,
2328 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2329 pub arguments: Vec<IotaArgument>,
2331}
2332
2333fn write_sep<T: Display>(
2334 f: &mut Formatter<'_>,
2335 items: impl IntoIterator<Item = T>,
2336 sep: &str,
2337) -> std::fmt::Result {
2338 let mut xs = items.into_iter().peekable();
2339 while let Some(x) = xs.next() {
2340 write!(f, "{x}")?;
2341 if xs.peek().is_some() {
2342 write!(f, "{sep}")?;
2343 }
2344 }
2345 Ok(())
2346}
2347
2348impl Display for IotaProgrammableMoveCall {
2349 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2350 let Self {
2351 package,
2352 module,
2353 function,
2354 type_arguments,
2355 arguments,
2356 } = self;
2357 write!(f, "{package}::{module}::{function}")?;
2358 if !type_arguments.is_empty() {
2359 write!(f, "<")?;
2360 write_sep(f, type_arguments, ",")?;
2361 write!(f, ">")?;
2362 }
2363 write!(f, "(")?;
2364 write_sep(f, arguments, ",")?;
2365 write!(f, ")")
2366 }
2367}
2368
2369impl From<MoveCall> for IotaProgrammableMoveCall {
2370 fn from(value: MoveCall) -> Self {
2371 let MoveCall {
2372 package,
2373 module,
2374 function,
2375 type_arguments,
2376 arguments,
2377 } = value;
2378 Self {
2379 package,
2380 module: module.to_string(),
2381 function: function.to_string(),
2382 type_arguments: type_arguments.into_iter().map(|t| t.to_string()).collect(),
2383 arguments: arguments.into_iter().map(IotaArgument::from).collect(),
2384 }
2385 }
2386}
2387
2388const fn default_shared_object_mutability() -> bool {
2389 true
2390}
2391
2392impl From<InputObjectKind> for IotaInputObjectKind {
2393 fn from(input: InputObjectKind) -> Self {
2394 match input {
2395 InputObjectKind::MovePackage(id) => Self::MovePackage(id),
2396 InputObjectKind::ImmOrOwnedMoveObject(oref) => Self::ImmOrOwnedMoveObject(oref),
2397 InputObjectKind::SharedMoveObject {
2398 id,
2399 initial_shared_version,
2400 mutable,
2401 } => Self::SharedMoveObject {
2402 id,
2403 initial_shared_version,
2404 mutable,
2405 },
2406 }
2407 }
2408}
2409
2410#[derive(Debug, Serialize, Deserialize, Clone)]
2411#[serde(rename = "TypeTag", rename_all = "camelCase")]
2412pub struct IotaTypeTag(String);
2413
2414impl IotaTypeTag {
2415 pub fn new(tag: String) -> Self {
2416 Self(tag)
2417 }
2418}
2419
2420impl AsRef<str> for IotaTypeTag {
2421 fn as_ref(&self) -> &str {
2422 &self.0
2423 }
2424}
2425
2426impl TryFrom<IotaTypeTag> for TypeTag {
2427 type Error = anyhow::Error;
2428 fn try_from(tag: IotaTypeTag) -> Result<Self, Self::Error> {
2429 parse_iota_type_tag(&tag.0)
2430 }
2431}
2432
2433impl From<TypeTag> for IotaTypeTag {
2434 fn from(tag: TypeTag) -> Self {
2435 Self(format!("{tag}"))
2436 }
2437}
2438
2439#[derive(Serialize, Deserialize, JsonSchema)]
2440#[serde(rename_all = "camelCase")]
2441pub enum RPCTransactionRequestParams {
2442 TransferObjectRequestParams(TransferObjectParams),
2443 MoveCallRequestParams(MoveCallParams),
2444}
2445
2446#[serde_as]
2447#[derive(Serialize, Deserialize, JsonSchema)]
2448#[serde(rename_all = "camelCase")]
2449pub struct TransferObjectParams {
2450 #[serde_as(as = "IotaAddressSchema")]
2451 #[schemars(with = "IotaAddressSchema")]
2452 pub recipient: IotaAddress,
2453 #[serde_as(as = "ObjectIdSchema")]
2454 #[schemars(with = "ObjectIdSchema")]
2455 pub object_id: ObjectId,
2456}
2457
2458#[serde_as]
2459#[derive(Serialize, Deserialize, JsonSchema)]
2460#[serde(rename_all = "camelCase")]
2461pub struct MoveCallParams {
2462 #[serde_as(as = "ObjectIdSchema")]
2463 #[schemars(with = "ObjectIdSchema")]
2464 pub package_object_id: ObjectId,
2465 pub module: String,
2466 pub function: String,
2467 #[serde(default)]
2468 #[schemars(with = "Vec<TypeTagSchema>")]
2469 pub type_arguments: Vec<IotaTypeTag>,
2470 pub arguments: Vec<PtbInput>,
2471}
2472
2473#[serde_as]
2474#[derive(Serialize, Deserialize, Clone, JsonSchema)]
2475#[serde(rename_all = "camelCase")]
2476pub struct TransactionBlockBytes {
2477 #[schemars(with = "Base64Schema")]
2480 pub tx_bytes: Base64,
2481 #[schemars(with = "Vec<ObjectRefSchema>")]
2483 #[serde_as(as = "Vec<ObjectRefSchema>")]
2484 pub gas: Vec<ObjectRef>,
2485 pub input_objects: Vec<IotaInputObjectKind>,
2487}
2488
2489impl TransactionBlockBytes {
2490 pub fn from_data(data: TransactionData) -> Result<Self, anyhow::Error> {
2491 Ok(Self {
2492 tx_bytes: Base64::from_bytes(bcs::to_bytes(&data)?.as_slice()),
2493 gas: data.gas().to_vec(),
2494 input_objects: data
2495 .input_objects()?
2496 .into_iter()
2497 .map(IotaInputObjectKind::from)
2498 .collect(),
2499 })
2500 }
2501
2502 pub fn to_data(self) -> Result<TransactionData, anyhow::Error> {
2503 bcs::from_bytes::<TransactionData>(&self.tx_bytes.to_vec().map_err(|e| anyhow::anyhow!(e))?)
2504 .map_err(|e| anyhow::anyhow!(e))
2505 }
2506}
2507
2508#[serde_as]
2509#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
2510#[serde(rename = "OwnedObjectRef")]
2511pub struct OwnedObjectRef {
2512 #[schemars(with = "OwnerSchema")]
2513 #[serde_as(as = "OwnerSchema")]
2514 pub owner: Owner,
2515 #[schemars(with = "ObjectRefSchema")]
2516 #[serde_as(as = "ObjectRefSchema")]
2517 pub reference: ObjectRef,
2518}
2519
2520impl OwnedObjectRef {
2521 pub fn object_id(&self) -> ObjectId {
2522 self.reference.object_id
2523 }
2524 pub fn version(&self) -> SequenceNumber {
2525 self.reference.version
2526 }
2527}
2528
2529#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2530#[serde(tag = "type", rename_all = "camelCase")]
2531pub enum IotaCallArg {
2532 Object(IotaObjectArg),
2534 Pure(IotaPureValue),
2536}
2537
2538impl IotaCallArg {
2539 pub fn try_from(
2540 value: CallArg,
2541 layout: Option<&MoveTypeLayout>,
2542 ) -> Result<Self, anyhow::Error> {
2543 Ok(match value {
2544 CallArg::Pure(p) => IotaCallArg::Pure(IotaPureValue {
2545 value_type: layout.map(|l| type_tag_core_to_sdk(&l.into())),
2546 value: IotaJsonValue::from_bcs_bytes(layout, &p)?,
2547 }),
2548 CallArg::ImmutableOrOwned(object_ref) => {
2549 IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject {
2550 object_id: object_ref.object_id,
2551 version: object_ref.version,
2552 digest: object_ref.digest,
2553 })
2554 }
2555 CallArg::Shared(SharedObjectRef {
2556 object_id: id,
2557 initial_shared_version,
2558 mutable,
2559 }) => IotaCallArg::Object(IotaObjectArg::SharedObject {
2560 object_id: id,
2561 initial_shared_version,
2562 mutable,
2563 }),
2564 CallArg::Receiving(object_ref) => IotaCallArg::Object(IotaObjectArg::Receiving {
2565 object_id: object_ref.object_id,
2566 version: object_ref.version,
2567 digest: object_ref.digest,
2568 }),
2569 _ => unimplemented!("a new CallArg enum variant was added and needs to be handled"),
2570 })
2571 }
2572
2573 pub fn pure(&self) -> Option<&IotaJsonValue> {
2574 match self {
2575 IotaCallArg::Pure(v) => Some(&v.value),
2576 _ => None,
2577 }
2578 }
2579
2580 pub fn object(&self) -> Option<&ObjectId> {
2581 match self {
2582 IotaCallArg::Object(IotaObjectArg::SharedObject { object_id, .. })
2583 | IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject { object_id, .. })
2584 | IotaCallArg::Object(IotaObjectArg::Receiving { object_id, .. }) => Some(object_id),
2585 _ => None,
2586 }
2587 }
2588}
2589
2590#[serde_as]
2591#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2592#[serde(rename_all = "camelCase")]
2593pub struct IotaPureValue {
2594 #[schemars(with = "Option<TypeTagSchema>")]
2595 #[serde_as(as = "Option<TypeTagSchema>")]
2596 value_type: Option<TypeTag>,
2597 value: IotaJsonValue,
2598}
2599
2600impl IotaPureValue {
2601 pub fn value(&self) -> IotaJsonValue {
2602 self.value.clone()
2603 }
2604
2605 pub fn value_type(&self) -> Option<TypeTag> {
2606 self.value_type.clone()
2607 }
2608}
2609
2610#[serde_as]
2611#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2612#[serde(tag = "objectType", rename_all = "camelCase")]
2613pub enum IotaObjectArg {
2614 #[serde(rename_all = "camelCase")]
2616 ImmOrOwnedObject {
2617 #[serde_as(as = "ObjectIdSchema")]
2618 #[schemars(with = "ObjectIdSchema")]
2619 object_id: ObjectId,
2620 #[schemars(with = "SequenceNumberStringSchema")]
2621 #[serde_as(as = "SequenceNumberStringSchema")]
2622 version: SequenceNumber,
2623 #[serde_as(as = "Base58Schema")]
2624 #[schemars(with = "Base58Schema")]
2625 digest: ObjectDigest,
2626 },
2627 #[serde(rename_all = "camelCase")]
2631 SharedObject {
2632 #[serde_as(as = "ObjectIdSchema")]
2633 #[schemars(with = "ObjectIdSchema")]
2634 object_id: ObjectId,
2635 #[schemars(with = "SequenceNumberStringSchema")]
2636 #[serde_as(as = "SequenceNumberStringSchema")]
2637 initial_shared_version: SequenceNumber,
2638 mutable: bool,
2639 },
2640 #[serde(rename_all = "camelCase")]
2642 Receiving {
2643 #[serde_as(as = "ObjectIdSchema")]
2644 #[schemars(with = "ObjectIdSchema")]
2645 object_id: ObjectId,
2646 #[schemars(with = "SequenceNumberStringSchema")]
2647 #[serde_as(as = "SequenceNumberStringSchema")]
2648 version: SequenceNumber,
2649 #[serde_as(as = "Base58Schema")]
2650 #[schemars(with = "Base58Schema")]
2651 digest: ObjectDigest,
2652 },
2653}
2654
2655#[derive(Clone)]
2656pub struct EffectsWithInput {
2657 pub effects: IotaTransactionBlockEffects,
2658 pub input: TransactionData,
2659}
2660
2661impl From<EffectsWithInput> for IotaTransactionBlockEffects {
2662 fn from(e: EffectsWithInput) -> Self {
2663 e.effects
2664 }
2665}
2666
2667#[serde_as]
2668#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2669pub enum TransactionFilter {
2670 Checkpoint(
2672 #[schemars(with = "String")]
2673 #[serde_as(as = "DisplayFromStr")]
2674 CheckpointSequenceNumber,
2675 ),
2676 MoveFunction {
2678 #[serde_as(as = "ObjectIdSchema")]
2679 #[schemars(with = "ObjectIdSchema")]
2680 package: ObjectId,
2681 module: Option<String>,
2682 function: Option<String>,
2683 },
2684 InputObject(
2686 #[serde_as(as = "ObjectIdSchema")]
2687 #[schemars(with = "ObjectIdSchema")]
2688 ObjectId,
2689 ),
2690 ChangedObject(
2693 #[serde_as(as = "ObjectIdSchema")]
2694 #[schemars(with = "ObjectIdSchema")]
2695 ObjectId,
2696 ),
2697 FromAddress(
2699 #[serde_as(as = "IotaAddressSchema")]
2700 #[schemars(with = "IotaAddressSchema")]
2701 IotaAddress,
2702 ),
2703 ToAddress(
2705 #[serde_as(as = "IotaAddressSchema")]
2706 #[schemars(with = "IotaAddressSchema")]
2707 IotaAddress,
2708 ),
2709 FromAndToAddress {
2711 #[serde_as(as = "IotaAddressSchema")]
2712 #[schemars(with = "IotaAddressSchema")]
2713 from: IotaAddress,
2714 #[serde_as(as = "IotaAddressSchema")]
2715 #[schemars(with = "IotaAddressSchema")]
2716 to: IotaAddress,
2717 },
2718 FromOrToAddress {
2720 #[serde_as(as = "IotaAddressSchema")]
2721 #[schemars(with = "IotaAddressSchema")]
2722 addr: IotaAddress,
2723 },
2724 TransactionKind(IotaTransactionKind),
2726 TransactionKindIn(Vec<IotaTransactionKind>),
2728}
2729
2730impl TransactionFilter {
2731 pub fn as_v2(&self) -> TransactionFilterV2 {
2732 match self {
2733 TransactionFilter::InputObject(o) => TransactionFilterV2::InputObject(*o),
2734 TransactionFilter::ChangedObject(o) => TransactionFilterV2::ChangedObject(*o),
2735 TransactionFilter::FromAddress(a) => TransactionFilterV2::FromAddress(*a),
2736 TransactionFilter::ToAddress(a) => TransactionFilterV2::ToAddress(*a),
2737 TransactionFilter::FromAndToAddress { from, to } => {
2738 TransactionFilterV2::FromAndToAddress {
2739 from: *from,
2740 to: *to,
2741 }
2742 }
2743 TransactionFilter::FromOrToAddress { addr } => {
2744 TransactionFilterV2::FromOrToAddress { addr: *addr }
2745 }
2746 TransactionFilter::MoveFunction {
2747 package,
2748 module,
2749 function,
2750 } => TransactionFilterV2::MoveFunction {
2751 package: *package,
2752 module: module.clone(),
2753 function: function.clone(),
2754 },
2755 TransactionFilter::TransactionKind(kind) => TransactionFilterV2::TransactionKind(*kind),
2756 TransactionFilter::TransactionKindIn(kinds) => {
2757 TransactionFilterV2::TransactionKindIn(kinds.clone())
2758 }
2759 TransactionFilter::Checkpoint(checkpoint) => {
2760 TransactionFilterV2::Checkpoint(*checkpoint)
2761 }
2762 }
2763 }
2764}
2765
2766impl Filter<EffectsWithInput> for TransactionFilter {
2767 fn matches(&self, item: &EffectsWithInput) -> bool {
2768 let _scope = monitored_scope("TransactionFilter::matches");
2769 match self {
2770 TransactionFilter::InputObject(o) => {
2771 let Ok(input_objects) = item.input.input_objects() else {
2772 return false;
2773 };
2774 input_objects.iter().any(|object| object.object_id() == *o)
2775 }
2776 TransactionFilter::ChangedObject(o) => item
2777 .effects
2778 .mutated()
2779 .iter()
2780 .any(|oref: &OwnedObjectRef| &oref.reference.object_id == o),
2781 TransactionFilter::FromAddress(a) => &item.input.sender() == a,
2782 TransactionFilter::ToAddress(a) => {
2783 let mutated: &[OwnedObjectRef] = item.effects.mutated();
2784 mutated.iter().chain(item.effects.unwrapped().iter()).any(|oref: &OwnedObjectRef| {
2785 matches!(oref.owner, Owner::Address(owner) if owner == *a)
2786 })
2787 }
2788 TransactionFilter::FromAndToAddress { from, to } => {
2789 Self::FromAddress(*from).matches(item) && Self::ToAddress(*to).matches(item)
2790 }
2791 TransactionFilter::FromOrToAddress { addr } => {
2792 Self::FromAddress(*addr).matches(item) || Self::ToAddress(*addr).matches(item)
2793 }
2794 TransactionFilter::MoveFunction {
2795 package,
2796 module,
2797 function,
2798 } => item.input.move_calls().into_iter().any(|(p, m, f)| {
2799 p == package
2800 && (module.is_none() || matches!(module, Some(m2) if m2 == &m.to_string()))
2801 && (function.is_none() || matches!(function, Some(f2) if f2 == &f.to_string()))
2802 }),
2803 TransactionFilter::TransactionKind(kind) => {
2804 kind == &IotaTransactionKind::from(item.input.kind())
2805 }
2806 TransactionFilter::TransactionKindIn(kinds) => kinds
2807 .iter()
2808 .any(|kind| kind == &IotaTransactionKind::from(item.input.kind())),
2809 TransactionFilter::Checkpoint(_) => false,
2811 }
2812 }
2813}
2814
2815#[serde_as]
2816#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2817#[non_exhaustive]
2818pub enum TransactionFilterV2 {
2819 Checkpoint(
2821 #[schemars(with = "String")]
2822 #[serde_as(as = "DisplayFromStr")]
2823 CheckpointSequenceNumber,
2824 ),
2825 MoveFunction {
2827 #[serde_as(as = "ObjectIdSchema")]
2828 #[schemars(with = "ObjectIdSchema")]
2829 package: ObjectId,
2830 module: Option<String>,
2831 function: Option<String>,
2832 },
2833 InputObject(
2835 #[serde_as(as = "ObjectIdSchema")]
2836 #[schemars(with = "ObjectIdSchema")]
2837 ObjectId,
2838 ),
2839 ChangedObject(
2842 #[serde_as(as = "ObjectIdSchema")]
2843 #[schemars(with = "ObjectIdSchema")]
2844 ObjectId,
2845 ),
2846 WrappedOrDeletedObject(
2850 #[serde_as(as = "ObjectIdSchema")]
2851 #[schemars(with = "ObjectIdSchema")]
2852 ObjectId,
2853 ),
2854 FromAddress(
2856 #[serde_as(as = "IotaAddressSchema")]
2857 #[schemars(with = "IotaAddressSchema")]
2858 IotaAddress,
2859 ),
2860 ToAddress(
2862 #[serde_as(as = "IotaAddressSchema")]
2863 #[schemars(with = "IotaAddressSchema")]
2864 IotaAddress,
2865 ),
2866 FromAndToAddress {
2868 #[serde_as(as = "IotaAddressSchema")]
2869 #[schemars(with = "IotaAddressSchema")]
2870 from: IotaAddress,
2871 #[serde_as(as = "IotaAddressSchema")]
2872 #[schemars(with = "IotaAddressSchema")]
2873 to: IotaAddress,
2874 },
2875 FromOrToAddress {
2877 #[serde_as(as = "IotaAddressSchema")]
2878 #[schemars(with = "IotaAddressSchema")]
2879 addr: IotaAddress,
2880 },
2881 TransactionKind(IotaTransactionKind),
2883 TransactionKindIn(Vec<IotaTransactionKind>),
2885}
2886
2887impl TransactionFilterV2 {
2888 pub fn as_v1(&self) -> Option<TransactionFilter> {
2889 match self {
2890 TransactionFilterV2::InputObject(o) => Some(TransactionFilter::InputObject(*o)),
2891 TransactionFilterV2::ChangedObject(o) => Some(TransactionFilter::ChangedObject(*o)),
2892 TransactionFilterV2::FromAddress(a) => Some(TransactionFilter::FromAddress(*a)),
2893 TransactionFilterV2::ToAddress(a) => Some(TransactionFilter::ToAddress(*a)),
2894 TransactionFilterV2::FromAndToAddress { from, to } => {
2895 Some(TransactionFilter::FromAndToAddress {
2896 from: *from,
2897 to: *to,
2898 })
2899 }
2900 TransactionFilterV2::FromOrToAddress { addr } => {
2901 Some(TransactionFilter::FromOrToAddress { addr: *addr })
2902 }
2903 TransactionFilterV2::MoveFunction {
2904 package,
2905 module,
2906 function,
2907 } => Some(TransactionFilter::MoveFunction {
2908 package: *package,
2909 module: module.clone(),
2910 function: function.clone(),
2911 }),
2912 TransactionFilterV2::TransactionKind(kind) => {
2913 Some(TransactionFilter::TransactionKind(*kind))
2914 }
2915 TransactionFilterV2::TransactionKindIn(kinds) => {
2916 Some(TransactionFilter::TransactionKindIn(kinds.clone()))
2917 }
2918 TransactionFilterV2::Checkpoint(checkpoint) => {
2919 Some(TransactionFilter::Checkpoint(*checkpoint))
2920 }
2921 TransactionFilterV2::WrappedOrDeletedObject(_) => None,
2923 }
2924 }
2925}
2926
2927impl Filter<EffectsWithInput> for TransactionFilterV2 {
2928 fn matches(&self, item: &EffectsWithInput) -> bool {
2929 let _scope = monitored_scope("TransactionFilterV2::matches");
2930 if let Some(v1) = self.as_v1() {
2931 return v1.matches(item);
2932 }
2933 match self {
2935 TransactionFilterV2::WrappedOrDeletedObject(o) => item
2936 .effects
2937 .wrapped()
2938 .iter()
2939 .chain(item.effects.deleted())
2940 .chain(item.effects.unwrapped_then_deleted())
2941 .any(|oref| &oref.object_id == o),
2942
2943 _ => false,
2944 }
2945 }
2946}
2947
2948#[derive(
2951 Debug, Clone, Copy, PartialEq, Eq, EnumString, Display, Serialize, Deserialize, JsonSchema,
2952)]
2953#[non_exhaustive]
2954pub enum IotaTransactionKind {
2955 SystemTransaction = 0,
2958 ProgrammableTransaction = 1,
2959 Genesis = 2,
2960 ConsensusCommitPrologueV1 = 3,
2961 RandomnessStateUpdate = 5,
2962 EndOfEpochTransaction = 6,
2963}
2964
2965impl IotaTransactionKind {
2966 pub fn is_system_transaction(&self) -> bool {
2968 !matches!(self, Self::ProgrammableTransaction)
2969 }
2970}
2971
2972impl From<&TransactionKind> for IotaTransactionKind {
2973 fn from(kind: &TransactionKind) -> Self {
2974 match kind {
2975 TransactionKind::Genesis(_) => Self::Genesis,
2976 TransactionKind::ConsensusCommitPrologueV1(_) => Self::ConsensusCommitPrologueV1,
2977 #[allow(deprecated)]
2978 TransactionKind::AuthenticatorStateUpdateV1Deprecated => Self::SystemTransaction,
2979 TransactionKind::RandomnessStateUpdate(_) => Self::RandomnessStateUpdate,
2980 TransactionKind::EndOfEpoch(_) => Self::EndOfEpochTransaction,
2981 TransactionKind::Programmable(_) => Self::ProgrammableTransaction,
2982 _ => unimplemented!(
2983 "a new TransactionKind enum variant was added and needs to be handled"
2984 ),
2985 }
2986 }
2987}