Skip to main content

iota_types/
derived_object.rs

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