1use std::{
6 collections::BTreeMap,
7 fmt::{self, Display, Formatter, Write},
8 str::FromStr,
9};
10
11use colored::Colorize;
12use iota_macros::EnumVariantOrder;
13use iota_sdk_types::{Address, Identifier, ObjectId, StructTag};
14use iota_types::{
15 error::{IotaError, UserInputError},
16 iota_sdk_types_conversions::{identifier_core_to_sdk, struct_tag_core_to_sdk},
17};
18use itertools::Itertools;
19use move_binary_format::{
20 file_format::{Ability, AbilitySet, DatatypeTyParameter, Visibility},
21 normalized::{
22 self, Enum as NormalizedEnum, Field as NormalizedField, Function as NormalizedFunction,
23 Module as NormalizedModule, Struct as NormalizedStruct, Type as NormalizedType,
24 },
25};
26use move_core_types::annotated_value::{MoveStruct, MoveValue, MoveVariant};
27use schemars::JsonSchema;
28use serde::{Deserialize, Serialize};
29use serde_json::{Value, json};
30use serde_with::serde_as;
31use tracing::warn;
32
33use crate::iota_primitives::{
34 Address as AddressSchema, ObjectId as ObjectIdSchema, StructTag as StructTagSchema,
35};
36
37pub type IotaMoveTypeParameterIndex = u16;
38
39#[cfg(test)]
40#[path = "unit_tests/iota_move_tests.rs"]
41mod iota_move_tests;
42
43#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
44pub enum IotaMoveAbility {
45 Copy,
46 Drop,
47 Store,
48 Key,
49}
50
51#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
52pub struct IotaMoveAbilitySet {
53 pub abilities: Vec<IotaMoveAbility>,
54}
55
56#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
57pub enum IotaMoveVisibility {
58 Private,
59 Public,
60 Friend,
61}
62
63#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
64#[serde(rename_all = "camelCase")]
65pub struct IotaMoveStructTypeParameter {
66 pub constraints: IotaMoveAbilitySet,
67 pub is_phantom: bool,
68}
69
70#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
71pub struct IotaMoveNormalizedField {
72 pub name: String,
73 #[serde(rename = "type")]
74 pub type_: IotaMoveNormalizedType,
75}
76
77#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
78#[serde(rename_all = "camelCase")]
79pub struct IotaMoveNormalizedStruct {
80 pub abilities: IotaMoveAbilitySet,
81 pub type_parameters: Vec<IotaMoveStructTypeParameter>,
82 pub fields: Vec<IotaMoveNormalizedField>,
83}
84
85#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
86#[serde(rename_all = "camelCase")]
87pub struct IotaMoveNormalizedEnum {
88 pub abilities: IotaMoveAbilitySet,
89 pub type_parameters: Vec<IotaMoveStructTypeParameter>,
90 pub variants: BTreeMap<String, Vec<IotaMoveNormalizedField>>,
91}
92
93#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
94pub enum IotaMoveNormalizedType {
95 Bool,
96 U8,
97 U16,
98 U32,
99 U64,
100 U128,
101 U256,
102 Address,
103 Signer,
104 Struct {
105 #[serde(flatten)]
106 inner: Box<IotaMoveNormalizedStructType>,
107 },
108 Vector(Box<IotaMoveNormalizedType>),
109 TypeParameter(IotaMoveTypeParameterIndex),
110 Reference(Box<IotaMoveNormalizedType>),
111 MutableReference(Box<IotaMoveNormalizedType>),
112}
113
114#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, PartialEq)]
115#[serde(rename_all = "camelCase")]
116pub struct IotaMoveNormalizedStructType {
117 pub address: String,
118 pub module: String,
119 pub name: String,
120 pub type_arguments: Vec<IotaMoveNormalizedType>,
121}
122#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)]
123#[serde(rename_all = "camelCase")]
124pub struct IotaMoveNormalizedFunction {
125 pub visibility: IotaMoveVisibility,
126 pub is_entry: bool,
127 pub type_parameters: Vec<IotaMoveAbilitySet>,
128 pub parameters: Vec<IotaMoveNormalizedType>,
129 pub return_: Vec<IotaMoveNormalizedType>,
130}
131
132#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
133pub struct IotaMoveModuleId {
134 address: String,
135 name: String,
136}
137
138#[serde_as]
140#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
141#[serde(rename_all = "camelCase")]
142pub struct MoveFunctionName {
143 #[serde_as(as = "ObjectIdSchema")]
145 #[schemars(with = "ObjectIdSchema")]
146 pub package: ObjectId,
147 pub module: String,
149 pub function: String,
151}
152
153impl FromStr for MoveFunctionName {
154 type Err = IotaError;
155
156 fn from_str(s: &str) -> Result<Self, Self::Err> {
157 let (module, name) =
158 iota_types::parse_iota_fq_name(s).map_err(|e| UserInputError::InvalidIdentifier {
159 error: e.to_string(),
160 })?;
161 let package = ObjectId::new(module.address().into_bytes());
162 Ok(Self {
163 package,
164 module: module.name().to_string(),
165 function: name,
166 })
167 }
168}
169
170#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
171#[serde(rename_all = "camelCase")]
172pub struct IotaMoveNormalizedModule {
173 pub file_format_version: u32,
174 pub address: String,
175 pub name: String,
176 pub friends: Vec<IotaMoveModuleId>,
177 pub structs: BTreeMap<String, IotaMoveNormalizedStruct>,
178 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
179 pub enums: BTreeMap<String, IotaMoveNormalizedEnum>,
180 pub exposed_functions: BTreeMap<String, IotaMoveNormalizedFunction>,
181}
182
183impl PartialEq for IotaMoveNormalizedModule {
184 fn eq(&self, other: &Self) -> bool {
185 self.file_format_version == other.file_format_version
186 && self.address == other.address
187 && self.name == other.name
188 }
189}
190
191impl<S: std::hash::Hash + Eq + ToString> From<&NormalizedModule<S>> for IotaMoveNormalizedModule {
192 fn from(module: &NormalizedModule<S>) -> Self {
193 Self {
194 file_format_version: module.file_format_version,
195 address: module.address().to_hex_literal(),
196 name: module.name().to_string(),
197 friends: module
198 .friends
199 .iter()
200 .map(|module_id| IotaMoveModuleId {
201 address: module_id.address.to_hex_literal(),
202 name: module_id.name.to_string(),
203 })
204 .collect::<Vec<IotaMoveModuleId>>(),
205 structs: module
206 .structs
207 .iter()
208 .map(|(name, struct_)| {
209 (name.to_string(), IotaMoveNormalizedStruct::from(&**struct_))
210 })
211 .collect::<BTreeMap<String, IotaMoveNormalizedStruct>>(),
212 enums: module
213 .enums
214 .iter()
215 .map(|(name, enum_)| (name.to_string(), IotaMoveNormalizedEnum::from(&**enum_)))
216 .collect(),
217 exposed_functions: module
218 .functions
219 .iter()
220 .filter(|(_name, function)| {
221 function.is_entry || function.visibility != Visibility::Private
222 })
223 .map(|(name, function)| {
224 (
227 name.to_string(),
228 IotaMoveNormalizedFunction::from(&**function),
229 )
230 })
231 .collect::<BTreeMap<String, IotaMoveNormalizedFunction>>(),
232 }
233 }
234}
235
236impl<S: ToString> From<&NormalizedFunction<S>> for IotaMoveNormalizedFunction {
237 fn from(function: &NormalizedFunction<S>) -> Self {
238 Self {
239 visibility: match function.visibility {
240 Visibility::Private => IotaMoveVisibility::Private,
241 Visibility::Public => IotaMoveVisibility::Public,
242 Visibility::Friend => IotaMoveVisibility::Friend,
243 },
244 is_entry: function.is_entry,
245 type_parameters: function
246 .type_parameters
247 .iter()
248 .copied()
249 .map(|a| a.into())
250 .collect::<Vec<IotaMoveAbilitySet>>(),
251 parameters: function
252 .parameters
253 .iter()
254 .map(|t| IotaMoveNormalizedType::from(&**t))
255 .collect::<Vec<IotaMoveNormalizedType>>(),
256 return_: function
257 .return_
258 .iter()
259 .map(|t| IotaMoveNormalizedType::from(&**t))
260 .collect::<Vec<IotaMoveNormalizedType>>(),
261 }
262 }
263}
264
265impl<S: ToString> From<&NormalizedStruct<S>> for IotaMoveNormalizedStruct {
266 fn from(struct_: &NormalizedStruct<S>) -> Self {
267 Self {
268 abilities: struct_.abilities.into(),
269 type_parameters: struct_
270 .type_parameters
271 .iter()
272 .copied()
273 .map(IotaMoveStructTypeParameter::from)
274 .collect::<Vec<IotaMoveStructTypeParameter>>(),
275 fields: struct_
276 .fields
277 .iter()
278 .map(|f| IotaMoveNormalizedField::from(&**f))
279 .collect::<Vec<IotaMoveNormalizedField>>(),
280 }
281 }
282}
283
284impl<S: ToString> From<&NormalizedEnum<S>> for IotaMoveNormalizedEnum {
285 fn from(value: &NormalizedEnum<S>) -> Self {
286 Self {
287 abilities: value.abilities.into(),
288 type_parameters: value
289 .type_parameters
290 .iter()
291 .copied()
292 .map(Into::into)
293 .collect(),
294 variants: value
295 .variants
296 .iter()
297 .map(|variant| {
298 (
299 variant.name.to_string(),
300 variant.fields.iter().map(Into::into).collect(),
301 )
302 })
303 .collect(),
304 }
305 }
306}
307
308impl From<DatatypeTyParameter> for IotaMoveStructTypeParameter {
309 fn from(type_parameter: DatatypeTyParameter) -> Self {
310 Self {
311 constraints: type_parameter.constraints.into(),
312 is_phantom: type_parameter.is_phantom,
313 }
314 }
315}
316
317impl<S: ToString> From<&NormalizedField<S>> for IotaMoveNormalizedField {
318 fn from(normalized_field: &NormalizedField<S>) -> Self {
319 Self {
320 name: normalized_field.name.to_string(),
321 type_: IotaMoveNormalizedType::from(&normalized_field.type_),
322 }
323 }
324}
325
326impl<S: ToString> From<&NormalizedType<S>> for IotaMoveNormalizedType {
327 fn from(type_: &NormalizedType<S>) -> Self {
328 match type_ {
329 NormalizedType::Bool => IotaMoveNormalizedType::Bool,
330 NormalizedType::U8 => IotaMoveNormalizedType::U8,
331 NormalizedType::U16 => IotaMoveNormalizedType::U16,
332 NormalizedType::U32 => IotaMoveNormalizedType::U32,
333 NormalizedType::U64 => IotaMoveNormalizedType::U64,
334 NormalizedType::U128 => IotaMoveNormalizedType::U128,
335 NormalizedType::U256 => IotaMoveNormalizedType::U256,
336 NormalizedType::Address => IotaMoveNormalizedType::Address,
337 NormalizedType::Signer => IotaMoveNormalizedType::Signer,
338 NormalizedType::Datatype(dt) => {
339 let normalized::Datatype {
340 module,
341 name,
342 type_arguments,
343 } = &**dt;
344 IotaMoveNormalizedType::new_struct(
345 module.address.to_hex_literal(),
346 module.name.to_string(),
347 name.to_string(),
348 type_arguments
349 .iter()
350 .map(IotaMoveNormalizedType::from)
351 .collect::<Vec<IotaMoveNormalizedType>>(),
352 )
353 }
354 NormalizedType::Vector(v) => {
355 IotaMoveNormalizedType::Vector(Box::new(IotaMoveNormalizedType::from(&**v)))
356 }
357 NormalizedType::TypeParameter(t) => IotaMoveNormalizedType::TypeParameter(*t),
358 NormalizedType::Reference(false, r) => {
359 IotaMoveNormalizedType::Reference(Box::new(IotaMoveNormalizedType::from(&**r)))
360 }
361 NormalizedType::Reference(true, mr) => IotaMoveNormalizedType::MutableReference(
362 Box::new(IotaMoveNormalizedType::from(&**mr)),
363 ),
364 }
365 }
366}
367
368impl From<AbilitySet> for IotaMoveAbilitySet {
369 fn from(set: AbilitySet) -> IotaMoveAbilitySet {
370 Self {
371 abilities: set
372 .into_iter()
373 .map(|a| match a {
374 Ability::Copy => IotaMoveAbility::Copy,
375 Ability::Drop => IotaMoveAbility::Drop,
376 Ability::Key => IotaMoveAbility::Key,
377 Ability::Store => IotaMoveAbility::Store,
378 })
379 .collect::<Vec<IotaMoveAbility>>(),
380 }
381 }
382}
383
384impl IotaMoveNormalizedType {
385 pub fn new_struct(
386 address: String,
387 module: String,
388 name: String,
389 type_arguments: Vec<IotaMoveNormalizedType>,
390 ) -> Self {
391 IotaMoveNormalizedType::Struct {
392 inner: Box::new(IotaMoveNormalizedStructType {
393 address,
394 module,
395 name,
396 type_arguments,
397 }),
398 }
399 }
400}
401
402#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
403pub enum ObjectValueKind {
404 ByImmutableReference,
405 ByMutableReference,
406 ByValue,
407}
408
409#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
410pub enum MoveFunctionArgType {
411 Pure,
412 Object(ObjectValueKind),
413}
414
415#[serde_as]
416#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, EnumVariantOrder)]
417#[serde(untagged, rename = "MoveValue")]
418pub enum IotaMoveValue {
419 Number(u32),
421 Bool(bool),
422 Address(
423 #[serde_as(as = "AddressSchema")]
424 #[schemars(with = "AddressSchema")]
425 Address,
426 ),
427 Vector(Vec<IotaMoveValue>),
428 String(String),
429 UID {
430 #[serde_as(as = "ObjectIdSchema")]
431 #[schemars(with = "ObjectIdSchema")]
432 id: ObjectId,
433 },
434 Struct(IotaMoveStruct),
435 Option(Box<Option<IotaMoveValue>>),
436 Variant(IotaMoveVariant),
437}
438
439impl IotaMoveValue {
440 pub fn to_json_value(self) -> Value {
442 match self {
443 IotaMoveValue::Struct(move_struct) => move_struct.to_json_value(),
444 IotaMoveValue::Vector(values) => IotaMoveStruct::Runtime(values).to_json_value(),
445 IotaMoveValue::Number(v) => json!(v),
446 IotaMoveValue::Bool(v) => json!(v),
447 IotaMoveValue::Address(v) => json!(v),
448 IotaMoveValue::String(v) => json!(v),
449 IotaMoveValue::UID { id } => json!({ "id": id }),
450 IotaMoveValue::Option(v) => json!(v),
451 IotaMoveValue::Variant(v) => v.to_json_value(),
452 }
453 }
454}
455
456impl Display for IotaMoveValue {
457 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
458 let mut writer = String::new();
459 match self {
460 IotaMoveValue::Number(value) => write!(writer, "{value}")?,
461 IotaMoveValue::Bool(value) => write!(writer, "{value}")?,
462 IotaMoveValue::Address(value) => write!(writer, "{value}")?,
463 IotaMoveValue::String(value) => write!(writer, "{value}")?,
464 IotaMoveValue::UID { id } => write!(writer, "{id}")?,
465 IotaMoveValue::Struct(value) => write!(writer, "{value}")?,
466 IotaMoveValue::Option(value) => write!(writer, "{value:?}")?,
467 IotaMoveValue::Vector(vec) => {
468 write!(
469 writer,
470 "{}",
471 vec.iter().map(|value| format!("{value}")).join(",\n")
472 )?;
473 }
474 IotaMoveValue::Variant(value) => write!(writer, "{value}")?,
475 }
476 write!(f, "{}", writer.trim_end_matches('\n'))
477 }
478}
479
480impl From<MoveValue> for IotaMoveValue {
481 fn from(value: MoveValue) -> Self {
482 match value {
483 MoveValue::U8(value) => IotaMoveValue::Number(value.into()),
484 MoveValue::U16(value) => IotaMoveValue::Number(value.into()),
485 MoveValue::U32(value) => IotaMoveValue::Number(value),
486 MoveValue::U64(value) => IotaMoveValue::String(format!("{value}")),
487 MoveValue::U128(value) => IotaMoveValue::String(format!("{value}")),
488 MoveValue::U256(value) => IotaMoveValue::String(format!("{value}")),
489 MoveValue::Bool(value) => IotaMoveValue::Bool(value),
490 MoveValue::Vector(values) => {
491 IotaMoveValue::Vector(values.into_iter().map(|value| value.into()).collect())
492 }
493 MoveValue::Struct(value) => {
494 let MoveStruct { type_, fields } = &value;
496 let type_ = struct_tag_core_to_sdk(type_);
497 let fields = fields
498 .iter()
499 .map(|(id, value)| (identifier_core_to_sdk(id), value.clone()))
500 .collect::<Vec<_>>();
501 if let Some(value) = try_convert_type(&type_, &fields) {
502 return value;
503 }
504 IotaMoveValue::Struct(value.into())
505 }
506 MoveValue::Signer(value) | MoveValue::Address(value) => {
507 IotaMoveValue::Address(Address::new(value.into_bytes()))
508 }
509 MoveValue::Variant(MoveVariant {
510 type_,
511 variant_name,
512 tag: _,
513 fields,
514 }) => IotaMoveValue::Variant(IotaMoveVariant {
515 type_: struct_tag_core_to_sdk(&type_),
516 variant: variant_name.to_string(),
517 fields: fields
518 .into_iter()
519 .map(|(id, value)| (id.into_string(), value.into()))
520 .collect::<BTreeMap<_, _>>(),
521 }),
522 }
523 }
524}
525
526fn to_bytearray(value: &[MoveValue]) -> Option<Vec<u8>> {
527 if value.iter().all(|value| matches!(value, MoveValue::U8(_))) {
528 let bytearray = value
529 .iter()
530 .flat_map(|value| {
531 if let MoveValue::U8(u8) = value {
532 Some(*u8)
533 } else {
534 None
535 }
536 })
537 .collect::<Vec<_>>();
538 Some(bytearray)
539 } else {
540 None
541 }
542}
543
544#[serde_as]
545#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq)]
546#[serde(rename = "MoveVariant")]
547pub struct IotaMoveVariant {
548 #[serde(rename = "type")]
549 #[schemars(with = "StructTagSchema")]
550 #[serde_as(as = "StructTagSchema")]
551 pub type_: StructTag,
552 pub variant: String,
553 pub fields: BTreeMap<String, IotaMoveValue>,
554}
555
556impl IotaMoveVariant {
557 pub fn to_json_value(self) -> Value {
558 let fields = self
561 .fields
562 .into_iter()
563 .map(|(key, value)| (key, value.to_json_value()))
564 .collect::<BTreeMap<_, _>>();
565 json!({
566 "variant": self.variant,
567 "fields": fields,
568 })
569 }
570}
571
572impl Display for IotaMoveVariant {
573 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
574 let mut writer = String::new();
575 let IotaMoveVariant {
576 type_,
577 variant,
578 fields,
579 } = self;
580 writeln!(writer)?;
581 writeln!(writer, " {}: {type_}", "type".bold().bright_black())?;
582 writeln!(writer, " {}: {variant}", "variant".bold().bright_black())?;
583 for (name, value) in fields {
584 let value = format!("{value}");
585 let value = if value.starts_with('\n') {
586 indent(&value, 2)
587 } else {
588 value
589 };
590 writeln!(writer, " {}: {value}", name.bold().bright_black())?;
591 }
592
593 write!(f, "{}", writer.trim_end_matches('\n'))
594 }
595}
596
597#[serde_as]
598#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, EnumVariantOrder)]
599#[serde(untagged, rename = "MoveStruct")]
600pub enum IotaMoveStruct {
601 Runtime(Vec<IotaMoveValue>),
602 WithTypes {
603 #[serde(rename = "type")]
604 #[schemars(with = "StructTagSchema")]
605 #[serde_as(as = "StructTagSchema")]
606 type_: StructTag,
607 fields: BTreeMap<String, IotaMoveValue>,
608 },
609 WithFields(BTreeMap<String, IotaMoveValue>),
610}
611
612impl IotaMoveStruct {
613 pub fn to_json_value(self) -> Value {
615 match self {
617 IotaMoveStruct::Runtime(values) => {
618 let values = values
619 .into_iter()
620 .map(|value| value.to_json_value())
621 .collect::<Vec<_>>();
622 json!(values)
623 }
624 IotaMoveStruct::WithTypes { type_: _, fields } | IotaMoveStruct::WithFields(fields) => {
627 let fields = fields
628 .into_iter()
629 .map(|(key, value)| (key, value.to_json_value()))
630 .collect::<BTreeMap<_, _>>();
631 json!(fields)
632 }
633 }
634 }
635
636 pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<IotaMoveValue> {
637 match self {
638 IotaMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
639 IotaMoveStruct::WithTypes { type_: _, fields } => fields.get(field_name).cloned(),
640 _ => None,
641 }
642 }
643}
644
645impl Display for IotaMoveStruct {
646 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
647 let mut writer = String::new();
648 match self {
649 IotaMoveStruct::Runtime(_) => {}
650 IotaMoveStruct::WithFields(fields) => {
651 for (name, value) in fields {
652 writeln!(writer, "{}: {value}", name.bold().bright_black())?;
653 }
654 }
655 IotaMoveStruct::WithTypes { type_, fields } => {
656 writeln!(writer)?;
657 writeln!(writer, " {}: {type_}", "type".bold().bright_black())?;
658 for (name, value) in fields {
659 let value = format!("{value}");
660 let value = if value.starts_with('\n') {
661 indent(&value, 2)
662 } else {
663 value
664 };
665 writeln!(writer, " {}: {value}", name.bold().bright_black())?;
666 }
667 }
668 }
669 write!(f, "{}", writer.trim_end_matches('\n'))
670 }
671}
672
673fn indent<T: Display>(d: &T, indent: usize) -> String {
674 d.to_string()
675 .lines()
676 .map(|line| format!("{:indent$}{line}", ""))
677 .join("\n")
678}
679
680fn try_convert_type(
681 type_: &StructTag,
682 fields: &[(Identifier, MoveValue)],
683) -> Option<IotaMoveValue> {
684 let struct_name = format!(
685 "{}::{}::{}",
686 type_.address().to_short_hex(),
687 type_.module(),
688 type_.name()
689 );
690 let mut values = fields
691 .iter()
692 .map(|(id, value)| (id.to_string(), value))
693 .collect::<BTreeMap<_, _>>();
694 match struct_name.as_str() {
695 "0x1::string::String" | "0x1::ascii::String" => {
696 if let Some(MoveValue::Vector(bytes)) = values.remove("bytes") {
697 return to_bytearray(bytes)
698 .and_then(|bytes| String::from_utf8(bytes).ok())
699 .map(IotaMoveValue::String);
700 }
701 }
702 "0x2::url::Url" => {
703 return values.remove("url").cloned().map(IotaMoveValue::from);
704 }
705 "0x2::object::ID" => {
706 return values.remove("bytes").cloned().map(IotaMoveValue::from);
707 }
708 "0x2::object::UID" => {
709 let id = values.remove("id").cloned().map(IotaMoveValue::from);
710 if let Some(IotaMoveValue::Address(address)) = id {
711 return Some(IotaMoveValue::UID {
712 id: ObjectId::from(address),
713 });
714 }
715 }
716 "0x2::balance::Balance" => {
717 return values.remove("value").cloned().map(IotaMoveValue::from);
718 }
719 "0x1::option::Option" => {
720 if let Some(MoveValue::Vector(values)) = values.remove("vec") {
721 return Some(IotaMoveValue::Option(Box::new(
722 values.first().cloned().map(IotaMoveValue::from),
724 )));
725 }
726 }
727 _ => return None,
728 }
729 warn!(
730 fields =? fields,
731 "failed to convert {struct_name} to IotaMoveValue"
732 );
733 None
734}
735
736impl From<MoveStruct> for IotaMoveStruct {
737 fn from(move_struct: MoveStruct) -> Self {
738 IotaMoveStruct::WithTypes {
739 type_: struct_tag_core_to_sdk(&move_struct.type_),
740 fields: move_struct
741 .fields
742 .into_iter()
743 .map(|(id, value)| (id.into_string(), value.into()))
744 .collect(),
745 }
746 }
747}
748
749#[test]
750fn enum_size() {
751 assert_eq!(std::mem::size_of::<IotaMoveNormalizedType>(), 16);
752}