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