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