1use iota_sdk_types::{ObjectId, TypeTag};
6use move_core_types::{
7 account_address::AccountAddress,
8 annotated_value as A,
9 annotated_visitor::{self, StructDriver, ValueDriver, VariantDriver, VecDriver, Visitor},
10 u256::U256,
11};
12
13use super::{DynamicFieldInfo, DynamicFieldType};
14use crate::{
15 id::UID,
16 iota_sdk_types_conversions::{struct_tag_core_to_sdk, type_tag_core_to_sdk},
17};
18
19pub struct FieldVisitor;
22
23#[derive(Debug, Clone)]
24pub struct Field<'b, 'l> {
25 pub id: ObjectId,
26 pub kind: DynamicFieldType,
27 pub name_layout: &'l A::MoveTypeLayout,
28 pub name_bytes: &'b [u8],
29 pub value_layout: &'l A::MoveTypeLayout,
30 pub value_bytes: &'b [u8],
31}
32
33pub enum ValueMetadata {
34 DynamicField(TypeTag),
35 DynamicObjectField(ObjectId),
36}
37
38#[derive(thiserror::Error, Debug)]
39pub enum Error {
40 #[error("Not a dynamic field")]
41 NotADynamicField,
42
43 #[error("Not a dynamic object field")]
44 NotADynamicObjectField,
45
46 #[error("{0}")]
47 Visitor(#[from] annotated_visitor::Error),
48}
49
50impl FieldVisitor {
51 pub fn deserialize<'b, 'l>(
55 bytes: &'b [u8],
56 layout: &'l A::MoveTypeLayout,
57 ) -> anyhow::Result<Field<'b, 'l>> {
58 A::MoveValue::visit_deserialize(bytes, layout, &mut FieldVisitor)
59 }
60}
61
62impl Field<'_, '_> {
63 pub fn value_metadata(&self) -> Result<ValueMetadata, Error> {
67 match self.kind {
68 DynamicFieldType::DynamicField => {
69 Ok(ValueMetadata::DynamicField(type_tag_core_to_sdk(
70 &move_core_types::language_storage::TypeTag::from(self.value_layout),
71 )))
72 }
73
74 DynamicFieldType::DynamicObject => {
75 let id: ObjectId =
76 bcs::from_bytes(self.value_bytes).map_err(|_| Error::NotADynamicObjectField)?;
77 Ok(ValueMetadata::DynamicObjectField(id))
78 }
79 }
80 }
81}
82
83impl<'b, 'l> Visitor<'b, 'l> for FieldVisitor {
84 type Value = Field<'b, 'l>;
85 type Error = Error;
86
87 fn visit_struct(
88 &mut self,
89 driver: &mut StructDriver<'_, 'b, 'l>,
90 ) -> Result<Self::Value, Error> {
91 if !DynamicFieldInfo::is_dynamic_field(&struct_tag_core_to_sdk(
92 &driver.struct_layout().type_,
93 )) {
94 return Err(Error::NotADynamicField);
95 }
96
97 let mut id = None;
100 let mut name_parts = None;
101 let mut value_parts = None;
102
103 while let Some(A::MoveFieldLayout { name, layout }) = driver.peek_field() {
104 match name.as_str() {
105 "id" => {
106 let lo = driver.position();
107 driver.skip_field()?;
108 let hi = driver.position();
109
110 if !matches!(layout, A::MoveTypeLayout::Struct(s) if s.as_ref() == &UID::layout())
111 {
112 return Err(Error::NotADynamicField);
113 }
114
115 let bytes = &driver.bytes()[lo..hi];
117 id = Some(ObjectId::from_bytes(bytes).map_err(|_| Error::NotADynamicField)?);
118 }
119
120 "name" => {
121 let lo = driver.position();
122 driver.skip_field()?;
123 let hi = driver.position();
124
125 let (kind, layout) = extract_name_layout(layout)?;
126 name_parts = Some((&driver.bytes()[lo..hi], layout, kind));
127 }
128
129 "value" => {
130 let lo = driver.position();
131 driver.skip_field()?;
132 let hi = driver.position();
133 value_parts = Some((&driver.bytes()[lo..hi], layout));
134 }
135
136 _ => {
137 return Err(Error::NotADynamicField);
138 }
139 }
140 }
141
142 let (Some(id), Some((name_bytes, name_layout, kind)), Some((value_bytes, value_layout))) =
143 (id, name_parts, value_parts)
144 else {
145 return Err(Error::NotADynamicField);
146 };
147
148 Ok(Field {
149 id,
150 kind,
151 name_layout,
152 name_bytes,
153 value_layout,
154 value_bytes,
155 })
156 }
157
158 fn visit_u8(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u8) -> Result<Self::Value, Error> {
164 Err(Error::NotADynamicField)
165 }
166
167 fn visit_u16(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u16) -> Result<Self::Value, Error> {
168 Err(Error::NotADynamicField)
169 }
170
171 fn visit_u32(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u32) -> Result<Self::Value, Error> {
172 Err(Error::NotADynamicField)
173 }
174
175 fn visit_u64(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u64) -> Result<Self::Value, Error> {
176 Err(Error::NotADynamicField)
177 }
178
179 fn visit_u128(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u128) -> Result<Self::Value, Error> {
180 Err(Error::NotADynamicField)
181 }
182
183 fn visit_u256(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: U256) -> Result<Self::Value, Error> {
184 Err(Error::NotADynamicField)
185 }
186
187 fn visit_bool(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: bool) -> Result<Self::Value, Error> {
188 Err(Error::NotADynamicField)
189 }
190
191 fn visit_address(
192 &mut self,
193 _: &ValueDriver<'_, 'b, 'l>,
194 _: AccountAddress,
195 ) -> Result<Self::Value, Error> {
196 Err(Error::NotADynamicField)
197 }
198
199 fn visit_signer(
200 &mut self,
201 _: &ValueDriver<'_, 'b, 'l>,
202 _: AccountAddress,
203 ) -> Result<Self::Value, Error> {
204 Err(Error::NotADynamicField)
205 }
206
207 fn visit_vector(&mut self, _: &mut VecDriver<'_, 'b, 'l>) -> Result<Self::Value, Error> {
208 Err(Error::NotADynamicField)
209 }
210
211 fn visit_variant(&mut self, _: &mut VariantDriver<'_, 'b, 'l>) -> Result<Self::Value, Error> {
212 Err(Error::NotADynamicField)
213 }
214}
215
216fn extract_name_layout(
219 layout: &A::MoveTypeLayout,
220) -> Result<(DynamicFieldType, &A::MoveTypeLayout), Error> {
221 let A::MoveTypeLayout::Struct(struct_) = layout else {
222 return Ok((DynamicFieldType::DynamicField, layout));
223 };
224
225 if !DynamicFieldInfo::is_dynamic_object_field_wrapper(&struct_tag_core_to_sdk(&struct_.type_)) {
226 return Ok((DynamicFieldType::DynamicField, layout));
227 }
228
229 let [A::MoveFieldLayout { name, layout }] = &struct_.fields[..] else {
231 return Err(Error::NotADynamicField);
232 };
233
234 if name.as_str() != "name" {
236 return Err(Error::NotADynamicField);
237 }
238
239 Ok((DynamicFieldType::DynamicObject, layout))
240}
241
242#[cfg(test)]
243mod tests {
244 use std::str::FromStr;
245
246 use move_core_types::{
247 account_address::AccountAddress, annotated_value as A, language_storage::TypeTag,
248 };
249
250 use super::*;
251 use crate::{
252 dynamic_field,
253 id::UID,
254 object::bounded_visitor::tests::{enum_, layout_, value_, variant_},
255 };
256
257 #[test]
258 fn test_dynamic_field_name() {
259 for (name, name_layout, name_bcs) in fixtures() {
260 for (value, value_layout, value_bcs) in fixtures() {
261 let df = serialized_df("0x264", name.clone(), value.clone());
262 let df_layout = df_layout(name_layout.clone(), value_layout.clone());
263 let field = FieldVisitor::deserialize(&df, &df_layout)
264 .unwrap_or_else(|e| panic!("Failed to deserialize {name} => {value}: {e}"));
265
266 assert_eq!(field.id, oid_("0x264"), "{name} => {value}");
267 assert_eq!(field.name_bytes, &name_bcs, "{name} => {value}");
268 assert_eq!(field.value_bytes, &value_bcs, "{name} => {value}");
269
270 assert_eq!(
271 field.kind,
272 DynamicFieldType::DynamicField,
273 "{name} => {value}",
274 );
275
276 assert_eq!(
277 TypeTag::from(field.name_layout),
278 TypeTag::from(&name_layout),
279 "{name} => {value}",
280 );
281
282 assert_eq!(
283 TypeTag::from(field.value_layout),
284 TypeTag::from(&value_layout),
285 "{name} => {value}",
286 );
287 }
288 }
289 }
290
291 #[test]
292 fn test_dynamic_object_field_name() {
293 let addr = A::MoveValue::Address(AccountAddress::ONE);
294 let id = value_("0x2::object::ID", vec![("bytes", addr)]);
295 let id_bcs = id.clone().undecorate().simple_serialize().unwrap();
296
297 for (name, name_layout, name_bcs) in fixtures() {
298 let df = serialized_df("0x264", name.clone(), id.clone());
299 let df_layout = dof_layout(name_layout.clone());
300 let field = FieldVisitor::deserialize(&df, &df_layout)
301 .unwrap_or_else(|e| panic!("Failed to deserialize {name}: {e}"));
302
303 assert_eq!(field.id, oid_("0x264"), "{name}");
304 assert_eq!(field.name_bytes, &name_bcs, "{name}");
305 assert_eq!(field.value_bytes, &id_bcs, "{name}");
306
307 assert_eq!(field.kind, DynamicFieldType::DynamicObject, "{name}",);
308
309 assert_eq!(
310 TypeTag::from(field.name_layout),
311 TypeTag::from(&name_layout),
312 "{name}",
313 );
314
315 assert_eq!(
316 TypeTag::from(field.value_layout),
317 TypeTag::from(&id_layout()),
318 "{name}",
319 );
320 }
321 }
322
323 #[test]
324 fn test_name_from_not_dynamic_field() {
325 for (value, layout, bytes) in fixtures() {
326 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
327 panic!("Expected NotADynamicField error for {value}");
328 };
329
330 assert_eq!(
331 e.to_string(),
332 "Not a dynamic field",
333 "Unexpected error for {value}"
334 );
335 }
336 }
337
338 #[test]
341 fn test_from_bad_type() {
342 for (value, layout, bytes) in fixtures() {
343 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
344 panic!("Expected NotADynamicField error for {value}");
345 };
346
347 assert_eq!(
348 e.to_string(),
349 "Not a dynamic field",
350 "Unexpected error for {value}"
351 );
352 }
353 }
354
355 #[test]
356 fn test_from_dynamic_field_missing_id() {
357 let bytes = bcs::to_bytes(&(42u8, 43u8)).unwrap();
358 let layout = layout_(
359 "0x2::dynamic_field::Field<u8, u8>",
360 vec![
361 ("name", A::MoveTypeLayout::U8),
362 ("value", A::MoveTypeLayout::U8),
363 ],
364 );
365
366 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
367 panic!("Expected NotADynamicField error");
368 };
369
370 assert_eq!(e.to_string(), "Not a dynamic field");
371 }
372
373 #[test]
374 fn test_from_dynamic_field_missing_name() {
375 let bytes = bcs::to_bytes(&(oid_("0x264"), 43u8)).unwrap();
376 let layout = layout_(
377 "0x2::dynamic_field::Field<u8, u8>",
378 vec![("id", id_layout()), ("value", A::MoveTypeLayout::U8)],
379 );
380
381 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
382 panic!("Expected NotADynamicField error");
383 };
384
385 assert_eq!(e.to_string(), "Not a dynamic field");
386 }
387
388 #[test]
389 fn test_from_dynamic_field_missing_value() {
390 let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
391 let layout = layout_(
392 "0x2::dynamic_field::Field<u8, u8>",
393 vec![("id", id_layout()), ("name", A::MoveTypeLayout::U8)],
394 );
395
396 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
397 panic!("Expected NotADynamicField error");
398 };
399
400 assert_eq!(e.to_string(), "Not a dynamic field");
401 }
402
403 #[test]
404 fn test_from_dynamic_field_weird_id() {
405 let bytes = bcs::to_bytes(&(42u8, 43u8, 44u8)).unwrap();
406 let layout = layout_(
407 "0x2::dynamic_field::Field<u8, u8>",
408 vec![
409 ("id", A::MoveTypeLayout::U8),
410 ("name", A::MoveTypeLayout::U8),
411 ("value", A::MoveTypeLayout::U8),
412 ],
413 );
414
415 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
416 panic!("Expected NotADynamicField error");
417 };
418
419 assert_eq!(e.to_string(), "Not a dynamic field");
420 }
421
422 #[test]
426 fn test_from_dynamic_object_field_bad_wrapper() {
427 let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
428 let layout = layout_(
429 "0x2::dynamic_field::Field<0x2::dynamic_object_field::Wrapper<u8>, u8>",
430 vec![
431 ("id", id_layout()),
432 (
433 "name",
434 layout_(
435 "0x2::dynamic_object_field::Wrapper<u8>",
436 vec![("wrapped", A::MoveTypeLayout::U8)],
438 ),
439 ),
440 ("value", A::MoveTypeLayout::U8),
441 ],
442 );
443
444 let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
445 panic!("Expected NotADynamicField error");
446 };
447
448 assert_eq!(e.to_string(), "Not a dynamic field");
449 }
450
451 fn fixtures() -> Vec<(A::MoveValue, A::MoveTypeLayout, Vec<u8>)> {
453 use A::{MoveTypeLayout as T, MoveValue as V};
454
455 vec![
456 fixture(V::U8(42), T::U8),
457 fixture(V::Address(AccountAddress::ONE), T::Address),
458 fixture(
459 V::Vector(vec![V::U32(43), V::U32(44), V::U32(45)]),
460 T::Vector(Box::new(T::U32)),
461 ),
462 fixture(
463 value_(
464 "0x2::object::ID",
465 vec![("bytes", V::Address(AccountAddress::TWO))],
466 ),
467 layout_("0x2::object::ID", vec![("bytes", T::Address)]),
468 ),
469 fixture(
470 variant_(
471 "0x1::option::Option<u64>",
472 "Some",
473 1,
474 vec![("value", V::U64(46))],
475 ),
476 enum_(
477 "0x1::option::Option<u64>",
478 vec![
479 (("None", 0), vec![]),
480 (("Some", 1), vec![("value", T::U64)]),
481 ],
482 ),
483 ),
484 ]
485 }
486
487 fn fixture(
488 value: A::MoveValue,
489 layout: A::MoveTypeLayout,
490 ) -> (A::MoveValue, A::MoveTypeLayout, Vec<u8>) {
491 let bytes = value
492 .clone()
493 .undecorate()
494 .simple_serialize()
495 .unwrap_or_else(|| panic!("Failed to serialize {}", value.clone()));
496
497 (value, layout, bytes)
498 }
499
500 fn oid_(rep: &str) -> ObjectId {
501 ObjectId::from_str(rep).unwrap()
502 }
503
504 fn serialized_df(id: &str, name: A::MoveValue, value: A::MoveValue) -> Vec<u8> {
505 bcs::to_bytes(&dynamic_field::Field {
506 id: UID::new(oid_(id)),
507 name: name.undecorate(),
508 value: value.undecorate(),
509 })
510 .unwrap()
511 }
512
513 fn id_layout() -> A::MoveTypeLayout {
514 let addr = A::MoveTypeLayout::Address;
515 layout_("0x2::object::ID", vec![("bytes", addr)])
516 }
517
518 fn df_layout(name: A::MoveTypeLayout, value: A::MoveTypeLayout) -> A::MoveTypeLayout {
519 let uid = layout_("0x2::object::UID", vec![("id", id_layout())]);
520 let field = format!(
521 "0x2::dynamic_field::Field<{}, {}>",
522 TypeTag::from(&name).to_canonical_display(true),
523 TypeTag::from(&value).to_canonical_display(true)
524 );
525
526 layout_(&field, vec![("id", uid), ("name", name), ("value", value)])
527 }
528
529 fn dof_layout(name: A::MoveTypeLayout) -> A::MoveTypeLayout {
530 let tag = TypeTag::from(&name);
531 let wrapper = format!(
532 "0x2::dynamic_object_field::Wrapper<{}>",
533 tag.to_canonical_display(true)
534 );
535
536 let name = layout_(&wrapper, vec![("name", name)]);
537 df_layout(name, id_layout())
538 }
539}