1use crate::base_types::{MoveObjectType, ObjectDigest, SuiAddress};
5use crate::crypto::DefaultHash;
6use crate::error::{SuiError, SuiErrorKind, SuiResult};
7use crate::id::UID;
8use crate::object::{MoveObject, Object};
9use crate::storage::{ChildObjectResolver, ObjectStore};
10use crate::sui_serde::Readable;
11use crate::sui_serde::SuiTypeTag;
12use crate::{MoveTypeTagTrait, ObjectID, SUI_FRAMEWORK_ADDRESS, SequenceNumber};
13use fastcrypto::encoding::Base64;
14use fastcrypto::hash::HashFunction;
15use move_core_types::annotated_value::{MoveStruct, MoveValue};
16use move_core_types::ident_str;
17use move_core_types::identifier::IdentStr;
18use move_core_types::language_storage::{StructTag, TypeTag};
19use schemars::JsonSchema;
20use serde::Deserialize;
21use serde::Serialize;
22use serde::de::DeserializeOwned;
23use serde_json::Value;
24use serde_with::DisplayFromStr;
25use serde_with::serde_as;
26use shared_crypto::intent::HashingIntentScope;
27use std::fmt;
28use std::fmt::{Display, Formatter};
29
30pub mod visitor;
31
32pub const DYNAMIC_FIELD_MODULE_NAME: &IdentStr = ident_str!("dynamic_field");
33pub const DYNAMIC_FIELD_FIELD_STRUCT_NAME: &IdentStr = ident_str!("Field");
34
35const DYNAMIC_OBJECT_FIELD_MODULE_NAME: &IdentStr = ident_str!("dynamic_object_field");
36const DYNAMIC_OBJECT_FIELD_WRAPPER_STRUCT_NAME: &IdentStr = ident_str!("Wrapper");
37
38#[derive(Clone, Serialize, Deserialize, Debug)]
40pub struct Field<N, V> {
41    pub id: UID,
42    pub name: N,
43    pub value: V,
44}
45
46#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
48pub struct DOFWrapper<N> {
49    pub name: N,
50}
51
52impl<N> MoveTypeTagTrait for DOFWrapper<N>
53where
54    N: MoveTypeTagTrait,
55{
56    fn get_type_tag() -> TypeTag {
57        TypeTag::Struct(Box::new(DynamicFieldInfo::dynamic_object_field_wrapper(
58            N::get_type_tag(),
59        )))
60    }
61}
62
63#[serde_as]
64#[derive(Clone, Serialize, Deserialize, Debug)]
65#[serde(rename_all = "camelCase")]
66pub struct DynamicFieldInfo {
67    pub name: DynamicFieldName,
68    #[serde_as(as = "Readable<Base64, _>")]
69    pub bcs_name: Vec<u8>,
70    pub type_: DynamicFieldType,
71    pub object_type: String,
72    pub object_id: ObjectID,
73    pub version: SequenceNumber,
74    pub digest: ObjectDigest,
75}
76
77#[serde_as]
78#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
79#[serde(rename_all = "camelCase")]
80pub struct DynamicFieldName {
81    #[schemars(with = "String")]
82    #[serde_as(as = "Readable<SuiTypeTag, _>")]
83    pub type_: TypeTag,
84    #[schemars(with = "Value")]
87    #[serde_as(as = "Readable<_, DisplayFromStr>")]
88    pub value: Value,
89}
90
91impl Display for DynamicFieldName {
92    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{}: {}", self.type_, self.value)
94    }
95}
96
97#[derive(
98    Copy, Clone, Serialize, Deserialize, JsonSchema, Ord, PartialOrd, Eq, PartialEq, Debug,
99)]
100pub enum DynamicFieldType {
101    #[serde(rename_all = "camelCase")]
102    DynamicField,
103    DynamicObject,
104}
105
106impl Display for DynamicFieldType {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            DynamicFieldType::DynamicField => write!(f, "DynamicField"),
110            DynamicFieldType::DynamicObject => write!(f, "DynamicObject"),
111        }
112    }
113}
114
115impl DynamicFieldInfo {
116    pub fn is_dynamic_field(tag: &StructTag) -> bool {
117        tag.address == SUI_FRAMEWORK_ADDRESS
118            && tag.module.as_ident_str() == DYNAMIC_FIELD_MODULE_NAME
119            && tag.name.as_ident_str() == DYNAMIC_FIELD_FIELD_STRUCT_NAME
120    }
121
122    pub fn is_dynamic_object_field_wrapper(tag: &StructTag) -> bool {
123        tag.address == SUI_FRAMEWORK_ADDRESS
124            && tag.module.as_ident_str() == DYNAMIC_OBJECT_FIELD_MODULE_NAME
125            && tag.name.as_ident_str() == DYNAMIC_OBJECT_FIELD_WRAPPER_STRUCT_NAME
126    }
127
128    pub fn dynamic_field_type(key: TypeTag, value: TypeTag) -> StructTag {
129        StructTag {
130            address: SUI_FRAMEWORK_ADDRESS,
131            name: DYNAMIC_FIELD_FIELD_STRUCT_NAME.to_owned(),
132            module: DYNAMIC_FIELD_MODULE_NAME.to_owned(),
133            type_params: vec![key, value],
134        }
135    }
136
137    pub fn dynamic_object_field_wrapper(key: TypeTag) -> StructTag {
138        StructTag {
139            address: SUI_FRAMEWORK_ADDRESS,
140            module: DYNAMIC_OBJECT_FIELD_MODULE_NAME.to_owned(),
141            name: DYNAMIC_OBJECT_FIELD_WRAPPER_STRUCT_NAME.to_owned(),
142            type_params: vec![key],
143        }
144    }
145
146    pub fn try_extract_field_name(tag: &StructTag, type_: &DynamicFieldType) -> SuiResult<TypeTag> {
147        match (type_, tag.type_params.first()) {
148            (DynamicFieldType::DynamicField, Some(name_type)) => Ok(name_type.clone()),
149            (DynamicFieldType::DynamicObject, Some(TypeTag::Struct(s))) => Ok(s
150                .type_params
151                .first()
152                .ok_or_else(|| SuiErrorKind::ObjectDeserializationError {
153                    error: format!("Error extracting dynamic object name from object: {tag}"),
154                })?
155                .clone()),
156            _ => Err(SuiErrorKind::ObjectDeserializationError {
157                error: format!("Error extracting dynamic object name from object: {tag}"),
158            }
159            .into()),
160        }
161    }
162
163    pub fn try_extract_field_value(tag: &StructTag) -> SuiResult<TypeTag> {
164        match tag.type_params.last() {
165            Some(value_type) => Ok(value_type.clone()),
166            None => Err(SuiErrorKind::ObjectDeserializationError {
167                error: format!("Error extracting dynamic object value from object: {tag}"),
168            }
169            .into()),
170        }
171    }
172
173    pub fn parse_move_object(
174        move_struct: &MoveStruct,
175    ) -> SuiResult<(MoveValue, DynamicFieldType, ObjectID)> {
176        let name = extract_field_from_move_struct(move_struct, "name").ok_or_else(|| {
177            SuiErrorKind::ObjectDeserializationError {
178                error: "Cannot extract [name] field from sui::dynamic_field::Field".to_string(),
179            }
180        })?;
181
182        let value = extract_field_from_move_struct(move_struct, "value").ok_or_else(|| {
183            SuiErrorKind::ObjectDeserializationError {
184                error: "Cannot extract [value] field from sui::dynamic_field::Field".to_string(),
185            }
186        })?;
187
188        Ok(if is_dynamic_object(move_struct) {
189            let name = match name {
190                MoveValue::Struct(name_struct) => {
191                    extract_field_from_move_struct(name_struct, "name")
192                }
193                _ => None,
194            }
195            .ok_or_else(|| SuiErrorKind::ObjectDeserializationError {
196                error: "Cannot extract [name] field from sui::dynamic_object_field::Wrapper."
197                    .to_string(),
198            })?;
199            let object_id = extract_id_value(value).ok_or_else(|| {
201                SuiErrorKind::ObjectDeserializationError {
202                    error: format!(
203                        "Cannot extract dynamic object's object id from \
204                        sui::dynamic_field::Field, {value:?}"
205                    ),
206                }
207            })?;
208            (name.clone(), DynamicFieldType::DynamicObject, object_id)
209        } else {
210            let object_id = extract_object_id(move_struct).ok_or_else(|| {
212                SuiErrorKind::ObjectDeserializationError {
213                    error: format!(
214                        "Cannot extract dynamic object's object id from \
215                        sui::dynamic_field::Field, {move_struct:?}",
216                    ),
217                }
218            })?;
219            (name.clone(), DynamicFieldType::DynamicField, object_id)
220        })
221    }
222}
223
224pub fn extract_field_from_move_struct<'a>(
225    move_struct: &'a MoveStruct,
226    field_name: &str,
227) -> Option<&'a MoveValue> {
228    move_struct.fields.iter().find_map(|(id, value)| {
229        if id.to_string() == field_name {
230            Some(value)
231        } else {
232            None
233        }
234    })
235}
236
237fn extract_object_id(value: &MoveStruct) -> Option<ObjectID> {
238    let uid_value = &value.fields.first()?.1;
240
241    let id_value = match uid_value {
243        MoveValue::Struct(MoveStruct { fields, .. }) => &fields.first()?.1,
244        _ => return None,
245    };
246    extract_id_value(id_value)
247}
248
249pub fn extract_id_value(id_value: &MoveValue) -> Option<ObjectID> {
250    let id_bytes_value = match id_value {
252        MoveValue::Struct(MoveStruct { fields, .. }) => &fields.first()?.1,
253        _ => return None,
254    };
255    match id_bytes_value {
257        MoveValue::Address(addr) => Some(ObjectID::from(*addr)),
258        _ => None,
259    }
260}
261
262pub fn is_dynamic_object(move_struct: &MoveStruct) -> bool {
263    matches!(
264        &move_struct.type_.type_params[0],
265        TypeTag::Struct(tag) if DynamicFieldInfo::is_dynamic_object_field_wrapper(tag)
266    )
267}
268
269pub fn derive_dynamic_field_id<T>(
270    parent: T,
271    key_type_tag: &TypeTag,
272    key_bytes: &[u8],
273) -> Result<ObjectID, bcs::Error>
274where
275    T: Into<SuiAddress>,
276{
277    let parent: SuiAddress = parent.into();
278    let k_tag_bytes = bcs::to_bytes(key_type_tag)?;
279    tracing::trace!(
280        "Deriving dynamic field ID for parent={:?}, key={:?}, key_type_tag={:?}",
281        parent,
282        key_bytes,
283        key_type_tag,
284    );
285
286    let mut hasher = DefaultHash::default();
288    hasher.update([HashingIntentScope::ChildObjectId as u8]);
289    hasher.update(parent);
290    hasher.update(key_bytes.len().to_le_bytes());
291    hasher.update(key_bytes);
292    hasher.update(k_tag_bytes);
293    let hash = hasher.finalize();
294
295    let id = ObjectID::try_from(&hash.as_ref()[0..ObjectID::LENGTH]).unwrap();
298    tracing::trace!("derive_dynamic_field_id result: {:?}", id);
299    Ok(id)
300}
301
302pub fn serialize_dynamic_field<K, V>(id: &UID, name: &K, value: V) -> Result<Vec<u8>, SuiError>
303where
304    K: Serialize + Clone,
305    V: Serialize,
306{
307    let field = Field::<K, V> {
308        id: id.clone(),
309        name: name.clone(),
310        value,
311    };
312
313    bcs::to_bytes(&field).map_err(|err| {
314        SuiErrorKind::ObjectSerializationError {
315            error: err.to_string(),
316        }
317        .into()
318    })
319}
320
321pub fn get_dynamic_field_object_from_store<K>(
326    object_store: &dyn ObjectStore,
327    parent_id: ObjectID,
328    key: &K,
329) -> Result<Object, SuiError>
330where
331    K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug + Clone,
332{
333    Ok(DynamicFieldKey(parent_id, key.clone(), K::get_type_tag())
334        .into_unbounded_id()?
335        .expect_object(key, object_store)?
336        .as_object())
337}
338
339pub fn get_dynamic_field_from_store<K, V>(
342    object_store: &dyn ObjectStore,
343    parent_id: ObjectID,
344    key: &K,
345) -> Result<V, SuiError>
346where
347    K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug + Clone,
348    V: Serialize + DeserializeOwned,
349{
350    DynamicFieldKey(parent_id, key.clone(), K::get_type_tag())
351        .into_unbounded_id()?
352        .expect_object(key, object_store)?
353        .load_value::<V>()
354}
355
356pub struct DynamicFieldKey<ParentID, K>(pub ParentID, pub K, pub TypeTag);
377
378impl<ParentID, K> DynamicFieldKey<ParentID, K>
379where
380    ParentID: Into<SuiAddress> + Into<ObjectID> + Copy,
381    K: Serialize + fmt::Debug,
382{
383    pub fn object_id(&self) -> Result<ObjectID, SuiError> {
385        derive_dynamic_field_id(self.0, &self.2, &bcs::to_bytes(&self.1).unwrap())
386            .map_err(|e| SuiErrorKind::DynamicFieldReadError(e.to_string()).into())
387    }
388
389    pub fn into_unbounded_id(self) -> Result<UnboundedDynamicFieldID<K>, SuiError> {
392        let id = self.object_id()?;
393        Ok(UnboundedDynamicFieldID::<K>::new(self.0.into(), id))
394    }
395
396    pub fn into_id_with_bound(
399        self,
400        parent_version: SequenceNumber,
401    ) -> Result<BoundedDynamicFieldID<K>, SuiError> {
402        let id = self.object_id()?;
403        Ok(BoundedDynamicFieldID::<K>::new(
404            self.0.into(),
405            id,
406            parent_version,
407        ))
408    }
409
410    pub fn into_field<V>(self, value: V) -> Result<DynamicField<K, V>, SuiError>
415    where
416        V: Serialize + DeserializeOwned + MoveTypeTagTrait,
417    {
418        let id = self.object_id()?;
419        let field = Field::<K, V> {
420            id: UID::new(id),
421            name: self.1,
422            value,
423        };
424        let type_tag = TypeTag::Struct(Box::new(DynamicFieldInfo::dynamic_field_type(
425            self.2,
426            V::get_type_tag(),
427        )));
428        Ok(DynamicField(field, type_tag))
429    }
430}
431
432pub struct DynamicField<K, V>(Field<K, V>, TypeTag);
434
435impl<K, V> DynamicField<K, V>
436where
437    K: Serialize,
438    V: Serialize,
439{
440    pub fn into_move_object_unsafe_for_testing(
447        self,
448        version: SequenceNumber,
449    ) -> Result<MoveObject, SuiError> {
450        let field = self.0;
451        let type_tag = self.1;
452        let TypeTag::Struct(struct_tag) = type_tag else {
453            unreachable!()
454        };
455        let move_object_type = MoveObjectType::from(*struct_tag);
457
458        let field_bytes =
459            bcs::to_bytes(&field).map_err(|e| SuiErrorKind::ObjectSerializationError {
460                error: e.to_string(),
461            })?;
462        Ok(unsafe {
463            MoveObject::new_from_execution_with_limit(
464                move_object_type,
465                false, version,
467                field_bytes,
468                512,
469            )
470        }?)
471    }
472
473    pub fn into_inner(self) -> Field<K, V> {
475        self.0
476    }
477}
478
479pub struct UnboundedDynamicFieldID<K: Serialize>(
485    pub ObjectID, pub ObjectID, std::marker::PhantomData<K>,
488);
489
490impl<K> UnboundedDynamicFieldID<K>
491where
492    K: Serialize + std::fmt::Debug,
493{
494    pub fn new(parent: ObjectID, id: ObjectID) -> Self {
496        Self(parent, id, std::marker::PhantomData)
497    }
498
499    pub fn load_object(self, object_store: &dyn ObjectStore) -> Option<DynamicFieldObject<K>> {
501        object_store
502            .get_object(&self.1)
503            .map(DynamicFieldObject::<K>::new)
504    }
505
506    pub fn expect_object(
509        self,
510        key: &K,
511        object_store: &dyn ObjectStore,
512    ) -> Result<DynamicFieldObject<K>, SuiError> {
513        let parent = self.0;
514        let id = self.1;
515        self.load_object(object_store).ok_or_else(|| {
516            {
517                SuiErrorKind::DynamicFieldReadError(format!(
518                    "Dynamic field with key={:?} and ID={:?} not found on parent {:?}",
519                    key, id, parent
520                ))
521            }
522            .into()
523        })
524    }
525
526    pub fn exists(self, object_store: &dyn ObjectStore) -> bool {
528        self.load_object(object_store).is_some()
529    }
530
531    pub fn with_bound(self, parent_version: SequenceNumber) -> BoundedDynamicFieldID<K> {
534        BoundedDynamicFieldID::new(self.0, self.1, parent_version)
535    }
536
537    pub fn as_object_id(self) -> ObjectID {
539        self.1
540    }
541}
542
543pub struct BoundedDynamicFieldID<K: Serialize>(
550    pub ObjectID,       pub ObjectID,       pub SequenceNumber, std::marker::PhantomData<K>,
554);
555
556impl<K> BoundedDynamicFieldID<K>
557where
558    K: Serialize,
559{
560    pub fn new(parent_id: ObjectID, child_id: ObjectID, parent_version: SequenceNumber) -> Self {
562        Self(
563            parent_id,
564            child_id,
565            parent_version,
566            std::marker::PhantomData,
567        )
568    }
569
570    pub fn load_object(
573        self,
574        child_object_resolver: &dyn ChildObjectResolver,
575    ) -> Result<Option<DynamicFieldObject<K>>, SuiError> {
576        child_object_resolver
577            .read_child_object(&self.0, &self.1, self.2)
578            .map(|r| r.map(DynamicFieldObject::<K>::new))
579    }
580
581    pub fn exists(self, child_object_resolver: &dyn ChildObjectResolver) -> Result<bool, SuiError> {
583        self.load_object(child_object_resolver).map(|r| r.is_some())
584    }
585}
586
587pub struct DynamicFieldObject<K>(pub Object, std::marker::PhantomData<K>);
589
590impl<K> DynamicFieldObject<K> {
591    pub fn new(object: Object) -> Self {
593        Self(object, std::marker::PhantomData)
594    }
595
596    pub fn as_object(self) -> Object {
598        self.0
599    }
600}
601
602impl<K> DynamicFieldObject<K>
603where
604    K: Serialize + DeserializeOwned,
605{
606    pub fn load_value<V>(self) -> Result<V, SuiError>
608    where
609        V: Serialize + DeserializeOwned,
610    {
611        self.load_field::<V>().map(|f| f.value)
612    }
613
614    pub fn load_field<V>(self) -> Result<Field<K, V>, SuiError>
616    where
617        V: Serialize + DeserializeOwned,
618    {
619        let object = self.0;
620        let move_object = object.data.try_as_move().ok_or_else(|| {
621            SuiErrorKind::DynamicFieldReadError(format!(
622                "Dynamic field {:?} is not a Move object",
623                object.id()
624            ))
625        })?;
626        bcs::from_bytes::<Field<K, V>>(move_object.contents())
627            .map_err(|err| SuiErrorKind::DynamicFieldReadError(err.to_string()).into())
628    }
629}