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_package_resolver::{CleverError, ErrorConstants, PackageStore, Resolver};
12use iota_sdk_types::{
13 Address, Argument, CanceledTransaction, ChangeEpoch, ChangeEpochV2, ChangeEpochV3,
14 ChangeEpochV4, Command, ConsensusCommitDigest, ConsensusDeterminedVersionAssignments,
15 EndOfEpochTransactionKind, ExecutionError as ExecutionFailureStatus, ExecutionStatus,
16 GenesisObject, Identifier, MoveCall, ObjectDigest, ObjectId, ObjectReference,
17 OwnedObjectReference, Owner, ProgrammableTransaction, SenderSignedTransaction,
18 SharedObjectReference, Transaction, TransactionDigest, TransactionEffects, TransactionEvents,
19 TransactionEventsDigest, TransactionKind, TransferObjects, TypeTag, UserSignature, Version,
20 VersionAssignment, WriteKind, 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,
35 transaction::{CallArg, InputObjectKind, TransactionAPI},
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 TransactionDenyRulesUpdate(IotaTransactionDenyRulesUpdate),
480 EndOfEpochTransaction(IotaEndOfEpochTransaction),
482 }
484
485impl Display for IotaTransactionBlockKind {
486 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
487 let mut writer = String::new();
488 match &self {
489 Self::Genesis(_) => {
490 writeln!(writer, "Transaction Kind: Genesis Transaction")?;
491 }
492 Self::ConsensusCommitPrologueV1(p) => {
493 writeln!(writer, "Transaction Kind: Consensus Commit Prologue V1")?;
494 writeln!(
495 writer,
496 "Epoch: {}, Round: {}, SubDagIndex: {:?}, Timestamp: {}, ConsensusCommitDigest: {}",
497 p.epoch,
498 p.round,
499 p.sub_dag_index,
500 p.commit_timestamp_ms,
501 p.consensus_commit_digest
502 )?;
503 }
504 Self::ProgrammableTransaction(p) => {
505 write!(writer, "Transaction Kind: Programmable")?;
506 write!(writer, "{}", crate::displays::Pretty(p))?;
507 }
508 Self::RandomnessStateUpdate(_) => {
509 writeln!(writer, "Transaction Kind: Randomness State Update")?;
510 }
511 Self::TransactionDenyRulesUpdate(_) => {
512 writeln!(writer, "Transaction Kind: Transaction Deny Rules Update")?;
513 }
514 Self::EndOfEpochTransaction(_) => {
515 writeln!(writer, "Transaction Kind: End of Epoch Transaction")?;
516 }
517 }
518 write!(f, "{writer}")
519 }
520}
521
522impl IotaTransactionBlockKind {
523 fn try_from_inner(
524 tx: TransactionKind,
525 tx_digest: TransactionDigest,
526 ) -> Result<Self, anyhow::Error> {
527 match tx {
528 TransactionKind::Genesis(g) => Ok(Self::Genesis(IotaGenesisTransaction {
529 objects: g.objects.iter().map(GenesisObject::id).collect(),
530 events: g
531 .events
532 .into_iter()
533 .enumerate()
534 .map(|(seq, _event)| EventID::from((tx_digest, seq as u64)))
535 .collect(),
536 })),
537 TransactionKind::ConsensusCommitPrologueV1(p) => Ok(Self::ConsensusCommitPrologueV1(
538 IotaConsensusCommitPrologueV1 {
539 epoch: p.epoch,
540 round: p.round,
541 sub_dag_index: p.sub_dag_index,
542 commit_timestamp_ms: p.commit_timestamp_ms,
543 consensus_commit_digest: p.consensus_commit_digest,
544 consensus_determined_version_assignments: p
545 .consensus_determined_version_assignments
546 .into(),
547 },
548 )),
549 TransactionKind::Programmable(_) => {
550 Err(anyhow::anyhow!(
552 "ProgrammableTransaction must be handled by the caller, not try_from_inner"
553 ))
554 }
555 #[allow(deprecated)]
556 TransactionKind::AuthenticatorStateUpdateV1Deprecated => {
557 Err(anyhow::anyhow!(
561 "AuthenticatorStateUpdateV1 transactions are deprecated and were never created on IOTA"
562 ))
563 }
564 TransactionKind::RandomnessStateUpdate(update) => {
565 Ok(Self::RandomnessStateUpdate(IotaRandomnessStateUpdate {
566 epoch: update.epoch,
567 randomness_round: update.randomness_round.value(),
568 random_bytes: update.random_bytes,
569 }))
570 }
571 TransactionKind::TransactionDenyRulesUpdate(update) => Ok(
572 Self::TransactionDenyRulesUpdate(IotaTransactionDenyRulesUpdate {
573 epoch: update.epoch,
574 round: update.round,
575 added_addresses: update.added_addresses.into_iter().collect(),
576 removed_addresses: update.removed_addresses.into_iter().collect(),
577 added_objects: update.added_objects.into_iter().collect(),
578 removed_objects: update.removed_objects.into_iter().collect(),
579 added_packages: update.added_packages.into_iter().collect(),
580 removed_packages: update.removed_packages.into_iter().collect(),
581 package_publish_disabled: update.package_publish_disabled,
582 package_upgrade_disabled: update.package_upgrade_disabled,
583 shared_object_disabled: update.shared_object_disabled,
584 user_transaction_disabled: update.user_transaction_disabled,
585 receiving_objects_disabled: update.receiving_objects_disabled,
586 move_authenticator_disabled: update.move_authenticator_disabled,
587 }),
588 ),
589 TransactionKind::EndOfEpoch(end_of_epoch_tx) => {
590 Ok(Self::EndOfEpochTransaction(IotaEndOfEpochTransaction {
591 transactions: end_of_epoch_tx
592 .into_iter()
593 .map(|tx| match tx {
594 EndOfEpochTransactionKind::ChangeEpoch(e) => {
595 IotaEndOfEpochTransactionKind::ChangeEpoch(e.into())
596 }
597 EndOfEpochTransactionKind::ChangeEpochV2(e) => {
598 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
599 }
600 EndOfEpochTransactionKind::ChangeEpochV3(e) => {
601 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
602 }
603 EndOfEpochTransactionKind::ChangeEpochV4(e) => {
604 IotaEndOfEpochTransactionKind::ChangeEpochV2(e.into())
605 }
606 EndOfEpochTransactionKind::TransactionDenyRulesCreate => {
607 IotaEndOfEpochTransactionKind::TransactionDenyRulesCreate
608 }
609 _ => unimplemented!(
610 "a new EndOfEpochTransactionKind enum variant was added and needs to be handled"
611 ),
612 })
613 .collect(),
614 }))
615 }
616 _ => unimplemented!(
617 "a new TransactionKind enum variant was added and needs to be handled"
618 )
619 }
620 }
621
622 fn try_from_with_module_cache(
623 tx: TransactionKind,
624 module_cache: &impl GetModule,
625 tx_digest: TransactionDigest,
626 ) -> Result<Self, anyhow::Error> {
627 match tx {
628 TransactionKind::Programmable(p) => Ok(Self::ProgrammableTransaction(
629 IotaProgrammableTransactionBlock::try_from_with_module_cache(p, module_cache)?,
630 )),
631 tx => Self::try_from_inner(tx, tx_digest),
632 }
633 }
634
635 async fn try_from_with_package_resolver(
636 tx: TransactionKind,
637 package_resolver: &Resolver<impl PackageStore>,
638 tx_digest: TransactionDigest,
639 ) -> Result<Self, anyhow::Error> {
640 match tx {
641 TransactionKind::Programmable(p) => Ok(Self::ProgrammableTransaction(
642 IotaProgrammableTransactionBlock::try_from_with_package_resolver(
643 p,
644 package_resolver,
645 )
646 .await?,
647 )),
648 tx => Self::try_from_inner(tx, tx_digest),
649 }
650 }
651
652 pub fn transaction_count(&self) -> usize {
653 match self {
654 Self::ProgrammableTransaction(p) => p.commands.len(),
655 _ => 1,
656 }
657 }
658
659 pub fn name(&self) -> &'static str {
660 match self {
661 Self::Genesis(_) => "Genesis",
662 Self::ConsensusCommitPrologueV1(_) => "ConsensusCommitPrologueV1",
663 Self::ProgrammableTransaction(_) => "ProgrammableTransaction",
664 Self::RandomnessStateUpdate(_) => "RandomnessStateUpdate",
665 Self::TransactionDenyRulesUpdate(_) => "TransactionDenyRulesUpdate",
666 Self::EndOfEpochTransaction(_) => "EndOfEpochTransaction",
667 }
668 }
669}
670
671#[serde_as]
672#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
673pub struct IotaChangeEpoch {
674 #[schemars(with = "String")]
675 #[serde_as(as = "DisplayFromStr")]
676 pub epoch: EpochId,
677 #[schemars(with = "String")]
678 #[serde_as(as = "DisplayFromStr")]
679 pub storage_charge: u64,
680 #[schemars(with = "String")]
681 #[serde_as(as = "DisplayFromStr")]
682 pub computation_charge: u64,
683 #[schemars(with = "String")]
684 #[serde_as(as = "DisplayFromStr")]
685 pub storage_rebate: u64,
686 #[schemars(with = "String")]
687 #[serde_as(as = "DisplayFromStr")]
688 pub epoch_start_timestamp_ms: u64,
689}
690
691impl From<ChangeEpoch> for IotaChangeEpoch {
692 fn from(e: ChangeEpoch) -> Self {
693 Self {
694 epoch: e.epoch,
695 storage_charge: e.storage_charge,
696 computation_charge: e.computation_charge,
697 storage_rebate: e.storage_rebate,
698 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
699 }
700 }
701}
702
703#[serde_as]
704#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
705pub struct IotaChangeEpochV2 {
706 #[schemars(with = "String")]
707 #[serde_as(as = "DisplayFromStr")]
708 pub epoch: EpochId,
709 #[schemars(with = "String")]
710 #[serde_as(as = "DisplayFromStr")]
711 pub storage_charge: u64,
712 #[schemars(with = "String")]
713 #[serde_as(as = "DisplayFromStr")]
714 pub computation_charge: u64,
715 #[schemars(with = "String")]
716 #[serde_as(as = "DisplayFromStr")]
717 pub computation_charge_burned: u64,
718 #[schemars(with = "String")]
719 #[serde_as(as = "DisplayFromStr")]
720 pub storage_rebate: u64,
721 #[schemars(with = "String")]
722 #[serde_as(as = "DisplayFromStr")]
723 pub epoch_start_timestamp_ms: u64,
724 #[schemars(with = "Option<Vec<String>>")]
725 #[serde_as(as = "Option<Vec<DisplayFromStr>>")]
726 #[serde(skip_serializing_if = "Option::is_none", default)]
727 pub eligible_active_validators: Option<Vec<u64>>,
728 #[schemars(with = "Option<Vec<String>>")]
729 #[serde_as(as = "Option<Vec<DisplayFromStr>>")]
730 #[serde(skip_serializing_if = "Option::is_none", default)]
731 pub scores: Option<Vec<u64>>,
732}
733
734impl From<ChangeEpochV2> for IotaChangeEpochV2 {
735 fn from(e: ChangeEpochV2) -> Self {
736 Self {
737 epoch: e.epoch,
738 storage_charge: e.storage_charge,
739 computation_charge: e.computation_charge,
740 computation_charge_burned: e.computation_charge_burned,
741 storage_rebate: e.storage_rebate,
742 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
743 eligible_active_validators: None,
744 scores: None,
745 }
746 }
747}
748
749impl From<ChangeEpochV3> for IotaChangeEpochV2 {
750 fn from(e: ChangeEpochV3) -> Self {
751 Self {
752 epoch: e.epoch,
753 storage_charge: e.storage_charge,
754 computation_charge: e.computation_charge,
755 computation_charge_burned: e.computation_charge_burned,
756 storage_rebate: e.storage_rebate,
757 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
758 eligible_active_validators: Some(e.eligible_active_validators),
759 scores: None,
760 }
761 }
762}
763
764impl From<ChangeEpochV4> for IotaChangeEpochV2 {
765 fn from(e: ChangeEpochV4) -> Self {
766 Self {
767 epoch: e.epoch,
768 storage_charge: e.storage_charge,
769 computation_charge: e.computation_charge,
770 computation_charge_burned: e.computation_charge_burned,
771 storage_rebate: e.storage_rebate,
772 epoch_start_timestamp_ms: e.epoch_start_timestamp_ms,
773 eligible_active_validators: Some(e.eligible_active_validators),
774 scores: Some(e.scores),
775 }
776 }
777}
778
779#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
780#[enum_dispatch(IotaTransactionBlockEffectsAPI)]
781#[serde(
782 rename = "TransactionBlockEffects",
783 rename_all = "camelCase",
784 tag = "messageVersion"
785)]
786pub enum IotaTransactionBlockEffects {
787 V1(IotaTransactionBlockEffectsV1),
788}
789
790#[enum_dispatch]
791pub trait IotaTransactionBlockEffectsAPI {
792 fn status(&self) -> &IotaExecutionStatus;
793 fn into_status(self) -> IotaExecutionStatus;
794 fn shared_objects(&self) -> &[ObjectReference];
795 fn created(&self) -> &[OwnedObjectRef];
796 fn mutated(&self) -> &[OwnedObjectRef];
797 fn unwrapped(&self) -> &[OwnedObjectRef];
798 fn deleted(&self) -> &[ObjectReference];
799 fn unwrapped_then_deleted(&self) -> &[ObjectReference];
800 fn wrapped(&self) -> &[ObjectReference];
801 fn gas_object(&self) -> &OwnedObjectRef;
802 fn events_digest(&self) -> Option<&TransactionEventsDigest>;
803 fn dependencies(&self) -> &[TransactionDigest];
804 fn executed_epoch(&self) -> EpochId;
805 fn transaction_digest(&self) -> &TransactionDigest;
806 fn gas_cost_summary(&self) -> &GasCostSummary;
807
808 fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef>;
810 fn modified_at_versions(&self) -> Vec<(ObjectId, Version)>;
811 fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>;
812 fn all_deleted_objects(&self) -> Vec<(&ObjectReference, DeleteKind)>;
813}
814
815#[serde_as]
816#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
817#[serde(
818 rename = "TransactionBlockEffectsModifiedAtVersions",
819 rename_all = "camelCase"
820)]
821pub struct IotaTransactionBlockEffectsModifiedAtVersions {
822 #[serde_as(as = "ObjectIdSchema")]
823 #[schemars(with = "ObjectIdSchema")]
824 object_id: ObjectId,
825 #[schemars(with = "SequenceNumberStringSchema")]
826 #[serde_as(as = "SequenceNumberStringSchema")]
827 sequence_number: Version,
828}
829
830#[serde_as]
832#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
833#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")]
834pub struct IotaTransactionBlockEffectsV1 {
835 pub status: IotaExecutionStatus,
837 #[schemars(with = "String")]
839 #[serde_as(as = "DisplayFromStr")]
840 pub executed_epoch: EpochId,
841 #[schemars(with = "IotaGasCostSummary")]
842 #[serde_as(as = "IotaGasCostSummary")]
843 pub gas_used: GasCostSummary,
844 #[serde(default, skip_serializing_if = "Vec::is_empty")]
847 pub modified_at_versions: Vec<IotaTransactionBlockEffectsModifiedAtVersions>,
848 #[serde(default, skip_serializing_if = "Vec::is_empty")]
851 #[schemars(with = "Vec<ObjectRefSchema>")]
852 #[serde_as(as = "Vec<ObjectRefSchema>")]
853 pub shared_objects: Vec<ObjectReference>,
854 #[serde_as(as = "Base58Schema")]
856 #[schemars(with = "Base58Schema")]
857 pub transaction_digest: TransactionDigest,
858 #[serde(default, skip_serializing_if = "Vec::is_empty")]
860 pub created: Vec<OwnedObjectRef>,
861 #[serde(default, skip_serializing_if = "Vec::is_empty")]
863 pub mutated: Vec<OwnedObjectRef>,
864 #[serde(default, skip_serializing_if = "Vec::is_empty")]
868 pub unwrapped: Vec<OwnedObjectRef>,
869 #[serde(default, skip_serializing_if = "Vec::is_empty")]
871 #[schemars(with = "Vec<ObjectRefSchema>")]
872 #[serde_as(as = "Vec<ObjectRefSchema>")]
873 pub deleted: Vec<ObjectReference>,
874 #[serde(default, skip_serializing_if = "Vec::is_empty")]
877 #[schemars(with = "Vec<ObjectRefSchema>")]
878 #[serde_as(as = "Vec<ObjectRefSchema>")]
879 pub unwrapped_then_deleted: Vec<ObjectReference>,
880 #[serde(default, skip_serializing_if = "Vec::is_empty")]
882 #[schemars(with = "Vec<ObjectRefSchema>")]
883 #[serde_as(as = "Vec<ObjectRefSchema>")]
884 pub wrapped: Vec<ObjectReference>,
885 pub gas_object: OwnedObjectRef,
888 #[serde(skip_serializing_if = "Option::is_none")]
891 #[serde_as(as = "Option<Base58Schema>")]
892 #[schemars(with = "Option<Base58Schema>")]
893 pub events_digest: Option<TransactionEventsDigest>,
894 #[serde(default, skip_serializing_if = "Vec::is_empty")]
896 #[serde_as(as = "Vec<Base58Schema>")]
897 #[schemars(with = "Vec<Base58Schema>")]
898 pub dependencies: Vec<TransactionDigest>,
899}
900
901impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 {
902 fn status(&self) -> &IotaExecutionStatus {
903 &self.status
904 }
905 fn into_status(self) -> IotaExecutionStatus {
906 self.status
907 }
908 fn shared_objects(&self) -> &[ObjectReference] {
909 &self.shared_objects
910 }
911 fn created(&self) -> &[OwnedObjectRef] {
912 &self.created
913 }
914 fn mutated(&self) -> &[OwnedObjectRef] {
915 &self.mutated
916 }
917 fn unwrapped(&self) -> &[OwnedObjectRef] {
918 &self.unwrapped
919 }
920 fn deleted(&self) -> &[ObjectReference] {
921 &self.deleted
922 }
923 fn unwrapped_then_deleted(&self) -> &[ObjectReference] {
924 &self.unwrapped_then_deleted
925 }
926 fn wrapped(&self) -> &[ObjectReference] {
927 &self.wrapped
928 }
929 fn gas_object(&self) -> &OwnedObjectRef {
930 &self.gas_object
931 }
932 fn events_digest(&self) -> Option<&TransactionEventsDigest> {
933 self.events_digest.as_ref()
934 }
935 fn dependencies(&self) -> &[TransactionDigest] {
936 &self.dependencies
937 }
938
939 fn executed_epoch(&self) -> EpochId {
940 self.executed_epoch
941 }
942
943 fn transaction_digest(&self) -> &TransactionDigest {
944 &self.transaction_digest
945 }
946
947 fn gas_cost_summary(&self) -> &GasCostSummary {
948 &self.gas_used
949 }
950
951 fn mutated_excluding_gas(&self) -> Vec<OwnedObjectRef> {
952 self.mutated
953 .iter()
954 .filter(|o| *o != &self.gas_object)
955 .cloned()
956 .collect()
957 }
958
959 fn modified_at_versions(&self) -> Vec<(ObjectId, Version)> {
960 self.modified_at_versions
961 .iter()
962 .map(|v| (v.object_id, v.sequence_number))
963 .collect::<Vec<_>>()
964 }
965
966 fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> {
967 self.mutated
968 .iter()
969 .map(|owner_ref| (owner_ref, WriteKind::Mutate))
970 .chain(
971 self.created
972 .iter()
973 .map(|owner_ref| (owner_ref, WriteKind::Create)),
974 )
975 .chain(
976 self.unwrapped
977 .iter()
978 .map(|owner_ref| (owner_ref, WriteKind::Unwrap)),
979 )
980 .collect()
981 }
982
983 fn all_deleted_objects(&self) -> Vec<(&ObjectReference, DeleteKind)> {
984 self.deleted
985 .iter()
986 .map(|r| (r, DeleteKind::Normal))
987 .chain(
988 self.unwrapped_then_deleted
989 .iter()
990 .map(|r| (r, DeleteKind::UnwrapThenDelete)),
991 )
992 .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap)))
993 .collect()
994 }
995}
996
997impl IotaTransactionBlockEffects {
998 pub fn new_for_testing(
999 transaction_digest: TransactionDigest,
1000 status: IotaExecutionStatus,
1001 ) -> Self {
1002 Self::V1(IotaTransactionBlockEffectsV1 {
1003 transaction_digest,
1004 status,
1005 gas_object: OwnedObjectRef {
1006 owner: Owner::Address(Address::random()),
1007 reference: iota_types::base_types::random_object_ref(),
1008 },
1009 executed_epoch: 0,
1010 modified_at_versions: vec![],
1011 gas_used: GasCostSummary::default(),
1012 shared_objects: vec![],
1013 created: vec![],
1014 mutated: vec![],
1015 unwrapped: vec![],
1016 deleted: vec![],
1017 unwrapped_then_deleted: vec![],
1018 wrapped: vec![],
1019 events_digest: None,
1020 dependencies: vec![],
1021 })
1022 }
1023
1024 pub async fn from_native_with_clever_error<S: PackageStore>(
1030 native: TransactionEffects,
1031 resolver: &Resolver<S>,
1032 ) -> Self {
1033 let clever_status =
1034 IotaExecutionStatus::from_native_with_clever_error(native.status().clone(), resolver)
1035 .await;
1036 match native {
1037 TransactionEffects::V1(inner) => {
1038 let mut inner = IotaTransactionBlockEffectsV1::from(TransactionEffects::V1(inner));
1039 inner.status = clever_status;
1040 inner.into()
1041 }
1042 _ => unimplemented!(
1043 "a new TransactionEffects enum variant was added and needs to be handled"
1044 ),
1045 }
1046 }
1047}
1048
1049impl TryFrom<TransactionEffects> for IotaTransactionBlockEffects {
1050 type Error = IotaError;
1051
1052 fn try_from(native: TransactionEffects) -> Result<Self, Self::Error> {
1053 Ok(IotaTransactionBlockEffects::V1(native.into()))
1054 }
1055}
1056
1057impl<T: TransactionEffectsAPI> From<T> for IotaTransactionBlockEffectsV1 {
1058 fn from(native: T) -> Self {
1059 Self {
1060 status: native.status().clone().into(),
1061 executed_epoch: native.epoch(),
1062 modified_at_versions: native
1063 .modified_at_versions()
1064 .into_iter()
1065 .map(|modified| IotaTransactionBlockEffectsModifiedAtVersions {
1066 object_id: modified.object_id,
1067 sequence_number: modified.version,
1068 })
1069 .collect(),
1070 gas_used: native.gas_cost_summary().clone(),
1071 shared_objects: native
1072 .input_shared_objects()
1073 .into_iter()
1074 .map(|shared| shared.object_reference())
1075 .collect(),
1076 transaction_digest: *native.transaction_digest(),
1077 created: to_owned_ref(native.created()),
1078 mutated: to_owned_ref(native.mutated().to_vec()),
1079 unwrapped: to_owned_ref(native.unwrapped().to_vec()),
1080 deleted: native.deleted().to_vec(),
1081 unwrapped_then_deleted: native.unwrapped_then_deleted().to_vec(),
1082 wrapped: native.wrapped().to_vec(),
1083 gas_object: OwnedObjectRef {
1084 owner: native.gas_object().owner,
1085 reference: native.gas_object().reference,
1086 },
1087 events_digest: native.events_digest().copied(),
1088 dependencies: native.dependencies().to_vec(),
1089 }
1090 }
1091}
1092
1093fn owned_objref_string(obj: &OwnedObjectRef) -> String {
1094 format!(
1095 " ┌──\n │ ID: {} \n │ Owner: {} \n │ Version: {} \n │ Digest: {}\n └──",
1096 obj.reference.object_id, obj.owner, obj.reference.version, obj.reference.digest
1097 )
1098}
1099
1100fn objref_string(obj: &ObjectReference) -> String {
1101 format!(
1102 " ┌──\n │ ID: {} \n │ Version: {} \n │ Digest: {}\n └──",
1103 obj.object_id, obj.version, obj.digest
1104 )
1105}
1106
1107impl Display for IotaTransactionBlockEffects {
1108 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1109 let mut builder = TableBuilder::default();
1110
1111 builder.push_record(vec![format!("Digest: {}", self.transaction_digest())]);
1112 builder.push_record(vec![format!("Status: {:?}", self.status())]);
1113 builder.push_record(vec![format!("Executed Epoch: {}", self.executed_epoch())]);
1114
1115 if !self.created().is_empty() {
1116 builder.push_record(vec!["\nCreated Objects: ".to_string()]);
1117
1118 for oref in self.created() {
1119 builder.push_record(vec![owned_objref_string(oref)]);
1120 }
1121 }
1122
1123 if !self.mutated().is_empty() {
1124 builder.push_record(vec!["Mutated Objects: ".to_string()]);
1125 for oref in self.mutated() {
1126 builder.push_record(vec![owned_objref_string(oref)]);
1127 }
1128 }
1129
1130 if !self.shared_objects().is_empty() {
1131 builder.push_record(vec!["Shared Objects: ".to_string()]);
1132 for oref in self.shared_objects() {
1133 builder.push_record(vec![objref_string(oref)]);
1134 }
1135 }
1136
1137 if !self.deleted().is_empty() {
1138 builder.push_record(vec!["Deleted Objects: ".to_string()]);
1139
1140 for oref in self.deleted() {
1141 builder.push_record(vec![objref_string(oref)]);
1142 }
1143 }
1144
1145 if !self.wrapped().is_empty() {
1146 builder.push_record(vec!["Wrapped Objects: ".to_string()]);
1147
1148 for oref in self.wrapped() {
1149 builder.push_record(vec![objref_string(oref)]);
1150 }
1151 }
1152
1153 if !self.unwrapped().is_empty() {
1154 builder.push_record(vec!["Unwrapped Objects: ".to_string()]);
1155 for oref in self.unwrapped() {
1156 builder.push_record(vec![owned_objref_string(oref)]);
1157 }
1158 }
1159
1160 builder.push_record(vec![format!(
1161 "Gas Object: \n{}",
1162 owned_objref_string(self.gas_object())
1163 )]);
1164
1165 let gas_cost_summary = self.gas_cost_summary();
1166 builder.push_record(vec![format!(
1167 "Gas Cost Summary:\n \
1168 Storage Cost: {} NANOS\n \
1169 Computation Cost: {} NANOS\n \
1170 Computation Cost Burned: {} NANOS\n \
1171 Storage Rebate: {} NANOS\n \
1172 Non-refundable Storage Fee: {} NANOS",
1173 gas_cost_summary.storage_cost,
1174 gas_cost_summary.computation_cost,
1175 gas_cost_summary.computation_cost_burned,
1176 gas_cost_summary.storage_rebate,
1177 gas_cost_summary.non_refundable_storage_fee,
1178 )]);
1179
1180 let dependencies = self.dependencies();
1181 if !dependencies.is_empty() {
1182 builder.push_record(vec!["\nTransaction Dependencies:".to_string()]);
1183 for dependency in dependencies {
1184 builder.push_record(vec![format!(" {dependency}")]);
1185 }
1186 }
1187
1188 let mut table = builder.build();
1189 table.with(TablePanel::header("Transaction Effects"));
1190 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1191 1,
1192 TableStyle::modern().get_horizontal(),
1193 )]));
1194 write!(f, "{table}")
1195 }
1196}
1197
1198#[serde_as]
1199#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1200#[serde(rename_all = "camelCase")]
1201pub struct DryRunTransactionBlockResponse {
1202 pub effects: IotaTransactionBlockEffects,
1203 pub events: IotaTransactionBlockEvents,
1204 pub object_changes: Vec<ObjectChange>,
1205 pub balance_changes: Vec<BalanceChange>,
1206 pub input: IotaTransactionBlockData,
1207 #[serde(default, skip_serializing_if = "Option::is_none")]
1209 #[schemars(with = "Option<String>")]
1210 #[serde_as(as = "Option<DisplayFromStr>")]
1211 pub suggested_gas_price: Option<u64>,
1212 pub execution_error_source: Option<String>,
1213}
1214
1215#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
1216#[serde(rename = "TransactionBlockEvents", transparent)]
1217pub struct IotaTransactionBlockEvents {
1218 pub data: Vec<IotaEvent>,
1219}
1220
1221impl IotaTransactionBlockEvents {
1222 pub fn try_from(
1223 mut events: TransactionEvents,
1224 tx_digest: TransactionDigest,
1225 timestamp_ms: Option<u64>,
1226 resolver: &mut dyn LayoutResolver,
1227 ) -> IotaResult<Self> {
1228 Ok(Self {
1229 data: events
1230 .drain(..)
1231 .enumerate()
1232 .map(|(seq, event)| {
1233 let layout = resolver.get_annotated_layout(&event.struct_tag)?;
1234 IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1235 })
1236 .collect::<Result<_, _>>()?,
1237 })
1238 }
1239
1240 pub fn try_from_using_module_resolver(
1243 mut events: TransactionEvents,
1244 tx_digest: TransactionDigest,
1245 timestamp_ms: Option<u64>,
1246 resolver: &impl GetModule,
1247 ) -> IotaResult<Self> {
1248 Ok(Self {
1249 data: events
1250 .drain(..)
1251 .enumerate()
1252 .map(|(seq, event)| {
1253 let layout = get_layout_from_struct_tag(event.struct_tag.clone(), resolver)?;
1254 IotaEvent::try_from(event, tx_digest, seq as u64, timestamp_ms, layout)
1255 })
1256 .collect::<Result<_, _>>()?,
1257 })
1258 }
1259}
1260
1261impl Display for IotaTransactionBlockEvents {
1262 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1263 if self.data.is_empty() {
1264 writeln!(f, "╭─────────────────────────────╮")?;
1265 writeln!(f, "│ No transaction block events │")?;
1266 writeln!(f, "╰─────────────────────────────╯")
1267 } else {
1268 let mut builder = TableBuilder::default();
1269
1270 for event in &self.data {
1271 builder.push_record(vec![format!("{event}")]);
1272 }
1273
1274 let mut table = builder.build();
1275 table.with(TablePanel::header("Transaction Block Events"));
1276 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1277 1,
1278 TableStyle::modern().get_horizontal(),
1279 )]));
1280 write!(f, "{table}")
1281 }
1282 }
1283}
1284
1285#[serde_as]
1289#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
1290#[serde(rename = "DevInspectArgs", rename_all = "camelCase")]
1291pub struct DevInspectArgs {
1292 #[serde_as(as = "Option<AddressSchema>")]
1295 #[schemars(with = "Option<AddressSchema>")]
1296 pub gas_sponsor: Option<Address>,
1297 #[schemars(with = "Option<String>")]
1299 #[serde_as(as = "Option<DisplayFromStr>")]
1300 pub gas_budget: Option<u64>,
1301 #[schemars(with = "Option<Vec<ObjectRefSchema>>")]
1303 #[serde_as(as = "Option<Vec<ObjectRefSchema>>")]
1304 pub gas_objects: Option<Vec<ObjectReference>>,
1305 pub skip_checks: Option<bool>,
1307 pub show_raw_txn_data_and_effects: Option<bool>,
1309}
1310
1311#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1313#[serde(rename = "DevInspectResults", rename_all = "camelCase")]
1314pub struct DevInspectResults {
1315 pub effects: IotaTransactionBlockEffects,
1320 pub events: IotaTransactionBlockEvents,
1323 #[serde(skip_serializing_if = "Option::is_none")]
1326 pub results: Option<Vec<IotaExecutionResult>>,
1327 #[serde(skip_serializing_if = "Option::is_none")]
1329 pub error: Option<String>,
1330 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1332 pub raw_txn_data: Vec<u8>,
1333 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1335 pub raw_effects: Vec<u8>,
1336}
1337
1338#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1339#[serde(rename = "IotaExecutionResult", rename_all = "camelCase")]
1340pub struct IotaExecutionResult {
1341 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1344 #[schemars(with = "Vec<(IotaArgument, Vec<u8>, TypeTagSchema)>")]
1345 pub mutable_reference_outputs: Vec<(IotaArgument, Vec<u8>, IotaTypeTag)>,
1346 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1348 #[schemars(with = "Vec<(Vec<u8>, TypeTagSchema)>")]
1349 pub return_values: Vec<(Vec<u8>, IotaTypeTag)>,
1350}
1351
1352impl IotaExecutionResult {
1353 fn into_stream_return_value_layouts<S: PackageStore>(
1354 self,
1355 package_resolver: &Resolver<S>,
1356 ) -> impl Stream<Item = anyhow::Result<(Vec<u8>, MoveTypeLayout)>> + use<'_, S> {
1357 self.return_values
1358 .into_iter()
1359 .map(|(bytes, iota_type_tag)| async {
1360 let type_tag = TypeTag::try_from(iota_type_tag)?;
1361 let move_type_layout = package_resolver.type_layout(type_tag).await?;
1362 Ok((bytes, move_type_layout))
1363 })
1364 .collect::<FuturesOrdered<_>>()
1365 }
1366}
1367
1368#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1369pub enum IotaMoveViewCallResults {
1370 #[serde(rename = "executionError")]
1372 Error(String),
1373 #[serde(rename = "functionReturnValues")]
1375 Results(Vec<IotaMoveValue>),
1376}
1377
1378impl IotaMoveViewCallResults {
1379 pub async fn from_dev_inspect_results<S: PackageStore>(
1382 package_store: S,
1383 dev_inspect_results: DevInspectResults,
1384 ) -> anyhow::Result<Self> {
1385 if let Some(error) = dev_inspect_results.error {
1386 return Ok(Self::Error(error));
1387 }
1388 let Some(mut tx_execution_results) = dev_inspect_results.results else {
1389 return Ok(Self::Error("function call returned no values".into()));
1390 };
1391 let Some(execution_results) = tx_execution_results.pop() else {
1392 return Ok(Self::Error(
1393 "no results from move view function call".into(),
1394 ));
1395 };
1396 if !tx_execution_results.is_empty() {
1397 return Ok(Self::Error("multiple transactions executed".into()));
1398 }
1399 let mut move_call_results = Vec::with_capacity(execution_results.return_values.len());
1400 let package_resolver = Resolver::new(package_store);
1401 let mut execution_results =
1402 execution_results.into_stream_return_value_layouts(&package_resolver);
1403 while let Some(result) = execution_results.next().await {
1404 let (bytes, move_type_layout) = result?;
1405 let move_value = BoundedVisitor::deserialize_value(&bytes, &move_type_layout)?;
1406 move_call_results.push(IotaMoveValue::from(move_value));
1407 }
1408 Ok(Self::Results(move_call_results))
1409 }
1410
1411 pub fn into_return_values(self) -> Vec<IotaMoveValue> {
1412 match self {
1413 IotaMoveViewCallResults::Error(_) => Default::default(),
1414 IotaMoveViewCallResults::Results(values) => values,
1415 }
1416 }
1417
1418 pub fn error(&self) -> Option<&str> {
1419 match self {
1420 IotaMoveViewCallResults::Error(e) => Some(e.as_str()),
1421 IotaMoveViewCallResults::Results(_) => None,
1422 }
1423 }
1424}
1425
1426type ExecutionResult = (
1427 Vec<(Argument, Vec<u8>, TypeTag)>,
1429 Vec<(Vec<u8>, TypeTag)>,
1431);
1432
1433impl DevInspectResults {
1434 pub fn new(
1435 effects: TransactionEffects,
1436 events: TransactionEvents,
1437 return_values: Result<Vec<ExecutionResult>, ExecutionError>,
1438 raw_txn_data: Vec<u8>,
1439 raw_effects: Vec<u8>,
1440 resolver: &mut dyn LayoutResolver,
1441 ) -> IotaResult<Self> {
1442 let tx_digest = *effects.transaction_digest();
1443 let mut error = None;
1444 let mut results = None;
1445 match return_values {
1446 Err(e) => error = Some(e.to_string()),
1447 Ok(srvs) => {
1448 results = Some(
1449 srvs.into_iter()
1450 .map(|srv| {
1451 let (mutable_reference_outputs, return_values) = srv;
1452 let mutable_reference_outputs = mutable_reference_outputs
1453 .into_iter()
1454 .map(|(a, bytes, tag)| (a.into(), bytes, IotaTypeTag::from(tag)))
1455 .collect();
1456 let return_values = return_values
1457 .into_iter()
1458 .map(|(bytes, tag)| (bytes, IotaTypeTag::from(tag)))
1459 .collect();
1460 IotaExecutionResult {
1461 mutable_reference_outputs,
1462 return_values,
1463 }
1464 })
1465 .collect(),
1466 )
1467 }
1468 };
1469 Ok(Self {
1470 effects: effects.try_into()?,
1471 events: IotaTransactionBlockEvents::try_from(events, tx_digest, None, resolver)?,
1472 results,
1473 error,
1474 raw_txn_data,
1475 raw_effects,
1476 })
1477 }
1478}
1479
1480#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1481pub enum IotaTransactionBlockBuilderMode {
1482 Commit,
1484 DevInspect,
1487}
1488
1489#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
1490#[serde(rename = "ExecutionStatus", rename_all = "camelCase", tag = "status")]
1491pub enum IotaExecutionStatus {
1492 Success,
1494 Failure { error: String },
1496}
1497
1498impl IotaExecutionStatus {
1499 pub async fn from_native_with_clever_error<S: PackageStore>(
1505 native: ExecutionStatus,
1506 resolver: &Resolver<S>,
1507 ) -> Self {
1508 match native {
1509 ExecutionStatus::Failure {
1510 error,
1511 command: Some(mut command_index),
1512 } => {
1513 let error = 'error: {
1514 let ExecutionFailureStatus::MoveAbort { location, code } = &error else {
1515 break 'error error.to_string();
1516 };
1517 let fname_string = if let Some(fname) = &location.function_name {
1518 format!("::{fname}'")
1519 } else {
1520 "'".to_string()
1521 };
1522
1523 let module_id = ModuleId::new(
1524 AccountAddress::from(location.package.into_bytes()),
1525 identifier_sdk_to_core(&location.module),
1526 );
1527
1528 let Some(CleverError {
1529 module_id,
1530 source_line_number,
1531 error_info,
1532 error_code,
1533 }) = resolver
1534 .resolve_clever_error(module_id.clone(), *code)
1535 .await
1536 else {
1537 break 'error format!(
1538 "from '{}{fname_string} (instruction {}), abort code: {code}",
1539 module_id.to_canonical_display(true),
1540 location.instruction,
1541 );
1542 };
1543
1544 let error_code_str = match error_code {
1545 Some(code) => format!("(code = {code})"),
1546 None => String::new(),
1547 };
1548
1549 match error_info {
1550 ErrorConstants::Rendered {
1551 identifier,
1552 constant,
1553 } => {
1554 format!(
1555 "from '{}{fname_string} (line {source_line_number}), abort{error_code_str} '{identifier}': {constant}",
1556 module_id.to_canonical_display(true)
1557 )
1558 }
1559 ErrorConstants::Raw { identifier, bytes } => {
1560 let const_str = Base64::encode(bytes);
1561 format!(
1562 "from '{}{fname_string} (line {source_line_number}), abort{error_code_str} '{identifier}': {const_str}",
1563 module_id.to_canonical_display(true)
1564 )
1565 }
1566 ErrorConstants::None => {
1567 format!(
1568 "from '{}{fname_string} (line {source_line_number}){}",
1569 module_id.to_canonical_display(true),
1570 match error_code {
1571 Some(code) => format!(" abort(code = {code})"),
1572 None => String::new(),
1573 }
1574 )
1575 }
1576 }
1577 };
1578 command_index += 1;
1580 let suffix = match command_index % 10 {
1581 1 if command_index % 100 != 11 => "st",
1582 2 if command_index % 100 != 12 => "nd",
1583 3 if command_index % 100 != 13 => "rd",
1584 _ => "th",
1585 };
1586 IotaExecutionStatus::Failure {
1587 error: format!("Error in {command_index}{suffix} command, {error}"),
1588 }
1589 }
1590 _ => native.into(),
1591 }
1592 }
1593}
1594
1595impl Display for IotaExecutionStatus {
1596 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1597 match self {
1598 Self::Success => write!(f, "success"),
1599 Self::Failure { error } => write!(f, "failure due to {error}"),
1600 }
1601 }
1602}
1603
1604impl IotaExecutionStatus {
1605 pub fn is_ok(&self) -> bool {
1606 matches!(self, IotaExecutionStatus::Success)
1607 }
1608 pub fn is_err(&self) -> bool {
1609 matches!(self, IotaExecutionStatus::Failure { .. })
1610 }
1611}
1612
1613impl From<ExecutionStatus> for IotaExecutionStatus {
1614 fn from(status: ExecutionStatus) -> Self {
1615 match status {
1616 ExecutionStatus::Success => Self::Success,
1617 ExecutionStatus::Failure {
1618 error,
1619 command: None,
1620 } => Self::Failure {
1621 error: error.to_string(),
1622 },
1623 ExecutionStatus::Failure {
1624 error,
1625 command: Some(idx),
1626 } => Self::Failure {
1627 error: format!("{error} in command {idx}"),
1628 },
1629 _ => unimplemented!(
1630 "a new ExecutionStatus enum variant was added and needs to be handled"
1631 ),
1632 }
1633 }
1634}
1635
1636fn to_owned_ref(owned_refs: Vec<OwnedObjectReference>) -> Vec<OwnedObjectRef> {
1637 owned_refs
1638 .into_iter()
1639 .map(|owned| OwnedObjectRef {
1640 owner: owned.owner,
1641 reference: owned.reference,
1642 })
1643 .collect()
1644}
1645
1646#[serde_as]
1647#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1648#[serde(rename = "GasData", rename_all = "camelCase")]
1649pub struct IotaGasData {
1650 #[schemars(with = "Vec<ObjectRefSchema>")]
1651 #[serde_as(as = "Vec<ObjectRefSchema>")]
1652 pub payment: Vec<ObjectReference>,
1653 #[serde_as(as = "AddressSchema")]
1654 #[schemars(with = "AddressSchema")]
1655 pub owner: Address,
1656 #[schemars(with = "String")]
1657 #[serde_as(as = "DisplayFromStr")]
1658 pub price: u64,
1659 #[schemars(with = "String")]
1660 #[serde_as(as = "DisplayFromStr")]
1661 pub budget: u64,
1662}
1663
1664impl Display for IotaGasData {
1665 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1666 writeln!(f, "Gas Owner: {}", self.owner)?;
1667 writeln!(f, "Gas Budget: {} NANOS", self.budget)?;
1668 writeln!(f, "Gas Price: {} NANOS", self.price)?;
1669 writeln!(f, "Gas Payment:")?;
1670 for payment in &self.payment {
1671 write!(f, "{} ", objref_string(payment))?;
1672 }
1673 writeln!(f)
1674 }
1675}
1676
1677#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1678#[enum_dispatch(IotaTransactionBlockDataAPI)]
1679#[serde(
1680 rename = "TransactionBlockData",
1681 rename_all = "camelCase",
1682 tag = "messageVersion"
1683)]
1684pub enum IotaTransactionBlockData {
1685 V1(IotaTransactionBlockDataV1),
1686}
1687
1688#[enum_dispatch]
1689pub trait IotaTransactionBlockDataAPI {
1690 fn transaction(&self) -> &IotaTransactionBlockKind;
1691 fn sender(&self) -> &Address;
1692 fn gas_data(&self) -> &IotaGasData;
1693}
1694
1695#[serde_as]
1696#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1697#[serde(rename = "TransactionBlockDataV1", rename_all = "camelCase")]
1698pub struct IotaTransactionBlockDataV1 {
1699 pub transaction: IotaTransactionBlockKind,
1700 #[serde_as(as = "AddressSchema")]
1701 #[schemars(with = "AddressSchema")]
1702 pub sender: Address,
1703 pub gas_data: IotaGasData,
1704}
1705
1706impl IotaTransactionBlockDataAPI for IotaTransactionBlockDataV1 {
1707 fn transaction(&self) -> &IotaTransactionBlockKind {
1708 &self.transaction
1709 }
1710 fn sender(&self) -> &Address {
1711 &self.sender
1712 }
1713 fn gas_data(&self) -> &IotaGasData {
1714 &self.gas_data
1715 }
1716}
1717
1718impl IotaTransactionBlockData {
1719 pub fn move_calls(&self) -> Vec<&IotaProgrammableMoveCall> {
1720 match self {
1721 Self::V1(data) => match &data.transaction {
1722 IotaTransactionBlockKind::ProgrammableTransaction(pt) => pt
1723 .commands
1724 .iter()
1725 .filter_map(|command| match command {
1726 IotaCommand::MoveCall(c) => Some(&**c),
1727 _ => None,
1728 })
1729 .collect(),
1730 _ => vec![],
1731 },
1732 }
1733 }
1734
1735 fn try_from_inner(
1736 tx: Transaction,
1737 transaction: IotaTransactionBlockKind,
1738 ) -> Result<Self, anyhow::Error> {
1739 let message_version = tx.message_version();
1740 let sender = tx.sender();
1741 let gas_data = IotaGasData {
1742 payment: tx.gas().to_vec(),
1743 owner: tx.gas_owner(),
1744 price: tx.gas_price(),
1745 budget: tx.gas_budget(),
1746 };
1747
1748 match message_version {
1749 1 => Ok(IotaTransactionBlockData::V1(IotaTransactionBlockDataV1 {
1750 transaction,
1751 sender,
1752 gas_data,
1753 })),
1754 _ => Err(anyhow::anyhow!(
1755 "Support for Transaction version {message_version} not implemented"
1756 )),
1757 }
1758 }
1759
1760 pub fn try_from_with_module_cache(
1761 tx: Transaction,
1762 module_cache: &impl GetModule,
1763 tx_digest: TransactionDigest,
1764 ) -> Result<Self, anyhow::Error> {
1765 let transaction = IotaTransactionBlockKind::try_from_with_module_cache(
1766 tx.kind().clone(),
1767 module_cache,
1768 tx_digest,
1769 )?;
1770 Self::try_from_inner(tx, transaction)
1771 }
1772
1773 pub async fn try_from_with_package_resolver(
1774 tx: Transaction,
1775 package_resolver: &Resolver<impl PackageStore>,
1776 tx_digest: TransactionDigest,
1777 ) -> Result<Self, anyhow::Error> {
1778 let transaction = IotaTransactionBlockKind::try_from_with_package_resolver(
1779 tx.kind().clone(),
1780 package_resolver,
1781 tx_digest,
1782 )
1783 .await?;
1784 Self::try_from_inner(tx, transaction)
1785 }
1786}
1787
1788impl Display for IotaTransactionBlockData {
1789 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1790 match self {
1791 Self::V1(data) => {
1792 writeln!(f, "Sender: {}", data.sender)?;
1793 writeln!(f, "{}", self.gas_data())?;
1794 writeln!(f, "{}", data.transaction)
1795 }
1796 }
1797 }
1798}
1799
1800#[serde_as]
1801#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)]
1802#[serde(rename = "TransactionBlock", rename_all = "camelCase")]
1803pub struct IotaTransactionBlock {
1804 pub data: IotaTransactionBlockData,
1805 #[serde_as(as = "Vec<UserSignatureSchema>")]
1806 #[schemars(with = "Vec<UserSignatureSchema>")]
1807 pub tx_signatures: Vec<UserSignature>,
1808}
1809
1810impl IotaTransactionBlock {
1811 pub fn try_from(
1812 tx: SenderSignedTransaction,
1813 module_cache: &impl GetModule,
1814 tx_digest: TransactionDigest,
1815 ) -> Result<Self, anyhow::Error> {
1816 Ok(Self {
1817 data: IotaTransactionBlockData::try_from_with_module_cache(
1818 tx.transaction().clone(),
1819 module_cache,
1820 tx_digest,
1821 )?,
1822 tx_signatures: tx.signatures().to_vec(),
1823 })
1824 }
1825
1826 pub async fn try_from_with_package_resolver(
1830 tx: SenderSignedTransaction,
1831 package_resolver: &Resolver<impl PackageStore>,
1832 tx_digest: TransactionDigest,
1833 ) -> Result<Self, anyhow::Error> {
1834 Ok(Self {
1835 data: IotaTransactionBlockData::try_from_with_package_resolver(
1836 tx.transaction().clone(),
1837 package_resolver,
1838 tx_digest,
1839 )
1840 .await?,
1841 tx_signatures: tx.signatures().to_vec(),
1842 })
1843 }
1844}
1845
1846impl Display for IotaTransactionBlock {
1847 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1848 let mut builder = TableBuilder::default();
1849
1850 builder.push_record(vec![format!("{}", self.data)]);
1851 builder.push_record(vec!["Signatures:".to_string()]);
1852 for tx_sig in &self.tx_signatures {
1853 builder.push_record(vec![format!(
1854 " {}\n",
1855 match tx_sig {
1856 UserSignature::Simple(sig) =>
1857 Base64::from_bytes(sig.signature_bytes()).encoded(),
1858 _ => Base64::from_bytes(&tx_sig.to_bytes()).encoded(),
1862 }
1863 )]);
1864 }
1865
1866 let mut table = builder.build();
1867 table.with(TablePanel::header("Transaction Data"));
1868 table.with(TableStyle::rounded().horizontals([HorizontalLine::new(
1869 1,
1870 TableStyle::modern().get_horizontal(),
1871 )]));
1872 write!(f, "{table}")
1873 }
1874}
1875
1876#[serde_as]
1877#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1878pub struct IotaGenesisTransaction {
1879 #[serde_as(as = "Vec<ObjectIdSchema>")]
1880 #[schemars(with = "Vec<ObjectIdSchema>")]
1881 pub objects: Vec<ObjectId>,
1882 #[schemars(with = "Vec<IotaEventID>")]
1883 pub events: Vec<EventID>,
1884}
1885
1886#[serde_as]
1887#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1888pub struct IotaConsensusCommitPrologueV1 {
1889 #[schemars(with = "String")]
1890 #[serde_as(as = "DisplayFromStr")]
1891 pub epoch: u64,
1892 #[schemars(with = "String")]
1893 #[serde_as(as = "DisplayFromStr")]
1894 pub round: u64,
1895 #[schemars(with = "Option<String>")]
1896 #[serde_as(as = "Option<DisplayFromStr>")]
1897 pub sub_dag_index: Option<u64>,
1898 #[schemars(with = "String")]
1899 #[serde_as(as = "DisplayFromStr")]
1900 pub commit_timestamp_ms: u64,
1901 #[serde_as(as = "Base58Schema")]
1902 #[schemars(with = "Base58Schema")]
1903 pub consensus_commit_digest: ConsensusCommitDigest,
1904 pub consensus_determined_version_assignments: IotaConsensusDeterminedVersionAssignments,
1905}
1906
1907#[serde_as]
1910#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
1911#[schemars(rename = "ConsensusDeterminedVersionAssignments")]
1912pub enum IotaConsensusDeterminedVersionAssignments {
1913 CancelledTransactions(
1915 #[serde_as(as = "Vec<(Base58Schema, Vec<(ObjectIdSchema, serde_with::Same)>)>")]
1916 #[schemars(with = "Vec<(Base58Schema, Vec<(ObjectIdSchema, SequenceNumberU64)>)>")]
1917 Vec<(TransactionDigest, Vec<(ObjectId, SequenceNumberU64)>)>,
1918 ),
1919}
1920
1921impl From<ConsensusDeterminedVersionAssignments> for IotaConsensusDeterminedVersionAssignments {
1922 fn from(
1923 consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments,
1924 ) -> Self {
1925 match consensus_determined_version_assignments {
1926 ConsensusDeterminedVersionAssignments::CanceledTransactions {
1927 canceled_transactions,
1928 } => IotaConsensusDeterminedVersionAssignments::CancelledTransactions(
1929 canceled_transactions
1930 .into_iter()
1931 .map(|cancelled| {
1932 (
1933 cancelled.digest,
1934 cancelled
1935 .version_assignments
1936 .into_iter()
1937 .map(|va| (va.object_id, va.version.into()))
1938 .collect(),
1939 )
1940 })
1941 .collect(),
1942 ),
1943 _ => unimplemented!(
1944 "a new ConsensusDeterminedVersionAssignments enum variant was added and needs to be handled"
1945 ),
1946 }
1947 }
1948}
1949
1950impl From<IotaConsensusDeterminedVersionAssignments> for ConsensusDeterminedVersionAssignments {
1951 fn from(
1952 iota_consensus_determined_version_assignments: IotaConsensusDeterminedVersionAssignments,
1953 ) -> Self {
1954 match iota_consensus_determined_version_assignments {
1955 IotaConsensusDeterminedVersionAssignments::CancelledTransactions(assignments) => {
1956 ConsensusDeterminedVersionAssignments::CanceledTransactions {
1957 canceled_transactions: assignments
1958 .into_iter()
1959 .map(|(digest, version_assignments)| CanceledTransaction {
1960 digest,
1961 version_assignments: version_assignments
1962 .into_iter()
1963 .map(|(object_id, version)| VersionAssignment {
1964 object_id,
1965 version: version.into(),
1966 })
1967 .collect(),
1968 })
1969 .collect(),
1970 }
1971 }
1972 }
1973 }
1974}
1975
1976#[serde_as]
1977#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1978pub struct IotaRandomnessStateUpdate {
1979 #[schemars(with = "String")]
1980 #[serde_as(as = "DisplayFromStr")]
1981 pub epoch: u64,
1982
1983 #[schemars(with = "String")]
1984 #[serde_as(as = "DisplayFromStr")]
1985 pub randomness_round: u64,
1986 pub random_bytes: Vec<u8>,
1987}
1988
1989#[serde_as]
1993#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1994pub struct IotaTransactionDenyRulesUpdate {
1995 #[schemars(with = "String")]
1996 #[serde_as(as = "DisplayFromStr")]
1997 pub epoch: u64,
1998
1999 #[schemars(with = "String")]
2000 #[serde_as(as = "DisplayFromStr")]
2001 pub round: u64,
2002 #[schemars(with = "Vec<AddressSchema>")]
2003 #[serde_as(as = "Vec<AddressSchema>")]
2004 pub added_addresses: Vec<Address>,
2005 #[schemars(with = "Vec<AddressSchema>")]
2006 #[serde_as(as = "Vec<AddressSchema>")]
2007 pub removed_addresses: Vec<Address>,
2008 #[schemars(with = "Vec<ObjectIdSchema>")]
2009 #[serde_as(as = "Vec<ObjectIdSchema>")]
2010 pub added_objects: Vec<ObjectId>,
2011 #[schemars(with = "Vec<ObjectIdSchema>")]
2012 #[serde_as(as = "Vec<ObjectIdSchema>")]
2013 pub removed_objects: Vec<ObjectId>,
2014 #[schemars(with = "Vec<ObjectIdSchema>")]
2015 #[serde_as(as = "Vec<ObjectIdSchema>")]
2016 pub added_packages: Vec<ObjectId>,
2017 #[schemars(with = "Vec<ObjectIdSchema>")]
2018 #[serde_as(as = "Vec<ObjectIdSchema>")]
2019 pub removed_packages: Vec<ObjectId>,
2020 pub package_publish_disabled: bool,
2021 pub package_upgrade_disabled: bool,
2022 pub shared_object_disabled: bool,
2023 pub user_transaction_disabled: bool,
2024 pub receiving_objects_disabled: bool,
2025 pub move_authenticator_disabled: bool,
2026}
2027
2028#[serde_as]
2029#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2030pub struct IotaEndOfEpochTransaction {
2031 pub transactions: Vec<IotaEndOfEpochTransactionKind>,
2032}
2033
2034#[serde_as]
2035#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2036pub enum IotaEndOfEpochTransactionKind {
2037 ChangeEpoch(IotaChangeEpoch),
2038 ChangeEpochV2(IotaChangeEpochV2),
2039 TransactionDenyRulesCreate,
2040}
2041
2042#[serde_as]
2043#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
2044#[serde(rename = "InputObjectKind")]
2045pub enum IotaInputObjectKind {
2046 MovePackage(
2048 #[serde_as(as = "ObjectIdSchema")]
2049 #[schemars(with = "ObjectIdSchema")]
2050 ObjectId,
2051 ),
2052 ImmOrOwnedMoveObject(
2054 #[schemars(with = "ObjectRefSchema")]
2055 #[serde_as(as = "ObjectRefSchema")]
2056 ObjectReference,
2057 ),
2058 SharedMoveObject {
2060 #[serde_as(as = "ObjectIdSchema")]
2061 #[schemars(with = "ObjectIdSchema")]
2062 id: ObjectId,
2063 #[schemars(with = "SequenceNumberStringSchema")]
2064 #[serde_as(as = "SequenceNumberStringSchema")]
2065 initial_shared_version: Version,
2066 #[serde(default = "default_shared_object_mutability")]
2067 mutable: bool,
2068 },
2069}
2070
2071#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2074pub struct IotaProgrammableTransactionBlock {
2075 pub inputs: Vec<IotaCallArg>,
2077 #[serde(rename = "transactions")]
2078 pub commands: Vec<IotaCommand>,
2082}
2083
2084impl Display for IotaProgrammableTransactionBlock {
2085 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2086 let Self { inputs, commands } = self;
2087 writeln!(f, "Inputs: {inputs:?}")?;
2088 writeln!(f, "Commands: [")?;
2089 for c in commands {
2090 writeln!(f, " {c},")?;
2091 }
2092 writeln!(f, "]")
2093 }
2094}
2095
2096impl IotaProgrammableTransactionBlock {
2097 fn try_from_with_module_cache(
2098 value: ProgrammableTransaction,
2099 module_cache: &impl GetModule,
2100 ) -> Result<Self, anyhow::Error> {
2101 let ProgrammableTransaction { inputs, commands } = value;
2102 let input_types = Self::resolve_input_type(&inputs, &commands, module_cache);
2103 Ok(IotaProgrammableTransactionBlock {
2104 inputs: inputs
2105 .into_iter()
2106 .zip(input_types)
2107 .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
2108 .collect::<Result<_, _>>()?,
2109 commands: commands.into_iter().map(IotaCommand::from).collect(),
2110 })
2111 }
2112
2113 async fn try_from_with_package_resolver(
2114 value: ProgrammableTransaction,
2115 package_resolver: &Resolver<impl PackageStore>,
2116 ) -> Result<Self, anyhow::Error> {
2117 let input_types = package_resolver
2120 .pure_input_layouts(&value)
2121 .await
2122 .unwrap_or_else(|e| {
2123 tracing::warn!("pure_input_layouts failed: {:?}", e);
2124 vec![None; value.inputs.len()]
2125 });
2126
2127 let ProgrammableTransaction { inputs, commands } = value;
2128 Ok(IotaProgrammableTransactionBlock {
2129 inputs: inputs
2130 .into_iter()
2131 .zip(input_types)
2132 .map(|(arg, layout)| IotaCallArg::try_from(arg, layout.as_ref()))
2133 .collect::<Result<_, _>>()?,
2134 commands: commands.into_iter().map(IotaCommand::from).collect(),
2135 })
2136 }
2137
2138 fn resolve_input_type(
2139 inputs: &[CallArg],
2140 commands: &[Command],
2141 module_cache: &impl GetModule,
2142 ) -> Vec<Option<MoveTypeLayout>> {
2143 let mut result_types = vec![None; inputs.len()];
2144 for command in commands.iter() {
2145 match command {
2146 Command::MoveCall(cmd) => {
2147 let module = identifier_sdk_to_core(&cmd.module);
2148 let id = ModuleId::new(AccountAddress::new(cmd.package.into_bytes()), module);
2149 let Some(types) = get_signature_types(id, &cmd.function, module_cache) else {
2150 return result_types;
2151 };
2152 for (arg, type_) in cmd.arguments.iter().zip(types) {
2153 if let (&Argument::Input(i), Some(type_)) = (arg, type_) {
2154 if let Some(x) = result_types.get_mut(i as usize) {
2155 x.replace(type_);
2156 }
2157 }
2158 }
2159 }
2160 Command::SplitCoins(cmd) => {
2161 for arg in &cmd.amounts {
2162 if let &Argument::Input(i) = arg {
2163 if let Some(x) = result_types.get_mut(i as usize) {
2164 x.replace(MoveTypeLayout::U64);
2165 }
2166 }
2167 }
2168 }
2169 Command::TransferObjects(TransferObjects {
2170 address: Argument::Input(i),
2171 ..
2172 }) => {
2173 if let Some(x) = result_types.get_mut((*i) as usize) {
2174 x.replace(MoveTypeLayout::Address);
2175 }
2176 }
2177 _ => {}
2178 }
2179 }
2180 result_types
2181 }
2182}
2183
2184fn get_signature_types(
2185 id: ModuleId,
2186 function: &Identifier,
2187 module_cache: &impl GetModule,
2188) -> Option<Vec<Option<MoveTypeLayout>>> {
2189 use std::borrow::Borrow;
2190 if let Ok(Some(module)) = module_cache.get_module_by_id(&id) {
2191 let module: &CompiledModule = module.borrow();
2192 let func = module
2193 .function_handles
2194 .iter()
2195 .find(|f| module.identifier_at(f.name).as_str() == function.as_str())?;
2196 Some(
2197 module
2198 .signature_at(func.parameters)
2199 .0
2200 .iter()
2201 .map(|s| primitive_type(module, &[], s))
2202 .collect(),
2203 )
2204 } else {
2205 None
2206 }
2207}
2208
2209#[serde_as]
2211#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2212#[serde(rename = "IotaTransaction")]
2213pub enum IotaCommand {
2214 MoveCall(Box<IotaProgrammableMoveCall>),
2216 TransferObjects(Vec<IotaArgument>, IotaArgument),
2221 SplitCoins(IotaArgument, Vec<IotaArgument>),
2224 MergeCoins(IotaArgument, Vec<IotaArgument>),
2227 Publish(
2230 #[serde_as(as = "Vec<ObjectIdSchema>")]
2231 #[schemars(with = "Vec<ObjectIdSchema>")]
2232 Vec<ObjectId>,
2233 ),
2234 Upgrade(
2236 #[serde_as(as = "Vec<ObjectIdSchema>")]
2237 #[schemars(with = "Vec<ObjectIdSchema>")]
2238 Vec<ObjectId>,
2239 #[serde_as(as = "ObjectIdSchema")]
2240 #[schemars(with = "ObjectIdSchema")]
2241 ObjectId,
2242 IotaArgument,
2243 ),
2244 MakeMoveVec(Option<String>, Vec<IotaArgument>),
2248}
2249
2250impl Display for IotaCommand {
2251 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2252 match self {
2253 Self::MoveCall(p) => {
2254 write!(f, "MoveCall({p})")
2255 }
2256 Self::MakeMoveVec(ty_opt, elems) => {
2257 write!(f, "MakeMoveVec(")?;
2258 if let Some(ty) = ty_opt {
2259 write!(f, "Some{ty}")?;
2260 } else {
2261 write!(f, "None")?;
2262 }
2263 write!(f, ",[")?;
2264 write_sep(f, elems, ",")?;
2265 write!(f, "])")
2266 }
2267 Self::TransferObjects(objs, addr) => {
2268 write!(f, "TransferObjects([")?;
2269 write_sep(f, objs, ",")?;
2270 write!(f, "],{addr})")
2271 }
2272 Self::SplitCoins(coin, amounts) => {
2273 write!(f, "SplitCoins({coin},")?;
2274 write_sep(f, amounts, ",")?;
2275 write!(f, ")")
2276 }
2277 Self::MergeCoins(target, coins) => {
2278 write!(f, "MergeCoins({target},")?;
2279 write_sep(f, coins, ",")?;
2280 write!(f, ")")
2281 }
2282 Self::Publish(deps) => {
2283 write!(f, "Publish(<modules>,")?;
2284 write_sep(f, deps, ",")?;
2285 write!(f, ")")
2286 }
2287 Self::Upgrade(deps, current_package_id, ticket) => {
2288 write!(f, "Upgrade(<modules>, {ticket},")?;
2289 write_sep(f, deps, ",")?;
2290 write!(f, ", {current_package_id}")?;
2291 write!(f, ")")
2292 }
2293 }
2294 }
2295}
2296
2297impl From<Command> for IotaCommand {
2298 fn from(value: Command) -> Self {
2299 match value {
2300 Command::MoveCall(cmd) => IotaCommand::MoveCall(Box::new((cmd).into())),
2301 Command::TransferObjects(cmd) => IotaCommand::TransferObjects(
2302 cmd.objects.into_iter().map(IotaArgument::from).collect(),
2303 cmd.address.into(),
2304 ),
2305 Command::SplitCoins(cmd) => IotaCommand::SplitCoins(
2306 cmd.coin.into(),
2307 cmd.amounts.into_iter().map(IotaArgument::from).collect(),
2308 ),
2309 Command::MergeCoins(cmd) => IotaCommand::MergeCoins(
2310 cmd.coin.into(),
2311 cmd.coins_to_merge
2312 .into_iter()
2313 .map(IotaArgument::from)
2314 .collect(),
2315 ),
2316 Command::Publish(cmd) => IotaCommand::Publish(cmd.dependencies),
2317 Command::MakeMoveVector(cmd) => IotaCommand::MakeMoveVec(
2318 cmd.type_tag.map(|tag| tag.to_string()),
2319 cmd.elements.into_iter().map(IotaArgument::from).collect(),
2320 ),
2321 Command::Upgrade(cmd) => IotaCommand::Upgrade(
2322 cmd.dependencies,
2323 cmd.package,
2324 IotaArgument::from(cmd.ticket),
2325 ),
2326 _ => unimplemented!("a new Command enum variant was added and needs to be handled"),
2327 }
2328 }
2329}
2330
2331#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2333pub enum IotaArgument {
2334 GasCoin,
2337 Input(u16),
2340 Result(u16),
2343 NestedResult(u16, u16),
2347}
2348
2349impl Display for IotaArgument {
2350 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2351 match self {
2352 Self::GasCoin => write!(f, "GasCoin"),
2353 Self::Input(i) => write!(f, "Input({i})"),
2354 Self::Result(i) => write!(f, "Result({i})"),
2355 Self::NestedResult(i, j) => write!(f, "NestedResult({i},{j})"),
2356 }
2357 }
2358}
2359
2360impl From<Argument> for IotaArgument {
2361 fn from(value: Argument) -> Self {
2362 match value {
2363 Argument::Gas => Self::GasCoin,
2364 Argument::Input(i) => Self::Input(i),
2365 Argument::Result(i) => Self::Result(i),
2366 Argument::NestedResult(i, j) => Self::NestedResult(i, j),
2367 _ => unimplemented!("a new Argument enum variant was added and needs to be handled"),
2368 }
2369 }
2370}
2371
2372#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2373#[serde(untagged)]
2374pub enum PtbInput {
2375 PtbRef(IotaArgument),
2376 CallArg(IotaJsonValue),
2377}
2378
2379#[serde_as]
2382#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2383pub struct IotaProgrammableMoveCall {
2384 #[serde_as(as = "ObjectIdSchema")]
2386 #[schemars(with = "ObjectIdSchema")]
2387 pub package: ObjectId,
2388 pub module: String,
2390 pub function: String,
2392 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2393 pub type_arguments: Vec<String>,
2395 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2396 pub arguments: Vec<IotaArgument>,
2398}
2399
2400fn write_sep<T: Display>(
2401 f: &mut Formatter<'_>,
2402 items: impl IntoIterator<Item = T>,
2403 sep: &str,
2404) -> std::fmt::Result {
2405 let mut xs = items.into_iter().peekable();
2406 while let Some(x) = xs.next() {
2407 write!(f, "{x}")?;
2408 if xs.peek().is_some() {
2409 write!(f, "{sep}")?;
2410 }
2411 }
2412 Ok(())
2413}
2414
2415impl Display for IotaProgrammableMoveCall {
2416 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2417 let Self {
2418 package,
2419 module,
2420 function,
2421 type_arguments,
2422 arguments,
2423 } = self;
2424 write!(f, "{package}::{module}::{function}")?;
2425 if !type_arguments.is_empty() {
2426 write!(f, "<")?;
2427 write_sep(f, type_arguments, ",")?;
2428 write!(f, ">")?;
2429 }
2430 write!(f, "(")?;
2431 write_sep(f, arguments, ",")?;
2432 write!(f, ")")
2433 }
2434}
2435
2436impl From<MoveCall> for IotaProgrammableMoveCall {
2437 fn from(value: MoveCall) -> Self {
2438 let MoveCall {
2439 package,
2440 module,
2441 function,
2442 type_arguments,
2443 arguments,
2444 } = value;
2445 Self {
2446 package,
2447 module: module.to_string(),
2448 function: function.to_string(),
2449 type_arguments: type_arguments.into_iter().map(|t| t.to_string()).collect(),
2450 arguments: arguments.into_iter().map(IotaArgument::from).collect(),
2451 }
2452 }
2453}
2454
2455const fn default_shared_object_mutability() -> bool {
2456 true
2457}
2458
2459impl From<InputObjectKind> for IotaInputObjectKind {
2460 fn from(input: InputObjectKind) -> Self {
2461 match input {
2462 InputObjectKind::MovePackage(id) => Self::MovePackage(id),
2463 InputObjectKind::ImmOrOwnedMoveObject(oref) => Self::ImmOrOwnedMoveObject(oref),
2464 InputObjectKind::SharedMoveObject {
2465 id,
2466 initial_shared_version,
2467 mutable,
2468 } => Self::SharedMoveObject {
2469 id,
2470 initial_shared_version,
2471 mutable,
2472 },
2473 }
2474 }
2475}
2476
2477#[derive(Debug, Serialize, Deserialize, Clone)]
2478#[serde(rename = "TypeTag", rename_all = "camelCase")]
2479pub struct IotaTypeTag(String);
2480
2481impl IotaTypeTag {
2482 pub fn new(tag: String) -> Self {
2483 Self(tag)
2484 }
2485}
2486
2487impl AsRef<str> for IotaTypeTag {
2488 fn as_ref(&self) -> &str {
2489 &self.0
2490 }
2491}
2492
2493impl TryFrom<IotaTypeTag> for TypeTag {
2494 type Error = anyhow::Error;
2495 fn try_from(tag: IotaTypeTag) -> Result<Self, Self::Error> {
2496 parse_iota_type_tag(&tag.0)
2497 }
2498}
2499
2500impl From<TypeTag> for IotaTypeTag {
2501 fn from(tag: TypeTag) -> Self {
2502 Self(format!("{tag}"))
2503 }
2504}
2505
2506#[derive(Serialize, Deserialize, JsonSchema)]
2507#[serde(rename_all = "camelCase")]
2508pub enum RPCTransactionRequestParams {
2509 TransferObjectRequestParams(TransferObjectParams),
2510 MoveCallRequestParams(MoveCallParams),
2511}
2512
2513#[serde_as]
2514#[derive(Serialize, Deserialize, JsonSchema)]
2515#[serde(rename_all = "camelCase")]
2516pub struct TransferObjectParams {
2517 #[serde_as(as = "AddressSchema")]
2518 #[schemars(with = "AddressSchema")]
2519 pub recipient: Address,
2520 #[serde_as(as = "ObjectIdSchema")]
2521 #[schemars(with = "ObjectIdSchema")]
2522 pub object_id: ObjectId,
2523}
2524
2525#[serde_as]
2526#[derive(Serialize, Deserialize, JsonSchema)]
2527#[serde(rename_all = "camelCase")]
2528pub struct MoveCallParams {
2529 #[serde_as(as = "ObjectIdSchema")]
2530 #[schemars(with = "ObjectIdSchema")]
2531 pub package_object_id: ObjectId,
2532 pub module: String,
2533 pub function: String,
2534 #[serde(default)]
2535 #[schemars(with = "Vec<TypeTagSchema>")]
2536 pub type_arguments: Vec<IotaTypeTag>,
2537 pub arguments: Vec<PtbInput>,
2538}
2539
2540#[serde_as]
2541#[derive(Serialize, Deserialize, Clone, JsonSchema)]
2542#[serde(rename_all = "camelCase")]
2543pub struct TransactionBlockBytes {
2544 #[schemars(with = "Base64Schema")]
2547 pub tx_bytes: Base64,
2548 #[schemars(with = "Vec<ObjectRefSchema>")]
2550 #[serde_as(as = "Vec<ObjectRefSchema>")]
2551 pub gas: Vec<ObjectReference>,
2552 pub input_objects: Vec<IotaInputObjectKind>,
2554}
2555
2556impl TransactionBlockBytes {
2557 pub fn from_data(tx: Transaction) -> Result<Self, anyhow::Error> {
2558 Ok(Self {
2559 tx_bytes: Base64::from_bytes(&tx.to_bcs()),
2560 gas: tx.gas().to_vec(),
2561 input_objects: tx
2562 .input_objects()?
2563 .into_iter()
2564 .map(IotaInputObjectKind::from)
2565 .collect(),
2566 })
2567 }
2568
2569 pub fn to_data(self) -> Result<Transaction, anyhow::Error> {
2570 bcs::from_bytes::<Transaction>(&self.tx_bytes.to_vec().map_err(|e| anyhow::anyhow!(e))?)
2571 .map_err(|e| anyhow::anyhow!(e))
2572 }
2573}
2574
2575#[serde_as]
2576#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
2577#[serde(rename = "OwnedObjectRef")]
2578pub struct OwnedObjectRef {
2579 #[schemars(with = "OwnerSchema")]
2580 #[serde_as(as = "OwnerSchema")]
2581 pub owner: Owner,
2582 #[schemars(with = "ObjectRefSchema")]
2583 #[serde_as(as = "ObjectRefSchema")]
2584 pub reference: ObjectReference,
2585}
2586
2587impl OwnedObjectRef {
2588 pub fn object_id(&self) -> ObjectId {
2589 self.reference.object_id
2590 }
2591 pub fn version(&self) -> Version {
2592 self.reference.version
2593 }
2594}
2595
2596#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2597#[serde(tag = "type", rename_all = "camelCase")]
2598pub enum IotaCallArg {
2599 Object(IotaObjectArg),
2601 Pure(IotaPureValue),
2603}
2604
2605impl IotaCallArg {
2606 pub fn try_from(
2607 value: CallArg,
2608 layout: Option<&MoveTypeLayout>,
2609 ) -> Result<Self, anyhow::Error> {
2610 Ok(match value {
2611 CallArg::Pure(p) => IotaCallArg::Pure(IotaPureValue {
2612 value_type: layout.map(|l| type_tag_core_to_sdk(&l.into())),
2613 value: IotaJsonValue::from_bcs_bytes(layout, &p)?,
2614 }),
2615 CallArg::ImmutableOrOwned(object_ref) => {
2616 IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject {
2617 object_id: object_ref.object_id,
2618 version: object_ref.version,
2619 digest: object_ref.digest,
2620 })
2621 }
2622 CallArg::Shared(SharedObjectReference {
2623 object_id: id,
2624 initial_shared_version,
2625 mutable,
2626 }) => IotaCallArg::Object(IotaObjectArg::SharedObject {
2627 object_id: id,
2628 initial_shared_version,
2629 mutable,
2630 }),
2631 CallArg::Receiving(object_ref) => IotaCallArg::Object(IotaObjectArg::Receiving {
2632 object_id: object_ref.object_id,
2633 version: object_ref.version,
2634 digest: object_ref.digest,
2635 }),
2636 _ => unimplemented!("a new CallArg enum variant was added and needs to be handled"),
2637 })
2638 }
2639
2640 pub fn pure(&self) -> Option<&IotaJsonValue> {
2641 match self {
2642 IotaCallArg::Pure(v) => Some(&v.value),
2643 _ => None,
2644 }
2645 }
2646
2647 pub fn object(&self) -> Option<&ObjectId> {
2648 match self {
2649 IotaCallArg::Object(IotaObjectArg::SharedObject { object_id, .. })
2650 | IotaCallArg::Object(IotaObjectArg::ImmOrOwnedObject { object_id, .. })
2651 | IotaCallArg::Object(IotaObjectArg::Receiving { object_id, .. }) => Some(object_id),
2652 _ => None,
2653 }
2654 }
2655}
2656
2657#[serde_as]
2658#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2659#[serde(rename_all = "camelCase")]
2660pub struct IotaPureValue {
2661 #[schemars(with = "Option<TypeTagSchema>")]
2662 #[serde_as(as = "Option<TypeTagSchema>")]
2663 value_type: Option<TypeTag>,
2664 value: IotaJsonValue,
2665}
2666
2667impl IotaPureValue {
2668 pub fn value(&self) -> IotaJsonValue {
2669 self.value.clone()
2670 }
2671
2672 pub fn value_type(&self) -> Option<TypeTag> {
2673 self.value_type.clone()
2674 }
2675}
2676
2677#[serde_as]
2678#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
2679#[serde(tag = "objectType", rename_all = "camelCase")]
2680pub enum IotaObjectArg {
2681 #[serde(rename_all = "camelCase")]
2683 ImmOrOwnedObject {
2684 #[serde_as(as = "ObjectIdSchema")]
2685 #[schemars(with = "ObjectIdSchema")]
2686 object_id: ObjectId,
2687 #[schemars(with = "SequenceNumberStringSchema")]
2688 #[serde_as(as = "SequenceNumberStringSchema")]
2689 version: Version,
2690 #[serde_as(as = "Base58Schema")]
2691 #[schemars(with = "Base58Schema")]
2692 digest: ObjectDigest,
2693 },
2694 #[serde(rename_all = "camelCase")]
2698 SharedObject {
2699 #[serde_as(as = "ObjectIdSchema")]
2700 #[schemars(with = "ObjectIdSchema")]
2701 object_id: ObjectId,
2702 #[schemars(with = "SequenceNumberStringSchema")]
2703 #[serde_as(as = "SequenceNumberStringSchema")]
2704 initial_shared_version: Version,
2705 mutable: bool,
2706 },
2707 #[serde(rename_all = "camelCase")]
2709 Receiving {
2710 #[serde_as(as = "ObjectIdSchema")]
2711 #[schemars(with = "ObjectIdSchema")]
2712 object_id: ObjectId,
2713 #[schemars(with = "SequenceNumberStringSchema")]
2714 #[serde_as(as = "SequenceNumberStringSchema")]
2715 version: Version,
2716 #[serde_as(as = "Base58Schema")]
2717 #[schemars(with = "Base58Schema")]
2718 digest: ObjectDigest,
2719 },
2720}
2721
2722#[derive(Clone)]
2723pub struct EffectsWithInput {
2724 pub effects: IotaTransactionBlockEffects,
2725 pub input: Transaction,
2726}
2727
2728impl From<EffectsWithInput> for IotaTransactionBlockEffects {
2729 fn from(e: EffectsWithInput) -> Self {
2730 e.effects
2731 }
2732}
2733
2734#[serde_as]
2735#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2736pub enum TransactionFilter {
2737 Checkpoint(
2739 #[schemars(with = "String")]
2740 #[serde_as(as = "DisplayFromStr")]
2741 CheckpointSequenceNumber,
2742 ),
2743 MoveFunction {
2745 #[serde_as(as = "ObjectIdSchema")]
2746 #[schemars(with = "ObjectIdSchema")]
2747 package: ObjectId,
2748 module: Option<String>,
2749 function: Option<String>,
2750 },
2751 InputObject(
2753 #[serde_as(as = "ObjectIdSchema")]
2754 #[schemars(with = "ObjectIdSchema")]
2755 ObjectId,
2756 ),
2757 ChangedObject(
2760 #[serde_as(as = "ObjectIdSchema")]
2761 #[schemars(with = "ObjectIdSchema")]
2762 ObjectId,
2763 ),
2764 FromAddress(
2766 #[serde_as(as = "AddressSchema")]
2767 #[schemars(with = "AddressSchema")]
2768 Address,
2769 ),
2770 ToAddress(
2772 #[serde_as(as = "AddressSchema")]
2773 #[schemars(with = "AddressSchema")]
2774 Address,
2775 ),
2776 FromAndToAddress {
2778 #[serde_as(as = "AddressSchema")]
2779 #[schemars(with = "AddressSchema")]
2780 from: Address,
2781 #[serde_as(as = "AddressSchema")]
2782 #[schemars(with = "AddressSchema")]
2783 to: Address,
2784 },
2785 FromOrToAddress {
2787 #[serde_as(as = "AddressSchema")]
2788 #[schemars(with = "AddressSchema")]
2789 addr: Address,
2790 },
2791 TransactionKind(IotaTransactionKind),
2793 TransactionKindIn(Vec<IotaTransactionKind>),
2795}
2796
2797impl TransactionFilter {
2798 pub fn as_v2(&self) -> TransactionFilterV2 {
2799 match self {
2800 TransactionFilter::InputObject(o) => TransactionFilterV2::InputObject(*o),
2801 TransactionFilter::ChangedObject(o) => TransactionFilterV2::ChangedObject(*o),
2802 TransactionFilter::FromAddress(a) => TransactionFilterV2::FromAddress(*a),
2803 TransactionFilter::ToAddress(a) => TransactionFilterV2::ToAddress(*a),
2804 TransactionFilter::FromAndToAddress { from, to } => {
2805 TransactionFilterV2::FromAndToAddress {
2806 from: *from,
2807 to: *to,
2808 }
2809 }
2810 TransactionFilter::FromOrToAddress { addr } => {
2811 TransactionFilterV2::FromOrToAddress { addr: *addr }
2812 }
2813 TransactionFilter::MoveFunction {
2814 package,
2815 module,
2816 function,
2817 } => TransactionFilterV2::MoveFunction {
2818 package: *package,
2819 module: module.clone(),
2820 function: function.clone(),
2821 },
2822 TransactionFilter::TransactionKind(kind) => TransactionFilterV2::TransactionKind(*kind),
2823 TransactionFilter::TransactionKindIn(kinds) => {
2824 TransactionFilterV2::TransactionKindIn(kinds.clone())
2825 }
2826 TransactionFilter::Checkpoint(checkpoint) => {
2827 TransactionFilterV2::Checkpoint(*checkpoint)
2828 }
2829 }
2830 }
2831}
2832
2833impl Filter<EffectsWithInput> for TransactionFilter {
2834 fn matches(&self, item: &EffectsWithInput) -> bool {
2835 match self {
2836 TransactionFilter::InputObject(o) => {
2837 let Ok(input_objects) = item.input.input_objects() else {
2838 return false;
2839 };
2840 input_objects.iter().any(|object| object.object_id() == *o)
2841 }
2842 TransactionFilter::ChangedObject(o) => item
2843 .effects
2844 .mutated()
2845 .iter()
2846 .any(|oref: &OwnedObjectRef| &oref.reference.object_id == o),
2847 TransactionFilter::FromAddress(a) => &item.input.sender() == a,
2848 TransactionFilter::ToAddress(a) => {
2849 let mutated: &[OwnedObjectRef] = item.effects.mutated();
2850 mutated.iter().chain(item.effects.unwrapped().iter()).any(|oref: &OwnedObjectRef| {
2851 matches!(oref.owner, Owner::Address(owner) if owner == *a)
2852 })
2853 }
2854 TransactionFilter::FromAndToAddress { from, to } => {
2855 Self::FromAddress(*from).matches(item) && Self::ToAddress(*to).matches(item)
2856 }
2857 TransactionFilter::FromOrToAddress { addr } => {
2858 Self::FromAddress(*addr).matches(item) || Self::ToAddress(*addr).matches(item)
2859 }
2860 TransactionFilter::MoveFunction {
2861 package,
2862 module,
2863 function,
2864 } => item.input.move_calls().into_iter().any(|(p, m, f)| {
2865 p == package
2866 && (module.is_none() || matches!(module, Some(m2) if m2 == &m.to_string()))
2867 && (function.is_none() || matches!(function, Some(f2) if f2 == &f.to_string()))
2868 }),
2869 TransactionFilter::TransactionKind(kind) => {
2870 kind == &IotaTransactionKind::from(item.input.kind())
2871 }
2872 TransactionFilter::TransactionKindIn(kinds) => kinds
2873 .iter()
2874 .any(|kind| kind == &IotaTransactionKind::from(item.input.kind())),
2875 TransactionFilter::Checkpoint(_) => false,
2877 }
2878 }
2879}
2880
2881#[serde_as]
2882#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)]
2883#[non_exhaustive]
2884pub enum TransactionFilterV2 {
2885 Checkpoint(
2887 #[schemars(with = "String")]
2888 #[serde_as(as = "DisplayFromStr")]
2889 CheckpointSequenceNumber,
2890 ),
2891 MoveFunction {
2893 #[serde_as(as = "ObjectIdSchema")]
2894 #[schemars(with = "ObjectIdSchema")]
2895 package: ObjectId,
2896 module: Option<String>,
2897 function: Option<String>,
2898 },
2899 InputObject(
2901 #[serde_as(as = "ObjectIdSchema")]
2902 #[schemars(with = "ObjectIdSchema")]
2903 ObjectId,
2904 ),
2905 ChangedObject(
2908 #[serde_as(as = "ObjectIdSchema")]
2909 #[schemars(with = "ObjectIdSchema")]
2910 ObjectId,
2911 ),
2912 WrappedOrDeletedObject(
2916 #[serde_as(as = "ObjectIdSchema")]
2917 #[schemars(with = "ObjectIdSchema")]
2918 ObjectId,
2919 ),
2920 FromAddress(
2922 #[serde_as(as = "AddressSchema")]
2923 #[schemars(with = "AddressSchema")]
2924 Address,
2925 ),
2926 ToAddress(
2928 #[serde_as(as = "AddressSchema")]
2929 #[schemars(with = "AddressSchema")]
2930 Address,
2931 ),
2932 FromAndToAddress {
2934 #[serde_as(as = "AddressSchema")]
2935 #[schemars(with = "AddressSchema")]
2936 from: Address,
2937 #[serde_as(as = "AddressSchema")]
2938 #[schemars(with = "AddressSchema")]
2939 to: Address,
2940 },
2941 FromOrToAddress {
2943 #[serde_as(as = "AddressSchema")]
2944 #[schemars(with = "AddressSchema")]
2945 addr: Address,
2946 },
2947 TransactionKind(IotaTransactionKind),
2949 TransactionKindIn(Vec<IotaTransactionKind>),
2951}
2952
2953impl TransactionFilterV2 {
2954 pub fn as_v1(&self) -> Option<TransactionFilter> {
2955 match self {
2956 TransactionFilterV2::InputObject(o) => Some(TransactionFilter::InputObject(*o)),
2957 TransactionFilterV2::ChangedObject(o) => Some(TransactionFilter::ChangedObject(*o)),
2958 TransactionFilterV2::FromAddress(a) => Some(TransactionFilter::FromAddress(*a)),
2959 TransactionFilterV2::ToAddress(a) => Some(TransactionFilter::ToAddress(*a)),
2960 TransactionFilterV2::FromAndToAddress { from, to } => {
2961 Some(TransactionFilter::FromAndToAddress {
2962 from: *from,
2963 to: *to,
2964 })
2965 }
2966 TransactionFilterV2::FromOrToAddress { addr } => {
2967 Some(TransactionFilter::FromOrToAddress { addr: *addr })
2968 }
2969 TransactionFilterV2::MoveFunction {
2970 package,
2971 module,
2972 function,
2973 } => Some(TransactionFilter::MoveFunction {
2974 package: *package,
2975 module: module.clone(),
2976 function: function.clone(),
2977 }),
2978 TransactionFilterV2::TransactionKind(kind) => {
2979 Some(TransactionFilter::TransactionKind(*kind))
2980 }
2981 TransactionFilterV2::TransactionKindIn(kinds) => {
2982 Some(TransactionFilter::TransactionKindIn(kinds.clone()))
2983 }
2984 TransactionFilterV2::Checkpoint(checkpoint) => {
2985 Some(TransactionFilter::Checkpoint(*checkpoint))
2986 }
2987 TransactionFilterV2::WrappedOrDeletedObject(_) => None,
2989 }
2990 }
2991}
2992
2993impl Filter<EffectsWithInput> for TransactionFilterV2 {
2994 fn matches(&self, item: &EffectsWithInput) -> bool {
2995 if let Some(v1) = self.as_v1() {
2996 return v1.matches(item);
2997 }
2998 match self {
3000 TransactionFilterV2::WrappedOrDeletedObject(o) => item
3001 .effects
3002 .wrapped()
3003 .iter()
3004 .chain(item.effects.deleted())
3005 .chain(item.effects.unwrapped_then_deleted())
3006 .any(|oref| &oref.object_id == o),
3007
3008 _ => false,
3009 }
3010 }
3011}
3012
3013#[derive(
3016 Debug, Clone, Copy, PartialEq, Eq, EnumString, Display, Serialize, Deserialize, JsonSchema,
3017)]
3018#[non_exhaustive]
3019pub enum IotaTransactionKind {
3020 SystemTransaction = 0,
3023 ProgrammableTransaction = 1,
3024 Genesis = 2,
3025 ConsensusCommitPrologueV1 = 3,
3026 RandomnessStateUpdate = 5,
3027 EndOfEpochTransaction = 6,
3028 TransactionDenyRulesUpdate = 7,
3029}
3030
3031impl IotaTransactionKind {
3032 pub fn is_system_transaction(&self) -> bool {
3034 !matches!(self, Self::ProgrammableTransaction)
3035 }
3036}
3037
3038impl From<&TransactionKind> for IotaTransactionKind {
3039 fn from(kind: &TransactionKind) -> Self {
3040 match kind {
3041 TransactionKind::Genesis(_) => Self::Genesis,
3042 TransactionKind::ConsensusCommitPrologueV1(_) => Self::ConsensusCommitPrologueV1,
3043 #[allow(deprecated)]
3044 TransactionKind::AuthenticatorStateUpdateV1Deprecated => Self::SystemTransaction,
3045 TransactionKind::RandomnessStateUpdate(_) => Self::RandomnessStateUpdate,
3046 TransactionKind::TransactionDenyRulesUpdate(_) => Self::TransactionDenyRulesUpdate,
3047 TransactionKind::EndOfEpoch(_) => Self::EndOfEpochTransaction,
3048 TransactionKind::Programmable(_) => Self::ProgrammableTransaction,
3049 _ => unimplemented!(
3050 "a new TransactionKind enum variant was added and needs to be handled"
3051 ),
3052 }
3053 }
3054}