iota_indexer/models/
display.rs1use diesel::prelude::*;
6use iota_sdk_move_types::iota_framework::vec_map::VecMap;
7use iota_sdk_types::{Address, Event, Identifier, ObjectId, StructTag, TypeTag};
8use iota_types::{
9 display::{DisplayObject, DisplayVersionUpdatedEvent},
10 id::ID,
11 object::Object,
12};
13
14use crate::{errors::IndexerError, schema::display};
15
16const DISPLAY_STRUCT_NAME: Identifier = Identifier::from_static("Display");
17
18#[derive(Debug, Copy, Clone)]
20pub enum DisplayBcsKind {
21 Event = 0,
23 Fields = 1,
25}
26
27impl TryFrom<i16> for DisplayBcsKind {
28 type Error = IndexerError;
29
30 fn try_from(value: i16) -> Result<Self, Self::Error> {
31 Ok(match value {
32 0 => Self::Event,
33 1 => Self::Fields,
34 value => {
35 return Err(IndexerError::PersistentStorageDataCorruption(format!(
36 "{value} as DisplayBcsKind"
37 )));
38 }
39 })
40 }
41}
42
43#[derive(Queryable, Insertable, Selectable, Debug, Clone)]
44#[diesel(table_name = display)]
45pub struct StoredDisplay {
46 pub object_type: String,
47 pub id: Vec<u8>,
48 pub version: i16,
49 pub bcs: Vec<u8>,
50 pub bcs_kind: i16,
51}
52
53impl StoredDisplay {
54 pub fn try_from_event(event: &Event) -> Option<Self> {
55 if !event.struct_tag.is_display_version_updated() {
56 return None;
57 }
58 let ty = match event.struct_tag.type_params() {
59 [TypeTag::Struct(struct_type)] => struct_type,
60 _ => return None,
61 };
62 let display_event: DisplayVersionUpdatedEvent = bcs::from_bytes(&event.contents).ok()?;
63
64 Some(Self {
65 object_type: ty.to_canonical_string(true),
66 id: display_event.id.bytes.as_bytes().to_vec(),
67 version: display_event.version as i16,
68 bcs: bcs::to_bytes(&display_event.fields)
69 .expect("serializing a deserialized value should succeed"),
70 bcs_kind: DisplayBcsKind::Fields as i16,
71 })
72 }
73
74 pub fn try_from_object(object: &Object) -> Option<Self> {
78 let move_object = object.data.as_opt_struct()?;
79 let struct_tag = move_object.struct_tag();
80 if !is_display(struct_tag) {
81 return None;
82 }
83 let [TypeTag::Struct(object_type)] = struct_tag.type_params() else {
84 return None;
85 };
86 let display: DisplayObject = bcs::from_bytes(move_object.contents()).ok()?;
87
88 Some(Self {
89 object_type: object_type.to_canonical_string(true),
90 id: display.id.object_id().as_bytes().to_vec(),
91 version: display.version as i16,
92 bcs: bcs::to_bytes(&display.fields)
93 .expect("serializing a deserialized value should succeed"),
94 bcs_kind: DisplayBcsKind::Fields as i16,
95 })
96 }
97
98 pub fn to_display_fields(&self) -> Result<VecMap<String, String>, IndexerError> {
99 match DisplayBcsKind::try_from(self.bcs_kind)? {
100 DisplayBcsKind::Event => {
101 bcs::from_bytes::<DisplayVersionUpdatedEvent>(&self.bcs).map(|event| event.fields)
102 }
103 DisplayBcsKind::Fields => bcs::from_bytes(&self.bcs),
104 }
105 .map_err(|e| {
106 IndexerError::PersistentStorageDataCorruption(format!(
107 "failed to decode stored display: {e}"
108 ))
109 })
110 }
111}
112
113pub fn displayed_type_from_created_event(event: &Event) -> Option<String> {
119 if !event.struct_tag.is_display_created() {
120 return None;
121 }
122 let [TypeTag::Struct(displayed_type)] = event.struct_tag.type_params() else {
123 return None;
124 };
125 Some(displayed_type.to_canonical_string(true))
126}
127
128pub fn display_id_from_created_event(event: &Event) -> Option<ObjectId> {
133 if !event.struct_tag.is_display_created() {
134 return None;
135 }
136 let created_event: ID = bcs::from_bytes(&event.contents).ok()?;
137 Some(created_event.bytes)
138}
139
140fn is_display(struct_tag: &StructTag) -> bool {
142 struct_tag.address() == Address::FRAMEWORK
143 && struct_tag.module() == &Identifier::DISPLAY_MODULE
144 && struct_tag.name() == &DISPLAY_STRUCT_NAME
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 fn display_event(struct_tag: StructTag, contents: Vec<u8>) -> Event {
152 Event {
153 package_id: ObjectId::random(),
154 module: Identifier::DISPLAY_MODULE,
155 sender: Address::random(),
156 struct_tag,
157 contents,
158 }
159 }
160
161 fn displayed_type() -> StructTag {
163 StructTag::new(
164 Address::random(),
165 Identifier::from_static("test"),
166 Identifier::from_static("Test"),
167 vec![],
168 )
169 }
170
171 #[test]
172 fn display_created_event_helpers_extract_type_and_id() {
173 let displayed_type = displayed_type();
174 let display_id = ObjectId::random();
175 let event = display_event(
176 StructTag::new_display_created(displayed_type.clone()),
177 bcs::to_bytes(&ID::new(display_id)).unwrap(),
178 );
179
180 assert_eq!(
181 displayed_type_from_created_event(&event),
182 Some(displayed_type.to_canonical_string(true))
183 );
184 assert_eq!(display_id_from_created_event(&event), Some(display_id));
185 }
186
187 #[test]
188 fn display_created_event_helpers_ignore_other_events() {
189 let event = display_event(
190 StructTag::new_display_version_updated(displayed_type()),
191 vec![],
192 );
193 assert_eq!(displayed_type_from_created_event(&event), None);
194 assert_eq!(display_id_from_created_event(&event), None);
195 }
196}