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