sui_types/
derived_object.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    SUI_FRAMEWORK_ADDRESS,
6    base_types::{ObjectID, SuiAddress},
7    dynamic_field,
8};
9use move_core_types::{
10    identifier::Identifier,
11    language_storage::{StructTag, TypeTag},
12};
13
14/// Using a parent object, a type tag and the bcs bytes of the key,
15/// compute the derived object address.
16///
17/// Note: Namespace is hidden so struct `T` should be passed in as is.
18pub fn derive_object_id<T>(
19    parent: T,
20    key_type_tag: &TypeTag,
21    key_bytes: &[u8],
22) -> Result<ObjectID, bcs::Error>
23where
24    T: Into<SuiAddress>,
25{
26    let parent_address = parent.into();
27
28    // Wrap `T` into `DerivedObjectKey<T>` type (to preserve on-chain namespacing)
29    let wrapper_type_tag = TypeTag::Struct(Box::new(StructTag {
30        address: SUI_FRAMEWORK_ADDRESS,
31        module: Identifier::new("derived_object").unwrap(),
32        name: Identifier::new("DerivedObjectKey").unwrap(),
33        type_params: vec![key_type_tag.clone()],
34    }));
35
36    dynamic_field::derive_dynamic_field_id(parent_address, &wrapper_type_tag, key_bytes)
37}
38
39#[cfg(test)]
40mod tests {
41    use std::str::FromStr;
42
43    use serde::Serialize;
44
45    use super::*;
46
47    #[derive(Serialize)]
48    struct DemoStruct {
49        value: u64,
50    }
51
52    #[derive(Serialize)]
53    struct GenericStruct<T> {
54        value: T,
55    }
56
57    // Snapshot tests that match the on-chain `derive_address` logic.
58    // These snapshots can also be found in `derived_object_tests.move` unit tests.
59    #[test]
60    fn test_derive_object_snapshot() {
61        // Our key is `UID, Vec<u8>, b"foo"`
62        let key_bytes = bcs::to_bytes("foo".as_bytes()).unwrap();
63        let key_type_tag = TypeTag::Vector(Box::new(TypeTag::U8));
64
65        let id = derive_object_id(
66            ObjectID::from_str("0x2").unwrap(),
67            &key_type_tag,
68            &key_bytes,
69        )
70        .unwrap();
71
72        assert_eq!(
73            id,
74            ObjectID::from_str(
75                "0xa2b411aa9588c398d8e3bc97dddbdd430b5ded7f81545d05e33916c3ca0f30c3"
76            )
77            .unwrap()
78        );
79    }
80
81    #[test]
82    fn test_derive_object_with_struct_key_snapshot() {
83        let key = DemoStruct { value: 1 };
84        let key_value = bcs::to_bytes(&key).unwrap();
85
86        let id = derive_object_id(
87            ObjectID::from_str("0x2").unwrap(),
88            &TypeTag::Struct(Box::new(StructTag {
89                address: SUI_FRAMEWORK_ADDRESS,
90                module: Identifier::new("derived_object_tests").unwrap(),
91                name: Identifier::new("DemoStruct").unwrap(),
92                type_params: vec![],
93            })),
94            &key_value,
95        )
96        .unwrap();
97
98        assert_eq!(
99            id,
100            ObjectID::from_str(
101                "0x20c58d8790a5d2214c159c23f18a5fdc347211e511186353e785ad543abcea6b"
102            )
103            .unwrap()
104        );
105    }
106
107    #[test]
108    fn test_derive_object_with_generic_struct_key_snapshot() {
109        let key = GenericStruct::<u64> { value: 1 };
110        let key_value = bcs::to_bytes(&key).unwrap();
111
112        let id = derive_object_id(
113            ObjectID::from_str("0x2").unwrap(),
114            &TypeTag::Struct(Box::new(StructTag {
115                address: SUI_FRAMEWORK_ADDRESS,
116                module: Identifier::new("derived_object_tests").unwrap(),
117                name: Identifier::new("GenericStruct").unwrap(),
118                type_params: vec![TypeTag::U64],
119            })),
120            &key_value,
121        )
122        .unwrap();
123
124        assert_eq!(
125            id,
126            ObjectID::from_str(
127                "0xb497b8dcf1e297ae5fa69c040e4a08ef8240d5373bbc9d6b686ffbd7dfe04cbe"
128            )
129            .unwrap()
130        );
131    }
132}