sui_indexer/models/
events.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::str::FromStr;
5use std::sync::Arc;
6
7use diesel::prelude::*;
8use move_core_types::identifier::Identifier;
9
10use sui_json_rpc_types::{BcsEvent, SuiEvent, type_and_fields_from_move_event_data};
11use sui_package_resolver::{PackageStore, Resolver};
12use sui_types::base_types::{ObjectID, SuiAddress};
13use sui_types::digests::TransactionDigest;
14use sui_types::event::EventID;
15use sui_types::object::bounded_visitor::BoundedVisitor;
16use sui_types::parse_sui_struct_tag;
17
18use crate::errors::IndexerError;
19use crate::schema::events;
20use crate::types::IndexedEvent;
21
22#[derive(Queryable, QueryableByName, Selectable, Insertable, Debug, Clone)]
23#[diesel(table_name = events)]
24pub struct StoredEvent {
25    pub tx_sequence_number: i64,
26    pub event_sequence_number: i64,
27    pub transaction_digest: Vec<u8>,
28    pub senders: Vec<Option<Vec<u8>>>,
29    pub package: Vec<u8>,
30    pub module: String,
31    pub event_type: String,
32    pub timestamp_ms: i64,
33    pub bcs: Vec<u8>,
34    pub sender: Option<Vec<u8>>,
35}
36
37pub type SendersType = Vec<Option<Vec<u8>>>;
38
39impl From<IndexedEvent> for StoredEvent {
40    fn from(event: IndexedEvent) -> Self {
41        Self {
42            tx_sequence_number: event.tx_sequence_number as i64,
43            event_sequence_number: event.event_sequence_number as i64,
44            transaction_digest: event.transaction_digest.into_inner().to_vec(),
45            senders: vec![Some(event.sender.to_vec())],
46            package: event.package.to_vec(),
47            module: event.module.clone(),
48            event_type: event.event_type.clone(),
49            bcs: event.bcs.clone(),
50            timestamp_ms: event.timestamp_ms as i64,
51            sender: Some(event.sender.to_vec()),
52        }
53    }
54}
55
56impl StoredEvent {
57    pub async fn try_into_sui_event(
58        self,
59        package_resolver: Arc<Resolver<impl PackageStore>>,
60    ) -> Result<SuiEvent, IndexerError> {
61        let package_id = ObjectID::from_bytes(self.package.clone()).map_err(|_e| {
62            IndexerError::PersistentStorageDataCorruptionError(format!(
63                "Failed to parse event package ID: {:?}",
64                self.package
65            ))
66        })?;
67        // Note: SuiEvent only has one sender today, so we always use the first one.
68        let sender = {
69            self.senders.first().ok_or_else(|| {
70                IndexerError::PersistentStorageDataCorruptionError(
71                    "Event senders should contain at least one address".to_string(),
72                )
73            })?
74        };
75        let sender = match sender {
76            Some(s) => SuiAddress::from_bytes(s).map_err(|_e| {
77                IndexerError::PersistentStorageDataCorruptionError(format!(
78                    "Failed to parse event sender address: {:?}",
79                    sender
80                ))
81            })?,
82            None => {
83                return Err(IndexerError::PersistentStorageDataCorruptionError(
84                    "Event senders element should not be null".to_string(),
85                ));
86            }
87        };
88
89        let type_ = parse_sui_struct_tag(&self.event_type)?;
90        let move_type_layout = package_resolver
91            .type_layout(type_.clone().into())
92            .await
93            .map_err(|e| {
94                IndexerError::ResolveMoveStructError(format!(
95                    "Failed to convert to sui event with Error: {e}",
96                ))
97            })?;
98        let move_object = BoundedVisitor::deserialize_value(&self.bcs, &move_type_layout)
99            .map_err(|e| IndexerError::SerdeError(e.to_string()))?;
100        let (_, parsed_json) = type_and_fields_from_move_event_data(move_object)
101            .map_err(|e| IndexerError::SerdeError(e.to_string()))?;
102        let tx_digest =
103            TransactionDigest::try_from(self.transaction_digest.as_slice()).map_err(|e| {
104                IndexerError::SerdeError(format!(
105                    "Failed to parse transaction digest: {:?}, error: {}",
106                    self.transaction_digest, e
107                ))
108            })?;
109        Ok(SuiEvent {
110            id: EventID {
111                tx_digest,
112                event_seq: self.event_sequence_number as u64,
113            },
114            package_id,
115            transaction_module: Identifier::from_str(&self.module)?,
116            sender,
117            type_,
118            bcs: BcsEvent::new(self.bcs),
119            parsed_json,
120            timestamp_ms: Some(self.timestamp_ms as u64),
121        })
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use move_core_types::{account_address::AccountAddress, language_storage::StructTag};
129    use sui_types::event::Event;
130
131    #[test]
132    fn test_canonical_string_of_event_type() {
133        let tx_digest = TransactionDigest::default();
134        let event = Event {
135            package_id: ObjectID::random(),
136            transaction_module: Identifier::new("test").unwrap(),
137            sender: AccountAddress::random().into(),
138            type_: StructTag {
139                address: AccountAddress::TWO,
140                module: Identifier::new("test").unwrap(),
141                name: Identifier::new("test").unwrap(),
142                type_params: vec![],
143            },
144            contents: vec![],
145        };
146
147        let indexed_event = IndexedEvent::from_event(1, 1, 1, tx_digest, &event, 100);
148
149        let stored_event = StoredEvent::from(indexed_event);
150
151        assert_eq!(
152            stored_event.event_type,
153            "0x0000000000000000000000000000000000000000000000000000000000000002::test::test"
154        );
155    }
156}