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