sui_analytics_indexer/handlers/tables/
df.rs1use std::collections::HashMap;
5use std::sync::Arc;
6
7use anyhow::Context as _;
8use anyhow::Result;
9use async_trait::async_trait;
10use fastcrypto::encoding::Base64;
11use fastcrypto::encoding::Encoding;
12use sui_indexer_alt_framework::pipeline::Processor;
13use sui_json_rpc_types::SuiMoveValue;
14use sui_types::TypeTag;
15use sui_types::base_types::EpochId;
16use sui_types::base_types::ObjectID;
17use sui_types::dynamic_field::DynamicFieldName;
18use sui_types::dynamic_field::DynamicFieldType;
19use sui_types::dynamic_field::visitor as DFV;
20use sui_types::full_checkpoint_content::Checkpoint;
21use sui_types::object::Object;
22use sui_types::object::Owner;
23use sui_types::object::bounded_visitor::BoundedVisitor;
24use tap::tap::TapFallible;
25use tracing::warn;
26
27use crate::Row;
28use crate::package_store::PackageCache;
29use crate::pipeline::Pipeline;
30use crate::tables::DynamicFieldRow;
31
32pub struct DynamicFieldProcessor {
33 package_cache: Arc<PackageCache>,
34}
35
36impl DynamicFieldProcessor {
37 pub fn new(package_cache: Arc<PackageCache>) -> Self {
38 Self { package_cache }
39 }
40}
41
42impl DynamicFieldProcessor {
43 async fn process_dynamic_field(
44 &self,
45 epoch: u64,
46 checkpoint: u64,
47 timestamp_ms: u64,
48 object: &Object,
49 all_written_objects: &HashMap<ObjectID, Object>,
50 ) -> Result<Option<DynamicFieldRow>> {
51 let move_obj_opt = object.data.try_as_move();
52 let Some(move_object) = move_obj_opt else {
53 return Ok(None);
54 };
55 if !move_object.type_().is_dynamic_field() {
56 return Ok(None);
57 }
58
59 let layout = self
60 .package_cache
61 .resolver_for_epoch(epoch)
62 .type_layout(move_object.type_().clone().into())
63 .await?;
64 let object_id = object.id();
65
66 let field = DFV::FieldVisitor::deserialize(move_object.contents(), &layout)?;
67
68 let type_ = field.kind;
69 let name_type: TypeTag = field.name_layout.into();
70 let bcs_name = field.name_bytes.to_owned();
71
72 let name_value = BoundedVisitor::deserialize_value(field.name_bytes, field.name_layout)
73 .tap_err(|e| {
74 warn!("{e}");
75 })?;
76 let name = DynamicFieldName {
77 type_: name_type,
78 value: SuiMoveValue::from(name_value).to_json_value(),
79 };
80 let name_json = serde_json::to_string(&name)?;
81 let owner_id = match &object.owner {
82 Owner::AddressOwner(address) => Some(*address),
83 Owner::ObjectOwner(address) => Some(*address),
84 Owner::Shared { .. } => None,
85 Owner::Immutable => None,
86 Owner::ConsensusAddressOwner { owner, .. } => Some(*owner),
87 };
88 let Some(parent_id) = owner_id else {
89 return Ok(None);
90 };
91 let row = match type_ {
92 DynamicFieldType::DynamicField => DynamicFieldRow {
93 parent_object_id: parent_id.to_string(),
94 transaction_digest: object.previous_transaction.base58_encode(),
95 checkpoint,
96 epoch,
97 timestamp_ms,
98 name: name_json,
99 bcs_name: Base64::encode(bcs_name),
100 type_,
101 object_id: object.id().to_string(),
102 version: object.version().value(),
103 digest: object.digest().to_string(),
104 object_type: move_object.clone().into_type().into_type_params()[1]
105 .to_canonical_string(true),
106 },
107 DynamicFieldType::DynamicObject => {
108 let object = all_written_objects.get(&object_id)
109 .with_context(|| format!("Failed to find object_id {object_id:?} when trying to create dynamic field info"))?;
110 let version = object.version().value();
111 let digest = object.digest().to_string();
112 let object_type = object.data.type_().unwrap().clone();
113 DynamicFieldRow {
114 parent_object_id: parent_id.to_string(),
115 transaction_digest: object.previous_transaction.base58_encode(),
116 checkpoint,
117 epoch,
118 timestamp_ms,
119 name: name_json,
120 bcs_name: Base64::encode(bcs_name),
121 type_,
122 object_id: object.id().to_string(),
123 digest,
124 version,
125 object_type: object_type.to_canonical_string(true),
126 }
127 }
128 };
129 Ok(Some(row))
130 }
131}
132
133#[async_trait]
134impl Processor for DynamicFieldProcessor {
135 const NAME: &'static str = Pipeline::DynamicField.name();
136 type Value = DynamicFieldRow;
137
138 async fn process(&self, checkpoint: &Arc<Checkpoint>) -> Result<Vec<Self::Value>> {
139 let epoch = checkpoint.summary.data().epoch;
140 let checkpoint_seq = checkpoint.summary.data().sequence_number;
141 let timestamp_ms = checkpoint.summary.data().timestamp_ms;
142
143 let mut entries = Vec::new();
144
145 for checkpoint_transaction in &checkpoint.transactions {
146 let output_objects: Vec<&Object> = checkpoint_transaction
147 .output_objects(&checkpoint.object_set)
148 .collect();
149
150 let all_objects: HashMap<_, _> = output_objects
151 .iter()
152 .map(|x| (x.id(), (*x).clone()))
153 .collect();
154
155 for object in &output_objects {
156 if let Some(row) = self
157 .process_dynamic_field(
158 epoch,
159 checkpoint_seq,
160 timestamp_ms,
161 object,
162 &all_objects,
163 )
164 .await?
165 {
166 entries.push(row);
167 }
168 }
169 }
170
171 Ok(entries)
172 }
173}
174
175impl Row for DynamicFieldRow {
176 fn get_epoch(&self) -> EpochId {
177 self.epoch
178 }
179
180 fn get_checkpoint(&self) -> u64 {
181 self.checkpoint
182 }
183}