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