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::{Identifier, ObjectId, StructTag};
14use iota_types::{
15 base_types::IotaAddress,
16 error::{IotaError, UserInputError},
17 iota_sdk_types_conversions::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 IotaAddress as IotaAddressSchema, 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: 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: 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 .iter()
279 .map(|f| IotaMoveNormalizedField::from(&**f))
280 .collect::<Vec<IotaMoveNormalizedField>>(),
281 }
282 }
283}
284
285impl<S: ToString> From<&NormalizedEnum<S>> for IotaMoveNormalizedEnum {
286 fn from(value: &NormalizedEnum<S>) -> Self {
287 Self {
288 abilities: value.abilities.into(),
289 type_parameters: value
290 .type_parameters
291 .iter()
292 .copied()
293 .map(Into::into)
294 .collect(),
295 variants: value
296 .variants
297 .iter()
298 .map(|variant| {
299 (
300 variant.name.to_string(),
301 variant.fields.iter().map(Into::into).collect(),
302 )
303 })
304 .collect(),
305 }
306 }
307}
308
309impl From<DatatypeTyParameter> for IotaMoveStructTypeParameter {
310 fn from(type_parameter: DatatypeTyParameter) -> Self {
311 Self {
312 constraints: type_parameter.constraints.into(),
313 is_phantom: type_parameter.is_phantom,
314 }
315 }
316}
317
318impl<S: ToString> From<&NormalizedField<S>> for IotaMoveNormalizedField {
319 fn from(normalized_field: &NormalizedField<S>) -> Self {
320 Self {
321 name: normalized_field.name.to_string(),
322 type_: IotaMoveNormalizedType::from(&normalized_field.type_),
323 }
324 }
325}
326
327impl<S: ToString> From<&NormalizedType<S>> for IotaMoveNormalizedType {
328 fn from(type_: &NormalizedType<S>) -> Self {
329 match type_ {
330 NormalizedType::Bool => IotaMoveNormalizedType::Bool,
331 NormalizedType::U8 => IotaMoveNormalizedType::U8,
332 NormalizedType::U16 => IotaMoveNormalizedType::U16,
333 NormalizedType::U32 => IotaMoveNormalizedType::U32,
334 NormalizedType::U64 => IotaMoveNormalizedType::U64,
335 NormalizedType::U128 => IotaMoveNormalizedType::U128,
336 NormalizedType::U256 => IotaMoveNormalizedType::U256,
337 NormalizedType::Address => IotaMoveNormalizedType::Address,
338 NormalizedType::Signer => IotaMoveNormalizedType::Signer,
339 NormalizedType::Datatype(dt) => {
340 let normalized::Datatype {
341 module,
342 name,
343 type_arguments,
344 } = &**dt;
345 IotaMoveNormalizedType::new_struct(
346 module.address.to_hex_literal(),
347 module.name.to_string(),
348 name.to_string(),
349 type_arguments
350 .iter()
351 .map(IotaMoveNormalizedType::from)
352 .collect::<Vec<IotaMoveNormalizedType>>(),
353 )
354 }
355 NormalizedType::Vector(v) => {
356 IotaMoveNormalizedType::Vector(Box::new(IotaMoveNormalizedType::from(&**v)))
357 }
358 NormalizedType::TypeParameter(t) => IotaMoveNormalizedType::TypeParameter(*t),
359 NormalizedType::Reference(false, r) => {
360 IotaMoveNormalizedType::Reference(Box::new(IotaMoveNormalizedType::from(&**r)))
361 }
362 NormalizedType::Reference(true, mr) => IotaMoveNormalizedType::MutableReference(
363 Box::new(IotaMoveNormalizedType::from(&**mr)),
364 ),
365 }
366 }
367}
368
369impl From<AbilitySet> for IotaMoveAbilitySet {
370 fn from(set: AbilitySet) -> IotaMoveAbilitySet {
371 Self {
372 abilities: set
373 .into_iter()
374 .map(|a| match a {
375 Ability::Copy => IotaMoveAbility::Copy,
376 Ability::Drop => IotaMoveAbility::Drop,
377 Ability::Key => IotaMoveAbility::Key,
378 Ability::Store => IotaMoveAbility::Store,
379 })
380 .collect::<Vec<IotaMoveAbility>>(),
381 }
382 }
383}
384
385impl IotaMoveNormalizedType {
386 pub fn new_struct(
387 address: String,
388 module: String,
389 name: String,
390 type_arguments: Vec<IotaMoveNormalizedType>,
391 ) -> Self {
392 IotaMoveNormalizedType::Struct {
393 inner: Box::new(IotaMoveNormalizedStructType {
394 address,
395 module,
396 name,
397 type_arguments,
398 }),
399 }
400 }
401}
402
403#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
404pub enum ObjectValueKind {
405 ByImmutableReference,
406 ByMutableReference,
407 ByValue,
408}
409
410#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema, PartialEq)]
411pub enum MoveFunctionArgType {
412 Pure,
413 Object(ObjectValueKind),
414}
415
416#[serde_as]
417#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, EnumVariantOrder)]
418#[serde(untagged, rename = "MoveValue")]
419pub enum IotaMoveValue {
420 Number(u32),
422 Bool(bool),
423 Address(
424 #[serde_as(as = "IotaAddressSchema")]
425 #[schemars(with = "IotaAddressSchema")]
426 IotaAddress,
427 ),
428 Vector(Vec<IotaMoveValue>),
429 String(String),
430 UID {
431 #[serde_as(as = "ObjectIdSchema")]
432 #[schemars(with = "ObjectIdSchema")]
433 id: ObjectId,
434 },
435 Struct(IotaMoveStruct),
436 Option(Box<Option<IotaMoveValue>>),
437 Variant(IotaMoveVariant),
438}
439
440impl IotaMoveValue {
441 pub fn to_json_value(self) -> Value {
443 match self {
444 IotaMoveValue::Struct(move_struct) => move_struct.to_json_value(),
445 IotaMoveValue::Vector(values) => IotaMoveStruct::Runtime(values).to_json_value(),
446 IotaMoveValue::Number(v) => json!(v),
447 IotaMoveValue::Bool(v) => json!(v),
448 IotaMoveValue::Address(v) => json!(v),
449 IotaMoveValue::String(v) => json!(v),
450 IotaMoveValue::UID { id } => json!({ "id": id }),
451 IotaMoveValue::Option(v) => json!(v),
452 IotaMoveValue::Variant(v) => v.to_json_value(),
453 }
454 }
455}
456
457impl Display for IotaMoveValue {
458 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
459 let mut writer = String::new();
460 match self {
461 IotaMoveValue::Number(value) => write!(writer, "{value}")?,
462 IotaMoveValue::Bool(value) => write!(writer, "{value}")?,
463 IotaMoveValue::Address(value) => write!(writer, "{value}")?,
464 IotaMoveValue::String(value) => write!(writer, "{value}")?,
465 IotaMoveValue::UID { id } => write!(writer, "{id}")?,
466 IotaMoveValue::Struct(value) => write!(writer, "{value}")?,
467 IotaMoveValue::Option(value) => write!(writer, "{value:?}")?,
468 IotaMoveValue::Vector(vec) => {
469 write!(
470 writer,
471 "{}",
472 vec.iter().map(|value| format!("{value}")).join(",\n")
473 )?;
474 }
475 IotaMoveValue::Variant(value) => write!(writer, "{value}")?,
476 }
477 write!(f, "{}", writer.trim_end_matches('\n'))
478 }
479}
480
481impl From<MoveValue> for IotaMoveValue {
482 fn from(value: MoveValue) -> Self {
483 match value {
484 MoveValue::U8(value) => IotaMoveValue::Number(value.into()),
485 MoveValue::U16(value) => IotaMoveValue::Number(value.into()),
486 MoveValue::U32(value) => IotaMoveValue::Number(value),
487 MoveValue::U64(value) => IotaMoveValue::String(format!("{value}")),
488 MoveValue::U128(value) => IotaMoveValue::String(format!("{value}")),
489 MoveValue::U256(value) => IotaMoveValue::String(format!("{value}")),
490 MoveValue::Bool(value) => IotaMoveValue::Bool(value),
491 MoveValue::Vector(values) => {
492 IotaMoveValue::Vector(values.into_iter().map(|value| value.into()).collect())
493 }
494 MoveValue::Struct(value) => {
495 let MoveStruct { type_, fields } = &value;
497 let type_ = struct_tag_core_to_sdk(type_);
498 let fields = fields
499 .iter()
500 .map(|(id, value)| (Identifier::new_unchecked(id.as_str()), value.clone()))
501 .collect::<Vec<_>>();
502 if let Some(value) = try_convert_type(&type_, &fields) {
503 return value;
504 }
505 IotaMoveValue::Struct(value.into())
506 }
507 MoveValue::Signer(value) | MoveValue::Address(value) => {
508 IotaMoveValue::Address(IotaAddress::new(value.into_bytes()))
509 }
510 MoveValue::Variant(MoveVariant {
511 type_,
512 variant_name,
513 tag: _,
514 fields,
515 }) => IotaMoveValue::Variant(IotaMoveVariant {
516 type_: struct_tag_core_to_sdk(&type_),
517 variant: variant_name.to_string(),
518 fields: fields
519 .into_iter()
520 .map(|(id, value)| (id.into_string(), value.into()))
521 .collect::<BTreeMap<_, _>>(),
522 }),
523 }
524 }
525}
526
527fn to_bytearray(value: &[MoveValue]) -> Option<Vec<u8>> {
528 if value.iter().all(|value| matches!(value, MoveValue::U8(_))) {
529 let bytearray = value
530 .iter()
531 .flat_map(|value| {
532 if let MoveValue::U8(u8) = value {
533 Some(*u8)
534 } else {
535 None
536 }
537 })
538 .collect::<Vec<_>>();
539 Some(bytearray)
540 } else {
541 None
542 }
543}
544
545#[serde_as]
546#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq)]
547#[serde(rename = "MoveVariant")]
548pub struct IotaMoveVariant {
549 #[serde(rename = "type")]
550 #[schemars(with = "StructTagSchema")]
551 #[serde_as(as = "StructTagSchema")]
552 pub type_: StructTag,
553 pub variant: String,
554 pub fields: BTreeMap<String, IotaMoveValue>,
555}
556
557impl IotaMoveVariant {
558 pub fn to_json_value(self) -> Value {
559 let fields = self
562 .fields
563 .into_iter()
564 .map(|(key, value)| (key, value.to_json_value()))
565 .collect::<BTreeMap<_, _>>();
566 json!({
567 "variant": self.variant,
568 "fields": fields,
569 })
570 }
571}
572
573impl Display for IotaMoveVariant {
574 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
575 let mut writer = String::new();
576 let IotaMoveVariant {
577 type_,
578 variant,
579 fields,
580 } = self;
581 writeln!(writer)?;
582 writeln!(writer, " {}: {type_}", "type".bold().bright_black())?;
583 writeln!(writer, " {}: {variant}", "variant".bold().bright_black())?;
584 for (name, value) in fields {
585 let value = format!("{value}");
586 let value = if value.starts_with('\n') {
587 indent(&value, 2)
588 } else {
589 value
590 };
591 writeln!(writer, " {}: {value}", name.bold().bright_black())?;
592 }
593
594 write!(f, "{}", writer.trim_end_matches('\n'))
595 }
596}
597
598#[serde_as]
599#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Eq, PartialEq, EnumVariantOrder)]
600#[serde(untagged, rename = "MoveStruct")]
601pub enum IotaMoveStruct {
602 Runtime(Vec<IotaMoveValue>),
603 WithTypes {
604 #[serde(rename = "type")]
605 #[schemars(with = "StructTagSchema")]
606 #[serde_as(as = "StructTagSchema")]
607 type_: StructTag,
608 fields: BTreeMap<String, IotaMoveValue>,
609 },
610 WithFields(BTreeMap<String, IotaMoveValue>),
611}
612
613impl IotaMoveStruct {
614 pub fn to_json_value(self) -> Value {
616 match self {
618 IotaMoveStruct::Runtime(values) => {
619 let values = values
620 .into_iter()
621 .map(|value| value.to_json_value())
622 .collect::<Vec<_>>();
623 json!(values)
624 }
625 IotaMoveStruct::WithTypes { type_: _, fields } | IotaMoveStruct::WithFields(fields) => {
628 let fields = fields
629 .into_iter()
630 .map(|(key, value)| (key, value.to_json_value()))
631 .collect::<BTreeMap<_, _>>();
632 json!(fields)
633 }
634 }
635 }
636
637 pub fn read_dynamic_field_value(&self, field_name: &str) -> Option<IotaMoveValue> {
638 match self {
639 IotaMoveStruct::WithFields(fields) => fields.get(field_name).cloned(),
640 IotaMoveStruct::WithTypes { type_: _, fields } => fields.get(field_name).cloned(),
641 _ => None,
642 }
643 }
644}
645
646impl Display for IotaMoveStruct {
647 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
648 let mut writer = String::new();
649 match self {
650 IotaMoveStruct::Runtime(_) => {}
651 IotaMoveStruct::WithFields(fields) => {
652 for (name, value) in fields {
653 writeln!(writer, "{}: {value}", name.bold().bright_black())?;
654 }
655 }
656 IotaMoveStruct::WithTypes { type_, fields } => {
657 writeln!(writer)?;
658 writeln!(writer, " {}: {type_}", "type".bold().bright_black())?;
659 for (name, value) in fields {
660 let value = format!("{value}");
661 let value = if value.starts_with('\n') {
662 indent(&value, 2)
663 } else {
664 value
665 };
666 writeln!(writer, " {}: {value}", name.bold().bright_black())?;
667 }
668 }
669 }
670 write!(f, "{}", writer.trim_end_matches('\n'))
671 }
672}
673
674fn indent<T: Display>(d: &T, indent: usize) -> String {
675 d.to_string()
676 .lines()
677 .map(|line| format!("{:indent$}{line}", ""))
678 .join("\n")
679}
680
681fn try_convert_type(
682 type_: &StructTag,
683 fields: &[(Identifier, MoveValue)],
684) -> Option<IotaMoveValue> {
685 let struct_name = format!(
686 "{}::{}::{}",
687 type_.address().to_short_hex(),
688 type_.module(),
689 type_.name()
690 );
691 let mut values = fields
692 .iter()
693 .map(|(id, value)| (id.to_string(), value))
694 .collect::<BTreeMap<_, _>>();
695 match struct_name.as_str() {
696 "0x1::string::String" | "0x1::ascii::String" => {
697 if let Some(MoveValue::Vector(bytes)) = values.remove("bytes") {
698 return to_bytearray(bytes)
699 .and_then(|bytes| String::from_utf8(bytes).ok())
700 .map(IotaMoveValue::String);
701 }
702 }
703 "0x2::url::Url" => {
704 return values.remove("url").cloned().map(IotaMoveValue::from);
705 }
706 "0x2::object::ID" => {
707 return values.remove("bytes").cloned().map(IotaMoveValue::from);
708 }
709 "0x2::object::UID" => {
710 let id = values.remove("id").cloned().map(IotaMoveValue::from);
711 if let Some(IotaMoveValue::Address(address)) = id {
712 return Some(IotaMoveValue::UID {
713 id: ObjectId::from(address),
714 });
715 }
716 }
717 "0x2::balance::Balance" => {
718 return values.remove("value").cloned().map(IotaMoveValue::from);
719 }
720 "0x1::option::Option" => {
721 if let Some(MoveValue::Vector(values)) = values.remove("vec") {
722 return Some(IotaMoveValue::Option(Box::new(
723 values.first().cloned().map(IotaMoveValue::from),
725 )));
726 }
727 }
728 _ => return None,
729 }
730 warn!(
731 fields =? fields,
732 "failed to convert {struct_name} to IotaMoveValue"
733 );
734 None
735}
736
737impl From<MoveStruct> for IotaMoveStruct {
738 fn from(move_struct: MoveStruct) -> Self {
739 IotaMoveStruct::WithTypes {
740 type_: struct_tag_core_to_sdk(&move_struct.type_),
741 fields: move_struct
742 .fields
743 .into_iter()
744 .map(|(id, value)| (id.into_string(), value.into()))
745 .collect(),
746 }
747 }
748}
749
750#[test]
751fn enum_size() {
752 assert_eq!(std::mem::size_of::<IotaMoveNormalizedType>(), 16);
753}