data_anchor_api/
indexing.rs1use anchor_lang::{AnchorDeserialize, Discriminator};
2use data_anchor_blober::{BLOB_ACCOUNT_INSTRUCTION_IDX, BLOB_BLOBER_INSTRUCTION_IDX};
3use serde::{Deserialize, Serialize};
4use solana_sdk::{
5 instruction::CompiledInstruction, pubkey::Pubkey, transaction::VersionedTransaction,
6};
7use solana_transaction_status::InnerInstructions;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct VersionedTransactionWithInnerInstructions {
11 pub transaction: VersionedTransaction,
12 pub inner_instructions: Vec<InnerInstructions>,
13}
14
15impl From<VersionedTransaction> for VersionedTransactionWithInnerInstructions {
16 fn from(transaction: VersionedTransaction) -> Self {
17 Self {
18 transaction,
19 inner_instructions: Vec::new(),
20 }
21 }
22}
23
24impl From<&VersionedTransaction> for VersionedTransactionWithInnerInstructions {
25 fn from(transaction: &VersionedTransaction) -> Self {
26 Self {
27 transaction: transaction.clone(),
28 inner_instructions: Vec::new(),
29 }
30 }
31}
32
33impl VersionedTransactionWithInnerInstructions {
34 pub fn iter_instructions(&self) -> impl Iterator<Item = &CompiledInstruction> {
37 self.transaction.message.instructions().iter().chain(
38 self.inner_instructions
39 .iter()
40 .flat_map(|inner| inner.instructions.iter().map(|inner| &inner.instruction)),
41 )
42 }
43}
44
45pub enum RelevantInstruction {
47 DeclareBlob(data_anchor_blober::instruction::DeclareBlob),
48 InsertChunk(data_anchor_blober::instruction::InsertChunk),
49 FinalizeBlob(data_anchor_blober::instruction::FinalizeBlob),
50}
51
52impl std::fmt::Debug for RelevantInstruction {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 RelevantInstruction::DeclareBlob(instruction) => f
56 .debug_struct("DeclareBlob")
57 .field("size", &instruction.blob_size)
58 .field("timestamp", &instruction.timestamp)
59 .finish(),
60 RelevantInstruction::InsertChunk(instruction) => f
61 .debug_struct("InsertChunk")
62 .field("idx", &instruction.idx)
63 .finish(),
64 RelevantInstruction::FinalizeBlob(_) => f.debug_struct("FinalizeBlob").finish(),
65 }
66 }
67}
68
69impl RelevantInstruction {
70 pub fn try_from_slice(compiled_instruction: &CompiledInstruction) -> Option<Self> {
71 use data_anchor_blober::instruction::*;
72 let discriminator = compiled_instruction.data.get(..8)?;
73
74 match discriminator {
75 DeclareBlob::DISCRIMINATOR => {
76 let data = compiled_instruction.data.get(8..).unwrap_or_default();
77 DeclareBlob::try_from_slice(data)
78 .map(RelevantInstruction::DeclareBlob)
79 .ok()
80 }
81 InsertChunk::DISCRIMINATOR => {
82 let data = compiled_instruction.data.get(8..).unwrap_or_default();
83 InsertChunk::try_from_slice(data)
84 .map(RelevantInstruction::InsertChunk)
85 .ok()
86 }
87 FinalizeBlob::DISCRIMINATOR => {
88 let data = compiled_instruction.data.get(8..).unwrap_or_default();
89 FinalizeBlob::try_from_slice(data)
90 .map(RelevantInstruction::FinalizeBlob)
91 .ok()
92 }
93 _ => None,
97 }
98 }
99}
100
101#[derive(Debug)]
103pub struct RelevantInstructionWithAccounts {
104 pub blob: Pubkey,
105 pub blober: Pubkey,
106 pub instruction: RelevantInstruction,
107}
108
109pub fn deserialize_relevant_instructions(
112 program_id: &Pubkey,
113 tx: &VersionedTransactionWithInnerInstructions,
114 blob_pubkey_index: usize,
115 blober_pubkey_index: usize,
116) -> Vec<RelevantInstructionWithAccounts> {
117 tx.iter_instructions()
118 .filter_map(|compiled_instruction| {
119 let program_id_idx: usize = compiled_instruction.program_id_index.into();
120 let relevant_program_id = tx
121 .transaction
122 .message
123 .static_account_keys()
124 .get(program_id_idx)?;
125
126 if program_id != relevant_program_id {
127 return None; }
129
130 let blob =
131 get_account_at_index(&tx.transaction, compiled_instruction, blob_pubkey_index)?;
132 let blober =
133 get_account_at_index(&tx.transaction, compiled_instruction, blober_pubkey_index)?;
134 let instruction = RelevantInstruction::try_from_slice(compiled_instruction)?;
135 let relevant_instruction = RelevantInstructionWithAccounts {
136 blob,
137 blober,
138 instruction,
139 };
140
141 Some(relevant_instruction)
142 })
143 .collect()
144}
145
146pub enum RelevantBloberInstruction {
148 Initialize(data_anchor_blober::instruction::Initialize),
149 Close(data_anchor_blober::instruction::Close),
150}
151
152impl std::fmt::Debug for RelevantBloberInstruction {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match self {
155 RelevantBloberInstruction::Initialize(instruction) => f
156 .debug_struct("Initialize")
157 .field("trusted", &instruction.trusted)
158 .finish(),
159 RelevantBloberInstruction::Close(_) => f.debug_struct("Close").finish(),
160 }
161 }
162}
163
164impl RelevantBloberInstruction {
165 pub fn try_from_slice(compiled_instruction: &CompiledInstruction) -> Option<Self> {
166 use data_anchor_blober::instruction::*;
167 let discriminator = compiled_instruction.data.get(..8)?;
168
169 match discriminator {
170 Initialize::DISCRIMINATOR => {
171 let data = compiled_instruction.data.get(8..).unwrap_or_default();
172 Initialize::try_from_slice(data)
173 .map(RelevantBloberInstruction::Initialize)
174 .ok()
175 }
176 Close::DISCRIMINATOR => {
177 let data = compiled_instruction.data.get(8..).unwrap_or_default();
178 Close::try_from_slice(data)
179 .map(RelevantBloberInstruction::Close)
180 .ok()
181 }
182 _ => None,
186 }
187 }
188}
189
190#[derive(Debug)]
192pub struct RelevantBloberInstructionWithPubkey {
193 pub blober: Pubkey,
194 pub instruction: RelevantBloberInstruction,
195}
196
197pub fn deserialize_blober_instructions(
199 program_id: &Pubkey,
200 tx: &VersionedTransactionWithInnerInstructions,
201) -> Vec<RelevantBloberInstructionWithPubkey> {
202 tx.iter_instructions()
203 .filter_map(|compiled_instruction| {
204 let program_id_idx: usize = compiled_instruction.program_id_index.into();
205
206 let relevant_program_id = tx
207 .transaction
208 .message
209 .static_account_keys()
210 .get(program_id_idx)?;
211
212 if program_id != relevant_program_id {
213 return None; }
215
216 let blober = get_account_at_index(&tx.transaction, compiled_instruction, 0)?;
217
218 let instruction = RelevantBloberInstruction::try_from_slice(compiled_instruction)?;
219
220 Some(RelevantBloberInstructionWithPubkey {
221 blober,
222 instruction,
223 })
224 })
225 .collect()
226}
227
228pub fn extract_relevant_instructions(
230 program_id: &Pubkey,
231 transactions: &[VersionedTransaction],
232) -> Vec<RelevantInstructionWithAccounts> {
233 transactions
234 .iter()
235 .flat_map(|tx| {
236 deserialize_relevant_instructions(
237 program_id,
238 &tx.into(),
239 BLOB_ACCOUNT_INSTRUCTION_IDX,
240 BLOB_BLOBER_INSTRUCTION_IDX,
241 )
242 })
243 .collect()
244}
245
246pub fn get_account_at_index(
250 tx: &VersionedTransaction,
251 instruction: &CompiledInstruction,
252 index: usize,
253) -> Option<Pubkey> {
254 let actual_index = *instruction.accounts.get(index)? as usize;
255 tx.message.static_account_keys().get(actual_index).copied()
256}