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