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